[編寫linux驅(qū)動(dòng)程序]
一、編寫驅(qū)動(dòng)核心程序
這里說的驅(qū)動(dòng)核心程序是指運(yùn)行在內(nèi)核空間的,完全按linux驅(qū)動(dòng)格式編寫的,基本上與android沒什么關(guān)系,一般包括xxx.h和xxx.c文件。
進(jìn)入到kernel/drivers目錄,新建snsled目錄,然后建立對(duì)應(yīng)的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");}
#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表示編譯選項(xiàng)HELLO支持在編譯內(nèi)核時(shí),hello模塊支持以模塊、內(nèi)建和不編譯三種編譯方法。
三、配置Makefile
1、在snsled目錄中,新建snsled文件夾對(duì)應(yīng)的Makefile:
#obj-$(CONFIG_SNSLED) += snsled.o
obj-y += snsled.o
上面根據(jù) CONFIG_SNSLED的值確定是否編譯,y為編譯。
2、修改snsled的父目錄 drivers/下的Makefile,加入:
obj-$(CONFIG_HELLO) += snsled/
這樣便能在編譯時(shí)編譯到snsled這個(gè)文件夾。
四、配置系統(tǒng)的autoconfig
打開 mediatek/config/bbk73_gb/autoconfig/kconfig/project,加入:
CONFIG_SNSLED=y
在這里定義變量 CONFIG_SNSLED.
- ps:目前這里配置好像還不能snsled自動(dòng)編譯進(jìn)去,目前的操作是直接在用到CONFIG_SNSLED的地方用y替代。
五、編譯
./makeMtk bbk73_gb remake kernel bootimage
[編寫hal模塊]
一、新建xxx.h文件
進(jìn)入"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"
/*硬件模塊結(jié)構(gòu)體*/
struct hello_module_t {
struct hw_module_t common;
};
/*硬件接口結(jié)構(gòu)體*/
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硬件抽象層規(guī)范的要求,分別定義模塊ID、模塊結(jié)構(gòu)體hello_module_t以及硬件接口結(jié)構(gòu)體hello_device_t。在硬件接口結(jié)構(gòu)體中,fd表示設(shè)備文件描述符,對(duì)應(yīng)linux下我們經(jīng)常接觸到的設(shè)備文件"/dev/xxx",set_val和get_val為該HAL對(duì)上提供的函數(shù)接口。
二、新建xxx.c文件
進(jìn)入到hardware/libhardware/modules目錄,新建hello目錄,并添加hello.c文件。 hello.c的內(nèi)容較多,我們分段來看。
1、包含相關(guān)頭文件和定義相關(guān)結(jié)構(gòu)
#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"
/*設(shè)備打開和關(guān)閉接口*/
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);
/*設(shè)備訪問接口*/
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
};
/*模塊實(shí)例變量*/
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,
}
};
這里,實(shí)例變量名必須為HAL_MODULE_INFO_SYM,tag也必須為HARDWARE_MODULE_TAG,這是Android硬件抽象層規(guī)范規(guī)定的。
2、定義hello_device_open函數(shù)
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"。由于設(shè)備文件是在內(nèi)核驅(qū)動(dòng)里面通過device_create創(chuàng)建的,而device_create創(chuàng)建的設(shè)備文件默認(rèn)只有root用戶可讀寫,而hello_device_open一般是由上層APP來調(diào)用的,這些APP一般不具有root權(quán)限,這時(shí)候就導(dǎo)致打開設(shè)備文件失?。?/p>
Hello Stub: failed to open /dev/hello -- Permission denied.
解決辦法是類似于Linux的udev規(guī)則,打開Android源代碼工程目錄下,進(jìn)入到system/core/rootdir目錄,里面有一個(gè)名為ueventd.rc文件,往里面添加一行:
/dev/hello 0666 root root
3、定義自定義的api函數(shù)
這里定義hello_device_close、hello_set_val和hello_get_val這三個(gè)函數(shù):
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的定義規(guī)則,hello后面跟有default,hello.default能夠保證我們的模塊總能被硬象抽象層加載到。
四、編譯、重新打包Android系統(tǒng)鏡像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]
雖然上一節(jié)我們?cè)贏ndroid系統(tǒng)為我們自己的硬件增加了一個(gè)硬件抽象層模塊,但是現(xiàn)在Java應(yīng)用程序還不能訪問到我們的硬件。我們還必須編寫JNI方法和在Android的Application Frameworks層增加API接口,才能讓上層Application訪問我們的硬件。在這一節(jié)中,我們將首先完成jni接口的編寫。
一、新建com_android_server_HelloService.cpp文件
進(jìn)入到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
{
/*在硬件抽象層中定義的硬件訪問結(jié)構(gòu)體,參考<hardware/hello.h>*/
struct hello_device_t* hello_device = NULL;
/*通過硬件抽象層定義的硬件訪問接口設(shè)置硬件寄存器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;
}
/*通過硬件抽象層定義的硬件模塊打開接口打開硬件設(shè)備*/
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},
};
/*注冊(cè)JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
};
注意文件的命名方法,com_android_server前綴表示的是包名,表示硬件服務(wù)HelloService是放在frameworks/base/services/java目錄下的com/android/server目錄的,即存在一個(gè)命名為com.android.server.HelloService的類。這里,我們暫時(shí)略去HelloService類的描述,在下一篇文章中,我們將回到HelloService類來。簡(jiǎn)單地說,HelloService是一個(gè)提供Java接口的硬件訪問服務(wù)類。
在這個(gè)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接口
通過對(duì)hal中函數(shù)的調(diào)用,編寫jni接口(這里只是簡(jiǎn)單地進(jìn)行了一層包裝):
- 注意,linux driver -- hal -- jni, jni與linux driver并無直接關(guān)系,即jni的函數(shù)接口與linux driver不一定完全一一對(duì)應(yīng),很簡(jiǎn)單的一個(gè)例子便是在linux driver中可能只有一個(gè)ioctl函數(shù),可是在hal層卻通過對(duì)ioctl的調(diào)用實(shí)現(xiàn)了get,set,exchange等多個(gè)功能.
/*通過硬件抽象層定義的硬件訪問接口設(shè)置硬件寄存器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加載函數(shù),注冊(cè)jni方法表
/*通過硬件抽象層定義的硬件模塊打開接口打開硬件設(shè)備*/
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},
};
/*注冊(cè)JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
其中,上面示例中的jni加載函數(shù)hello_init是通過hal中定義的hello_device_open函數(shù)實(shí)現(xiàn)的。在hello_init函數(shù)中,通過Android硬件抽象層提供的hw_get_module方法來加載模塊ID為HELLO_HARDWARE_MODULE_ID的硬件抽象層模塊,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定義的。Android硬件抽象層會(huì)根據(jù)HELLO_HARDWARE_MODULE_ID的值在Android系統(tǒng)的/system/lib/hw目錄中找到相應(yīng)的模塊,然后加載起來,并且返回hw_module_t接口給調(diào)用者使用。在jniRegisterNativeMethods函數(shù)中,第二個(gè)參數(shù)的值必須對(duì)應(yīng)HelloService所在的包的路徑,即com.android.server.HelloService。
二、修改onload.cpp,使系統(tǒng)啟動(dòng)時(shí)自動(dòng)加載JNI方法調(diào)用表
修改frameworks/base/services/jni/onload.cpp:
1、在namespace android增加register_android_server_HelloService函數(shù)聲明:
namespace android {
//......
int register_android_server_HelloService(JNIEnv *env);
};
2、在JNI_onLoad增加register_android_server_HelloService函數(shù)調(diào)用:
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
//......
register_android_server_HelloService(env);
//......
}
這樣,在Android系統(tǒng)初始化時(shí),就會(huì)自動(dòng)加載該JNI方法調(diào)用表。
三、修改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系統(tǒng)中,硬件服務(wù)一般是運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中為各種應(yīng)用程序提供服務(wù)。因此,調(diào)用這些硬件服務(wù)的應(yīng)用程序與這些硬件服務(wù)之間的通信需要通過代理來進(jìn)行。
一、定義通信接口
1、新增接口文件
進(jìn)入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl接口定義文件:
package android.os;
interface IHelloService {
void setVal(int val);
int getVal();
}
IHelloService接口主要提供了設(shè)備和獲取硬件寄存器val的值的功能,分別通過setVal和getVal兩個(gè)函數(shù)來實(shí)現(xiàn)。
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
這樣,就會(huì)根據(jù)IHelloService.aidl生成相應(yīng)的IHelloService.Stub接口。
二、建立java文件,編寫Framework接口
進(jìn)入到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函數(shù)中增加加載代碼
修改同目錄的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系統(tǒng)鏡像文件就在Application Frameworks層中包含了我們自定義的硬件服務(wù)了,并且會(huì)在系統(tǒng)啟動(dòng)的時(shí)候會(huì)自動(dòng)加載HelloService,這樣應(yīng)用程序就可以通過Java接口來訪問Hello硬件服務(wù)了。
[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);
//...
參考資料:
在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內(nèi)核驅(qū)動(dòng)程序
在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務(wù)接口
http://www./en/%E6%B7%BB%E5%8A%A0%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9D%97
|