5 [App访问]

[编写linux驱动程序]

一、编写驱动核心程序

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

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

//snsled.h

//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");}

#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; //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");

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 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;

}

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 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);

//...

MTK andorid从底层到上层添加驱动的更多相关文章

  1. Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整(原理:底层SurfaceView+上层绘制ImageView)

    Android摄像头:只拍摄SurfaceView预览界面特定区域内容(矩形框)---完整实现(原理:底层SurfaceView+上层绘制ImageView) 分类: Android开发 Androi ...

  2. 【initrd】向虚拟文件系统initrd.img中添加驱动

    虚拟文件系统:initrd-2.6.18-194.el5.img 希望添加网卡或SCSI等驱动 步骤: 解压initrd-2.6.18-194.el5.img: 添加*.ko文件,并修改init可执行 ...

  3. ARM Mcp2515添加驱动

    Mcp2515添加驱动   2012-01-10 21:39:32 上图1: 上图2: 上图3: 之前完成了spi接口驱动,所以mcp2515也是通过spi来读写数据的.就是多加一个中断脚. 另外在2 ...

  4. EasyPlayer-RTSP播放器:从底层到上层专注于RTSP播放Windows、Android、iOS RTSP Player

    EasyPlayer-RTSP播放器是一套RTSP专用的播放器,包括有:Windows(支持IE插件,npapi插件).Android.iOS三个平台,是由EasyDSS团队开发和维护的区别于市面上大 ...

  5. 往android的内核添加驱动及 ueventd.rc 修改【转】

    本文转载自:http://blog.csdn.net/lkqboy2599/article/details/8350100 向android的内核添加驱动,其实就是向linux内核添加驱动.主要在两个 ...

  6. MTK Androiod HAL如何向上层提供接口

    Android中HAL如何向上层提供接口总结 转自:http://blog.csdn.net/flydream0/article/details/7086273 参考文献: http://blog.c ...

  7. 通用权限管理系统底层更换最新Oracle驱动的方法

    通用权限管理系统底层先前访问Oracle数据库时需要客户端安装驱动软件,如下图: 安装完毕还需要一番配置,系统再引用其dll, 现在我们使用了最新的dll 该dll是Oracle出的最新的版本. 通用 ...

  8. Qt添加驱动——Qt数据库之添加MySQL驱动插件

    Qt数据库之添加MySQL驱动插件(1) 现在可用的数据库驱动只有3种,在Qt中,我们需要自己编译其他数据库驱动的代码,让它们以插件的形式来使用.下面我们就以现在比较流行的MySQL数据库为例,说明一 ...

  9. MTK 平台上如何给 camera 添加一种 preview size

    1,首先检查该项目所使用的是哪一颗sensor, 就以OV2659 为例OV2659 是一颗2M 的摄像头,Sensor 吐出的数据分辨率能达到 1600*1200,肯定是支持 1280*720 的分 ...

随机推荐

  1. MySQL日期处理

    一.MySQL 获得当前日期时间 函数1.1 获得当前日期+时间(date + time)函数:now()mysql> select now(); +---------------------+ ...

  2. 笔记《精通css》第2章 选择器,注释

    第2章    选择器,注释 1.常用选择器(id选择器,类选择器,类型选择器,后代选择器,伪类选择器(文档结构之外)) 通用选择器(*{    }) 高级选择器(子选择器,相邻同胞选择器,属性选择器) ...

  3. Verilog设计中的锁存器

    问题: 什么是锁存器? 什么时候出现锁存器? 锁存器对电路有什么影响? 如何在FPGA设计中避免锁存器? 在FPGA设计中应该避免锁存器.实际上,锁存器与D触发器实现的逻辑功能基本相同,都有暂存数据的 ...

  4. Long time no blogging

    It is a long time before I posted the last blog on myspace and seems that all of my blogs/documents ...

  5. Python学前基础知识

    Python基础计算机常识:硬件性能:CPU.内存输入设备:鼠标.键盘外部存储设备:硬盘输出设备;显示器.打印机(不算自带)通讯设备:无线网卡----------------------------- ...

  6. 自定义Jquery 下拉框

    (function ($){ 'use strict'; var g_id = 0; var g_open_id = []; $.fn.select3 = function () { var _id ...

  7. Elasticsearch插件清单

    1.API插件:主要对Elasticsearch添加的API特性或者功能,通常用于搜索或者映射 2. 报警插件: 当Elasticsearch的索引指标超过阀值时就会触发 3. 分词插件:ik是比较好 ...

  8. Spring事务管理全面分析

    Spring 事务属性分析什么是事物  事务管理对于企业应用而言至关重要.它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性.就像银行的自助取款机,通常都能正常 ...

  9. centos7 取消Ctrl+Alt+Del重启功能

    转载:http://www.cnblogs.com/huangjc/p/4536620.html Linux默认允许任何人按下Ctrl+Alt+Del来重启系统.但是在生产环境中,应该停用按下Ctrl ...

  10. vue :class 可以接收 字符串 数组 和 对象 对象里面的key值 根据true或false 显示不显示

    vue :class 可以接收 字符串 数组 和 对象 对象里面的key值 根据true或false 显示不显示 https://cn.vuejs.org/v2/guide/class-and-sty ...