日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

MTK Android添加驅(qū)動(dòng)模塊

 dwlinux_gs 2014-07-17










[編寫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



    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多