本文转载自:http://www.hovercool.com/en/%E6%B7%BB%E5%8A%A0%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9D%97#a_.E5.9B.9B.E3.80.81.E9.85.8D.E7.BD.AE.E7.B3.BB.E7.BB.9F.E7.9A.84autoconfig

一、编写驱动核心程序

这里说的驱动核心程序是指运行在内核空间的,完全按linux驱动格式编写的,基本上与android没什么关系,一般包括xxx.h和xxx.c文件。

进入到kernel/drivers目录,新建snsled目录,然后建立对应的snsled.h和snsled.c文件:

//snsled.h

#ifndef _SNSLED_H_
#define _SNSLED_H_ #define SNSLED_NUM (1) #define SNSLED_CLASS_NAME "snsled"
#define SNSLED_DEVICE_NAME "snsled"
#define SNSLED_NODE_NAME "snsled"
#define SNSLED_PROC_NAME "snsled" #define SNSLED_IOC_MAGIC 'k' #define SNSLED_IO_ON 2323 //_IO(SNSLED_IOC_MAGIC, 0)
#define SNSLED_IO_OFF 2324 //_IO(SNSLED_IOC_MAGIC, 1)
#define SNSLED_IOW_PWM 2325 //_IOW(SNSLED_IOC_MAGIC, 2, int)
#define SNSLED_IOR_PWM 2326 //_IOR(SNSLED_IOC_MAGIC, 3, int) struct snsled_cntx {
int r1;
struct semaphore sem;
struct cdev cdev;
}; #endif

//snsled.c

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h> #include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */ //#include <asm/semaphore.h> /* semaphore */
#include <linux/semaphore.h>
#include <linux/device.h> /*class_create*/ #include "snsled.h" /* local definitions */ /*
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/leds-mt65xx.h>
#include <linux/workqueue.h>
#include <linux/wakelock.h>
#include <linux/slab.h> #include <cust_leds.h>*/ #if defined (CONFIG_ARCH_MT6573)
#include <mach/mt6573_pwm.h>
#include <mach/mt6573_gpio.h>
#include <mach/pmu6573_sw.h> #elif defined (CONFIG_ARCH_MT6516)
#include <mach/mt6516_pwm.h>
#include <mach/mt6516_gpio.h> #endif /*====macros====*/
#define BUF_SIZE (64) #define SNS_LED_CONTROL_LINE GPIO99 //GPIO39
#define SNS_LED_CONTROL_LINE_GPIO_MODE GPIO_MODE_00
#define SNS_LED_CONTROL_LINE_PWM_MODE GPIO_MODE_01 /*====declares====*/
ssize_t snsled_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t snsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
long snsled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
int snsled_open(struct inode *inode, struct file *filp);
int snsled_release(struct inode *inode, struct file *filp); int snsled_turn_on(void);
int snsled_turn_off(void);
int snsled_set_pwm(int arg); /*====global====*/
static int g_snsled_major = 0;
static int g_snsled_minor = 0; struct snsled_cntx *g_snsled_ptr = NULL; struct class *g_snsled_class = 0; static struct file_operations g_snsled_fops = {
.owner = THIS_MODULE,
.read = snsled_read,
.write = snsled_write,
.unlocked_ioctl = snsled_unlocked_ioctl,
.open = snsled_open,
.release = snsled_release,
}; /*====implements====*/
ssize_t snsled_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
#if 1
printk(KERN_ALERT "Snsled read.\n");
#else
char tmp_buf[512] = {0};
int len =sprintf(tmp_buf, "snsled read.\n");
if (copy_to_user(buf, tmp_buf, count)) {
//do nothing
}
#endif
return 0;
} ssize_t snsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
printk(KERN_ALERT "Snsled write.\n");
return count;
} long snsled_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
int err = 0;
long retval =0; printk(KERN_ALERT "Snsled ioctl:cmd=%d, arg=%d.\n", cmd, arg);/*
//extract the type and number bitfields, and don't decode
//wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
if (_IOC_TYPE(cmd) != SNSLED_IOC_MAGIC) return -ENOTTY;
//if (_IOC_NR(cmd) > SNSLED_IOC_MAXNR) return -ENOTTY; //to verify *arg is in user space
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
*/switch(cmd){case SNSLED_IO_ON:
printk(KERN_ALERT "Snsled ioctl:on.\n");//if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ONE)){printk("Snsled set gpio failed!! \n");}
snsled_turn_on();break;case SNSLED_IO_OFF:
printk(KERN_ALERT "Snsled ioctl:off.\n");//if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}
snsled_turn_off();break;case SNSLED_IOW_PWM:
printk(KERN_ALERT "Snsled ioctl:set pwm, arg=%d.\n", arg);//retval = __get_user(g_snsled_ptr->r1, (int __user *)arg);
snsled_set_pwm((int __user *)arg);break;case SNSLED_IOR_PWM:#if 0
retval = __put_user(g_snsled_ptr->r1,(int __user *)arg);
printk(KERN_ALERT "Snsled ioctl:read r1:%i.\n", g_snsled_ptr->r1);#endif
printk(KERN_ALERT "Snsled ioctl:read pwm -- not configured yet.\n");break;default:
printk(KERN_ALERT "Snsled ioctl:you got the wrong command.\n");break;}return retval;}int snsled_open(struct inode *inode,struct file *filp){
printk(KERN_ALERT "Snsled: snsled_open.\n");#if 0 if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ONE)){printk("Snsled set gpio failed!! \n");}#endifreturn0;}int snsled_release(struct inode *inode,struct file *filp){
printk(KERN_ALERT "Snsled: snsled_release.\n");#if 0 if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}#endifreturn0;}int snsled_turn_on(void){
printk(KERN_ALERT "Snsled: snsled_turn_on.\n");if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_PWM_MODE)){printk("Snsled set gpio mode failed!! \n");}if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}return0;}int snsled_turn_off(void){
printk(KERN_ALERT "Snsled: snsled_turn_off.\n");if(mt_set_gpio_mode(SNS_LED_CONTROL_LINE,SNS_LED_CONTROL_LINE_GPIO_MODE)){printk("Snsled set gpio mode failed!! \n");}if(mt_set_gpio_dir(SNS_LED_CONTROL_LINE,GPIO_DIR_OUT)){printk("Snsled set gpio dir failed!! \n");}if(mt_set_gpio_out(SNS_LED_CONTROL_LINE,GPIO_OUT_ZERO)){printk("Snsled set gpio failed!! \n");}return0;}//for old mode/**
struct _PWM_OLDMODE_REGS {
U16 IDLE_VALUE; //0
U16 GUARD_VALUE; //0
U16 GDURATION; //~
U16 WAVE_NUM; //0
U16 DATA_WIDTH; //high level, 13bits, 0~8191
U16 THRESH; //t
}PWM_MODE_OLD_REGS;
**/int snsled_set_pwm(int arg){struct pwm_spec_config pwm_setting;
pwm_setting.pwm_no = PWM1;
printk(KERN_ALERT "Snsled: snsled_open begin.\n"); pwm_setting.mode = PWM_MODE_OLD;
pwm_setting.clk_div = CLK_DIV16;//CLK_DIV128;
pwm_setting.clk_src = PWM_CLK_OLD_MODE_32K;
pwm_setting.PWM_MODE_OLD_REGS.IDLE_VALUE =0;
pwm_setting.PWM_MODE_OLD_REGS.GUARD_VALUE =0;
pwm_setting.PWM_MODE_OLD_REGS.GDURATION =8100;
pwm_setting.PWM_MODE_OLD_REGS.WAVE_NUM =0;
pwm_setting.PWM_MODE_OLD_REGS.DATA_WIDTH =8100;
pwm_setting.PWM_MODE_OLD_REGS.THRESH =8100; pwm_set_spec_config(&pwm_setting);
printk(KERN_ALERT "Snsled: snsled_open done.\n");return0;}//alloc device majorstaticint vircdex_alloc_major(void){dev_t devt =0;int result =0; result = alloc_chrdev_region(&devt, g_snsled_minor, SNSLED_NUM, SNSLED_NODE_NAME);
g_snsled_major = MAJOR(devt);return result;}staticint snsled_release_major(void){dev_t devt = MKDEV(g_snsled_major, g_snsled_minor);
unregister_chrdev_region(devt,1);return0;}staticint snsled_setup_dev(struct snsled_cntx *dev){int err, devno = MKDEV(g_snsled_major, g_snsled_minor); cdev_init(&(dev->cdev),&g_snsled_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno,1);if(err){return err;}//init_MUTEX(&(dev->sem));
sema_init(&(dev->sem),1);return0;}staticint snsled_unsetup_dev(struct snsled_cntx *dev){
cdev_del(&(dev->cdev));return0;}staticint snsled_create_devfiles(dev_t devt){//, const struct device_attribute *attr) {int err =-1;struct device *dev = NULL; g_snsled_class = class_create(THIS_MODULE, SNSLED_CLASS_NAME);if(IS_ERR(g_snsled_class)){
err = PTR_ERR(g_snsled_class);
printk(KERN_ALERT "Failed to create class.\n");goto CLASS_CREATE_ERR;} dev = device_create(g_snsled_class, NULL, devt, NULL, SNSLED_DEVICE_NAME);//dev = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME); //device_create( my_class, NULL, MKDEV(hello_major, 0), "hello" "%d", 0 );//dev = device_create(g_snsled_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME);if(IS_ERR(dev)){
err = PTR_ERR(dev);
printk(KERN_ALERT "Failed to create device.\n");goto DEVICE_CREATE_ERR;}/*err = device_create_file(dev, attr);
if(err < 0) {
printk(KERN_ALERT"Failed to create attribute file.");
goto DEVICE_CREATE_FILE_ERR;
}*/
printk(KERN_ALERT "seems ok.\n");//zmk@@debug return0; DEVICE_CREATE_FILE_ERR:
device_destroy(g_snsled_class, devt);
DEVICE_CREATE_ERR:
class_destroy(g_snsled_class);
CLASS_CREATE_ERR:return err;}staticint snsled_delete_devfiles(dev_t devt){
device_destroy(g_snsled_class, devt);
class_destroy(g_snsled_class);//device_remove_file(dev, attr);return0;}staticint snsled_read_proc(char*buf,char**start,off_t offset,int count,int*eof,void*data){int len =sprintf(buf,"snsled read proc.\n");return len;}staticint snsled_create_proc_file(void){struct proc_dir_entry *entry = NULL; entry = create_proc_read_entry(SNSLED_PROC_NAME,0,
NULL, snsled_read_proc,
NULL);if(entry){return0;}else{return-1;}}staticint snsled_delete_proc_file(void){
remove_proc_entry(SNSLED_PROC_NAME, NULL);return0;} MODULE_LICENSE("GPL");staticint snsled_init(void){int err =-1;dev_t devt =0;//[1] alloc node number
err = vircdex_alloc_major();if(0> err){
printk(KERN_ALERT"alloc major failed.\n");goto ALLOC_MAJOR_ERR;}
devt = MKDEV(g_snsled_major, g_snsled_minor);//[2] device object init
g_snsled_ptr = kmalloc(sizeof(struct snsled_cntx), GFP_KERNEL);if(!g_snsled_ptr){
err =-ENOMEM;
printk(KERN_ALERT"kmalloc failed.\n");goto KMALLOC_ERR;}
memset(g_snsled_ptr,0,sizeof(struct snsled_cntx));//[3] setup device
err = snsled_setup_dev(g_snsled_ptr);if(0> err){
printk(KERN_ALERT"device setup failed.\n");goto DEVICE_SETUP_ERR;}//[4] create files in directory "/dev/" and "/sys/" ///err = snsled_create_devfiles(devt, attr);
err = snsled_create_devfiles(devt);if(0> err){
printk(KERN_ALERT"devfiles create failed.\n");goto DEVFILES_CREATE_ERR;}//[5] create proc file
err = snsled_create_proc_file();if(0> err){
printk(KERN_ALERT"proc file create failed.\n");goto PROC_FILE_CREATE_ERR;}return0; PROC_FILE_CREATE_ERR:
snsled_delete_devfiles(devt);
DEVFILES_CREATE_ERR:
snsled_unsetup_dev(g_snsled_ptr);
DEVICE_SETUP_ERR:
kfree(g_snsled_ptr);
ALLOC_MAJOR_ERR:
snsled_release_major();
KMALLOC_ERR:return err;}staticvoid snsled_exit(void){dev_t devt = MKDEV(g_snsled_major, g_snsled_minor); snsled_delete_proc_file();
snsled_delete_devfiles(devt);
snsled_unsetup_dev(g_snsled_ptr);
kfree(g_snsled_ptr);
snsled_release_major();} module_init(snsled_init);
module_exit(snsled_exit);

二、配置Kconfig

在snsled目录中,新建Kconfig文件:

       config SNSLED
tristate "snsled Driver"
default n #y ?
help
This is the sns led driver.

其中,tristate表示编译选项HELLO支持在编译内核时,hello模块支持以模块、内建和不编译三种编译方法。

三、配置Makefile

1、在snsled目录中,新建snsled文件夹对应的Makefile:

#obj-$(CONFIG_SNSLED) += snsled.o
obj-y += snsled.o

上面根据 CONFIG_SNSLED的值确定是否编译,y为编译。

2、修改snsled的父目录 drivers/下的Makefile,加入:

obj-$(CONFIG_HELLO) += snsled/

这样便能在编译时编译到snsled这个文件夹。

四、配置系统的autoconfig

打开 mediatek/config/bbk73_gb/autoconfig/kconfig/project,加入:

CONFIG_SNSLED=y

在这里定义变量 CONFIG_SNSLED.

  • ps:目前这里配置好像还不能snsled自动编译进去,目前的操作是直接在用到CONFIG_SNSLED的地方用y替代。

五、编译

./makeMtk bbk73_gb remake kernel bootimage

[编写hal模块]

一、新建xxx.h文件

进入"hardware/libhardware/include/hardware"目录,新建vircdev.h文件:

#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include <hardware/hardware.h> __BEGIN_DECLS /*定义模块ID*/
#define HELLO_HARDWARE_MODULE_ID "hello" /*硬件模块结构体*/
struct hello_module_t {
struct hw_module_t common;
}; /*硬件接口结构体*/
struct hello_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct hello_device_t* dev, int val);
int (*get_val)(struct hello_device_t* dev, int* val);
}; __END_DECLS #endif

这里按照Android硬件抽象层规范的要求,分别定义模块ID、模块结构体hello_module_t以及硬件接口结构体hello_device_t。在硬件接口结构体中,fd表示设备文件描述符,对应linux下我们经常接触到的设备文件"/dev/xxx",set_val和get_val为该HAL对上提供的函数接口。

二、新建xxx.c文件

进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。 hello.c的内容较多,我们分段来看。

1、包含相关头文件和定义相关结构

#define LOG_TAG "HelloStub"

#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h> #define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Hello"
#define MODULE_AUTHOR "shyluo@gmail.com" /*设备打开和关闭接口*/
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int hello_device_close(struct hw_device_t* device); /*设备访问接口*/
static int hello_set_val(struct hello_device_t* dev, int val);
static int hello_get_val(struct hello_device_t* dev, int* val); /*模块方法表*/
static struct hw_module_methods_t hello_module_methods = {
open: hello_device_open
}; /*模块实例变量*/
struct hello_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: HELLO_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &hello_module_methods,
}
};

这里,实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的。

2、定义hello_device_open函数

static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t)); if(!dev) {
LOGE("Hello Stub: failed to alloc space");
return -EFAULT;
} memset(dev, 0, sizeof(struct hello_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = hello_device_close;
dev->set_val = hello_set_val;dev->get_val = hello_get_val; if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev);
return -EFAULT;
} *device = &(dev->common);
LOGI("Hello Stub: open /dev/hello successfully."); return 0;
}

DEVICE_NAME定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而hello_device_open一般是由上层APP来调用的,这些APP一般不具有root权限,这时候就导致打开设备文件失败:

Hello Stub: failed to open /dev/hello -- Permission denied.

解决办法是类似于Linux的udev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,里面有一个名为ueventd.rc文件,往里面添加一行:

/dev/hello 0666 root root

3、定义自定义的api函数

这里定义hello_device_close、hello_set_val和hello_get_val这三个函数:

static int hello_device_close(struct hw_device_t* device) {
struct hello_device_t* hello_device = (struct hello_device_t*)device; if(hello_device) {
close(hello_device->fd);
free(hello_device);
} return 0;
} static int hello_set_val(struct hello_device_t* dev, int val) {
LOGI("Hello Stub: set value %d to device.", val); write(dev->fd, &val, sizeof(val)); return 0;
} static int hello_get_val(struct hello_device_t* dev, int* val) {
if(!val) {
LOGE("Hello Stub: error val pointer");
return -EFAULT;
} read(dev->fd, val, sizeof(*val)); LOGI("Hello Stub: get value %d from device", *val); return 0;
}

三、在hello目录下新建Android.mk文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := hello.c
LOCAL_MODULE := hello.default
include $(BUILD_SHARED_LIBRARY)

注意,LOCAL_MODULE的定义规则,hello后面跟有default,hello.default能够保证我们的模块总能被硬象抽象层加载到。

四、编译、重新打包Android系统镜像system.img

$:~/Android$ mmm hardware/libhardware/modules/hello

编译成功后,就可以在out/target/product/generic/system/lib/hw目录下看到hello.default.so文件了。

$:USER-NAME@MACHINE-NAME:~/Android$ make snod

重新打包后,system.img就包含我们定义的硬件抽象层模块hello.default了。

[编写jni]

虽然上一节我们在Android系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java应用程序还不能访问到我们的硬件。我们还必须编写JNI方法和在Android的Application Frameworks层增加API接口,才能让上层Application访问我们的硬件。在这一节中,我们将首先完成jni接口的编写。

一、新建com_android_server_HelloService.cpp文件

进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件:

#define LOG_TAG "HelloService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <stdio.h> namespace android
{
/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/
struct hello_device_t* hello_device = NULL; /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
int val = value;
LOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return;
} hello_device->set_val(hello_device, val);
} /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
static jint hello_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val); LOGI("Hello JNI: get value %d from device.", val); return val;
} /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
} /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init(JNIEnv* env, jclass clazz) {
hello_module_t* module; LOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
} /*JNI方法表*/
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
}; /*注册JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
};

注意文件的命名方法,com_android_server前缀表示的是包名,表示硬件服务HelloService是放在frameworks/base/services/java目录下的com/android/server目录的,即存在一个命名为com.android.server.HelloService的类。这里,我们暂时略去HelloService类的描述,在下一篇文章中,我们将回到HelloService类来。简单地说,HelloService是一个提供Java接口的硬件访问服务类。

在这个cpp文件中,我们主要是做了以下事情:

1、包括头文件

(尤其是在hal层所定义的头文件)

#define LOG_TAG "HelloService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <stdio.h>

2、编写jni接口

通过对hal中函数的调用,编写jni接口(这里只是简单地进行了一层包装):

  • 注意,linux driver -- hal -- jni, jni与linux driver并无直接关系,即jni的函数接口与linux driver不一定完全一一对应,很简单的一个例子便是在linux driver中可能只有一个ioctl函数,可是在hal层却通过对ioctl的调用实现了get,set,exchange等多个功能.

    /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
    static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
    int val = value;
    LOGI("Hello JNI: set value %d to device.", val);
    if(!hello_device) {
    LOGI("Hello JNI: device is not open.");
    return;
    } hello_device->set_val(hello_device, val);
    } /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
    static jint hello_getVal(JNIEnv* env, jobject clazz) {
    int val = 0;
    if(!hello_device) {
    LOGI("Hello JNI: device is not open.");
    return val;
    }
    hello_device->get_val(hello_device, &val); LOGI("Hello JNI: get value %d from device.", val); return val;
    }

3、定义jni加载函数,注册jni方法表

/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
} /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init(JNIEnv* env, jclass clazz) {
hello_module_t* module; LOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
} /*JNI方法表*/
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
}; /*注册JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}

其中,上面示例中的jni加载函数hello_init是通过hal中定义的hello_device_open函数实现的。在hello_init函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定义的。Android硬件抽象层会根据HELLO_HARDWARE_MODULE_ID的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。在jniRegisterNativeMethods函数中,第二个参数的值必须对应HelloService所在的包的路径,即com.android.server.HelloService。

二、修改onload.cpp,使系统启动时自动加载JNI方法调用表

修改frameworks/base/services/jni/onload.cpp:

1、在namespace android增加register_android_server_HelloService函数声明:

namespace android {
//......
int register_android_server_HelloService(JNIEnv *env);
};

2、在JNI_onLoad增加register_android_server_HelloService函数调用:

extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
//......
register_android_server_HelloService(env);
//......
}

这样,在Android系统初始化时,就会自动加载该JNI方法调用表。

三、修改Android.mk文件,添加编译路径

打开frameworks/base/services/jni/Android.mk,在LOCAL_SRC_FILES变量中增加一行:

LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_UsbService.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
com_android_server_HelloService.cpp /
onload.cpp

四、编译和重新生成system.img

$:mmm frameworks/base/services/jni
$:make snod

这样,重新打包的system.img镜像文件就包含我们刚才编写的JNI方法了

[编写Framework接口]

在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行。

一、定义通信接口

1、新增接口文件

进入到frameworks/base/core/java/android/os目录,新增IHelloService.aidl接口定义文件:

package android.os;  

interface IHelloService {
void setVal(int val);
int getVal();
}

IHelloService接口主要提供了设备和获取硬件寄存器val的值的功能,分别通过setVal和getVal两个函数来实现。

2、添加编译路径

返回到frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IHelloService.aidl源文件:

LOCAL_SRC_FILES += /
//......
core/java/android/os/IVibratorService.aidl /
core/java/android/os/IHelloService.aidl /
core/java/android/service/urlrenderer/IUrlRendererService.aidl /
//.....

3、编译接口文件

$:mmm frameworks/base

这样,就会根据IHelloService.aidl生成相应的IHelloService.Stub接口。

二、建立java文件,编写Framework接口

进入到frameworks/base/services/java/com/android/server目录,新增HelloService.java文件:

package com.android.server;
import android.content.Context;
import android.os.IHelloService;
import android.util.Slog;
public class HelloService extends IHelloService.Stub {
private static final String TAG = "HelloService";
HelloService() {
init_native();
}
public void setVal(int val) {
setVal_native(val);
}
public int getVal() {
return getVal_native();
} private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};

三、在ServerThread::run函数中增加加载代码

修改同目录的SystemServer.java文件:

@Override
public void run() {
//.....
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
} //start:增加加载代码
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
//end
//......
}

四、编译、重新打包system.img

$:mmm frameworks/base/services/java
$:make snod

这样,重新打包后的system.img系统镜像文件就在Application Frameworks层中包含了我们自定义的硬件服务了,并且会在系统启动的时候会自动加载HelloService,这样应用程序就可以通过Java接口来访问Hello硬件服务了。

[App访问]

//...
import android.os.IHelloService;
//...
private IHelloService helloService = null;
//...
@Override
public void onCreate(Bundle savedInstanceState) {
//...
helloService = IHelloService.Stub.asInterface(
ServiceManager.getService("hello"));
//...
}
//...
int val = helloService.getVal();
//...
helloService.setVal(val);

Android系统驱动【转】的更多相关文章

  1. 第一章 Android系统移植与驱动开发概述

    本书第一章首先简单概要地介绍了关于Android系统移植和驱动开发的相关内容. 所谓“移植”是指为特定的自己的设备,如手机定制Android的过程.自己开发一些程序(移植)装载在设备上,使得Andro ...

  2. 浅谈Android系统移植、Linux设备驱动

    一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...

  3. 第一章Android系统移植与驱动开发概述--读书笔记

    以前,初步学习过嵌入式Linux驱动开发的基础课程,对于驱动开发可以说是有了一点点微末的基础吧.首先我们要对Android嵌入式系统有一个初步的认识,Android系统发展到今天已经具备了完善的架构. ...

  4. android系统平台显示驱动开发简要:LCD基本原理篇『一』

    平台信息:内核:linux3.4.39系统:android4.4 平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新浪博客 ...

  5. Android系统移植与驱动开发----第一章

    第一章 Android系统移植与驱动开发 Android源代码定制完全属于自己的嵌入式系统,但是支持的设备不多,所以要移植,而在移植的过程中使用的不得不提的是驱动开发. Android系统构架主要包括 ...

  6. Android系统--Binder系统具体框架分析(二)Binder驱动情景分析

    Android系统--Binder系统具体框架分析(二)Binder驱动情景分析 1. Binder驱动情景分析 1.1 进程间通信三要素 源 目的:handle表示"服务",即向 ...

  7. Android之 看“马达”如何贯通Android系统 (从硬件设计 --> 驱动 --> HAL --> JNI --> Framework --> Application)

    在Android 2.3(Gingerbread) 系统的时候,我写过一篇关于“Android 震动马达系统“的文章,当时的Linux内核还是2.6版本的.写那篇文章的目的,是想彻底的了解从硬件到驱动 ...

  8. Android系统移植与驱动开发

    21世纪,Android发展非常迅速,在市场上占有很大的比例,遥遥领先与iOS,很大程度上是因为任何人都可以利用Android的源代码定制完全属于自己的嵌入式开发系统,而不需要向Google交一分钱. ...

  9. Android系统--灯光系统驱动编写

    Android系统开发--Android灯光系统tiny4412_led_class驱动编写 框架分析 led_classdev_4412结构体 创建led_classdev_4412结构体 分配结构 ...

随机推荐

  1. jQuery hooks源码学习

    段落不够清晰,待整理 看jQuery源码的时候,经常见到含有hooks标志的对象,如cssHooks, attrHooks, propHooks, valHooks. 下面对其中的一段进行解读. jQ ...

  2. Core篇——初探IdentityServer4(OpenID Connect模式)

    Core篇——初探IdentityServer4(OpenID Connect客户端验证) 目录 1.Oauth2协议授权码模式介绍2.IdentityServer4的OpenID Connect客户 ...

  3. springdatajpa使用informix数据库出现no such column 异常的问题

    本博客属原创,转载请注明出处 问题描述: 环境: spring data jpa版本4.0.3 informix驱动版本3.50.JC9 程序结构 jpa配置文件对应的jdbc配置 dao层继承jpa ...

  4. 从ReadImage到ML- 一个不错的博客

    实在对不起原作者,为了不把文章淹没在 转载的海洋里.... 原文链接:     http://www.cnblogs.com/tornadomeet/archive/2012/09/26/270404 ...

  5. 01--数据结构——动态链表(C++)

    数据结构——动态链表(C++)   定义一个节点: [cpp] view plain copy   print? #include <iostream> using namespace s ...

  6. linux下的头文件和库文件搜索路径 (转)

     GCC 找头文件有三种策略: 1. 会在默认情况下指定到 /usr/include 文件夹 ( 更深层次的是一个相对路径, GCC 可执行程序的路径是 /usr/bin ,那么它在实际工作时指定头文 ...

  7. java类型和mysql类型的转换

    Integer  -----> int 11String     ----->  varchar 20Long       -----> bigint 20String      - ...

  8. Spring Boot 项目学习 (二) MySql + MyBatis 注解 + 分页控件 配置

    0 引言 本文主要在Spring Boot 基础项目的基础上,添加 Mysql .MyBatis(注解方式)与 分页控件 的配置,用于协助完成数据库操作. 1 创建数据表 这个过程就暂时省略了. 2 ...

  9. 了解 object.defineProperty 的基本使用方法(数据双向绑定的底层原理)

    Object.defineProperty 给一个对象定义一个新的属性或者在修改一个对象现有的属性,并返回这个对象 语法: Object.defineProperty(参数1,参数2,参数3) 参数1 ...

  10. nodejs 使用crypto实现sha256\md5加密

    var crypto = require('crypto'); var hash = crypto.createHash('sha256');// sha256或者md5 hash.update('1 ...