MTK Android添加驱动模块

  • 1 [编写linux驱动程序]

    • 1.1 一、编写驱动核心程序
    • 1.2 二、配置Kconfig
    • 1.3 三、配置Makefile
    • 1.4 四、配置系统的autoconfig
    • 1.5 五、编译
  • 2 [编写hal模块]
    • 2.1 一、新建xxx.h文件
    • 2.2 二、新建xxx.c文件
      • 2.2.1 1、包含相关头文件和定义相关结构
      • 2.2.2 2、定义hello_device_open函数
      • 2.2.3 3、定义自定义的api函数
    • 2.3 三、在hello目录下新建Android.mk文件
    • 2.4 四、编译、重新打包Android系统镜像system.img
  • 3 [编写jni]
    • 3.1 一、新建com_android_server_HelloService.cpp文件

      • 3.1.1 1、包括头文件
      • 3.1.2 2、编写jni接口
      • 3.1.3 3、定义jni加载函数,注册jni方法表
    • 3.2 二、修改onload.cpp,使系统启动时自动加载JNI方法调用表
    • 3.3 三、修改Android.mk文件,添加编译路径
    • 3.4 四、编译和重新生成system.img
  • 4 [编写Framework接口]
    • 4.1 一、定义通信接口

      • 4.1.1 1、新增接口文件
      • 4.1.2 2、添加编译路径
      • 4.1.3 3、编译接口文件
    • 4.2 二、建立java文件,编写Framework接口
    • 4.3 三、在ServerThread::run函数中增加加载代码
    • 4.4 四、编译、重新打包system.img
  • 5 [App访问]

[编写linux驱动程序]

一、编写驱动核心程序

这里说的驱动核心程序是指运行在内核空间的,完全按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 1printk(KERN_ALERT "Snsled read.\n");
#elsechar tmp_buf[512] = {0};int len =sprintf(tmp_buf, "snsled read.\n");if (copy_to_user(buf, tmp_buf, count)) {//do nothing}
#endifreturn 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 spaceif (_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 0retval = __put_user(g_snsled_ptr->r1, (int __user *)arg);printk(KERN_ALERT "Snsled ioctl:read r1:%i.\n", g_snsled_ptr->r1);#endifprintk(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");}
#endif  return 0;
}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");}
#endif  return 0;
}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");}return 0;
}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");}return 0;
}//for old mode
/**
struct _PWM_OLDMODE_REGS {U16 IDLE_VALUE; //0U16 GUARD_VALUE;   //0U16 GDURATION;   //~U16 WAVE_NUM;    //0U16 DATA_WIDTH;  //high level, 13bits, 0~8191U16 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");return 0;
}//alloc device major
static int 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;
}
static int snsled_release_major(void) {dev_t devt = MKDEV(g_snsled_major, g_snsled_minor);unregister_chrdev_region(devt, 1);return 0;
}static int 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);return 0;
}
static int snsled_unsetup_dev(struct snsled_cntx *dev) {cdev_del(&(dev->cdev));return 0;
}static int 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    return 0;DEVICE_CREATE_FILE_ERR:device_destroy(g_snsled_class, devt);
DEVICE_CREATE_ERR:class_destroy(g_snsled_class);
CLASS_CREATE_ERR:  return err;
}
static int snsled_delete_devfiles(dev_t devt) {device_destroy(g_snsled_class, devt);class_destroy(g_snsled_class);//device_remove_file(dev, attr);return 0;
}static int 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;
}
static int 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){return 0;}else                            {                        return -1;                       }
}
static int snsled_delete_proc_file(void) {remove_proc_entry(SNSLED_PROC_NAME, NULL);return 0;
}MODULE_LICENSE("GPL");static int snsled_init(void) {int err = -1;dev_t devt = 0;//[1] alloc node numbererr = 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 deviceerr = 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 fileerr = snsled_create_proc_file();if(0 > err){printk(KERN_ALERT"proc file create failed.\n");goto PROC_FILE_CREATE_ERR;}return 0;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;
}
static void 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 SNSLEDtristate "snsled Driver"default n #y ?helpThis 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文件:

@Overridepublic 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;
//...@Overridepublic void onCreate(Bundle savedInstanceState) {
//...helloService = IHelloService.Stub.asInterface(ServiceManager.getService("hello"));
//...}
//...
int val = helloService.getVal();
//...
helloService.setVal(val);
//...

Android 驱动(2)---MTKAndroid添加驱动模块相关推荐

  1. MTK Android添加驱动模块

    1 [编写linux驱动程序] 1.1 一.编写驱动核心程序 1.2 二.配置Kconfig 1.3 三.配置Makefile 1.4 四.配置系统的autoconfig 1.5 五.编译 2 [编写 ...

  2. Android驱动开发读书笔记六

    第六章 Linux 驱动的工作和访问方式是 Linux 的亮点之一,Linux 系统将每一个驱动都映射成一个文件.这些文件称为设备文件或驱动文件,都保存在/dev目录中,由于大多数Linux驱动都有与 ...

  3. Android 驱动开发系列二

    最近琐碎事太多了,都没什么时间来写blog.现在继续写这个android驱动的开发调试 这一章主要是讲如何测试驱动. 1.驱动的简单测试 在上一篇文章中,我们已经把添加驱动模块做完了,并把驱动下载到了 ...

  4. 初入android驱动开发之字符设备(一)

    大学毕业,初入公司,招进去的是android驱动开发工程师的岗位,那时候刚进去,首先学到的就是如何搭建kernel.android的编译环境,然后就是了解如何刷设备以及一些最基本的工具.如adb.fa ...

  5. Android 驱动(3)---Android驱动开发知识储备

    Android驱动开发知识储备 Android软件层次结构 (1)操作系统层 显示驱动(Frame Buffer),Flash内存驱动,照相机驱动,音频驱动,WiFi驱动,键盘驱动,蓝牙驱动,Bind ...

  6. Android驱动开发知识储备(纯手打,更新:2018.4.11)

    Android软件层次结构 (1)操作系统层 显示驱动(Frame Buffer),Flash内存驱动,照相机驱动,音频驱动,WiFi驱动,键盘驱动,蓝牙驱动,Binder IPC驱动,Power M ...

  7. Android系统中如何添加USB网络共享

    Android系统中如何添加USB网络共享 类别 需求 索引类别 USB网络共享 问题描述 平台是RT1296,在Android系统中已经有支持USB网络共享,但相应的产品系统中还未开启USB网络共享 ...

  8. 视频教程-Android驱动深度开发视频教程-驱动/内核开发

    Android驱动深度开发视频教程 2003 年毕业于中国科学技术大学,电子专业.软件专业双学位.近10年嵌入式开发经验,曾ZTE公司负责Linux底层系统开发. 近5年作为特聘讲师在若干个有名的培训 ...

  9. Android驱动开发全过程

    Android驱动开发全过程(有图有真相) 前言 意外在网上发现了这扁文章,看后感觉很有必要分享,所以整理并上传,希望大家喜欢. Android 硬件抽象层(HAL)概要介绍和学习计划 Android ...

最新文章

  1. php 数字转中文函数 1转一,一个阿拉伯数字转中文数字的函数-PHP教程,PHP函数
  2. JavaScript学习笔记——JS中的变量复制、参数传递和作用域链
  3. HttpClient ||GET请求||带参数的GET请求
  4. STM32 SPI NSS 引脚为不能拉高问题
  5. MySQL的TRUNCATE关键字
  6. 梦世界服务器修改指令,我的世界梦世界有哪些指令必须知道 梦世界所有必须知道指令汇总...
  7. 整数阶贝塞尔函数c语言,整数阶复宗量变形贝塞尔函数的计算.pdf
  8. linux下 mysql 忘记root用户密码解决办法
  9. java that关键字_Java中this关键字的几种用法
  10. 第一家云创大数据产业学院在佛山职业技术学院挂牌
  11. Javascript实现BF算法
  12. 分布式链路追踪 之 Skywalking 设计理念核心原理
  13. 完美的做图配色专辑,UI设计师赶紧收藏
  14. 物联网终端安全系列(之二) -- 物联网终端安全需求分析
  15. openocd编译安装使用 (基于百问网openjtag工具 ftdi)
  16. c语言程序烧写步骤,单片机烧写程序步骤
  17. 道家王凤仪《五行语录》
  18. Zend studio 调整优化
  19. 任务调度系统-业务线资源隔离
  20. Excel如何过滤座机号提取出手机号码

热门文章

  1. JSON在android中应用
  2. app抢购脚本如何编写_如何用1个记事本文件征服全世界?——cmd批处理脚本编写...
  3. php的list函数的使用,php list函数如何使用
  4. java kafka client_Kafka Client API 基本使用
  5. c#调用c语言的自定义函数,[转]在C#中调用C语言函数(静态调用Native DLL,Windows Microsoft.Net平台)...
  6. 《RabbitMQ 实战指南》第二章 RabbitMQ 入门
  7. OS操作系统概念简介
  8. field-symbols的用法[转]
  9. java中的浮点数相加
  10. ajax正确返回数据,却进入了error分支