Android架构分析之Android驱动程序开发
Android版本:2.3.7_r1Linux内核版本:android-goldfish-2.6.29
本文介绍如何开发Android驱动程序并进行测试。
一、Android驱动程序开发
Android是基于Linux的,所以Android驱动程序的开发方法与Linux驱动程序开发方法相同。
下面我们通过一个例子程序来熟悉一下Android驱动程序的开发,这里只是一个简单的说明,如果你对Linux驱动开发也不熟悉,可以学习《Linux Device Driver》或参考的我博客的《LDD3源码分析》系列文章。
首先我们在Android内核源码的drivers目录下创建一个新的目录example,我们的驱动程序源码就放在这个目录下:
# mkdir drivers/example
# cd drivers/example
创建example.h文件,其内容如下:
1#ifndef _EXAMPLE_H_ 2#define _EXAMPLE_H_ 3 4#include <linux/cdev.h> 5#include <linux/semaphore.h> 6 7#define EXAMPLE_DEVICE_NODE_NAME"example" 8#define EXAMPLE_DEVICE_FILE_NAME"example" 9#define EXAMPLE_DEVICE_PROC_NAME"example"10#define EXAMPLE_DEVICE_CLASS_NAME "example"11#define EXAMPLE_MAJOR 01213struct example_dev {14 struct semaphore sem;15 struct cdev cdev;16 int val;17};1819#endif
该头文件中定义了驱动程序中将用到的一些宏,并定义了设备结构体example_dev,该结构体的成员sem用于保证对设备的同步访问,cdev表示该设备是Linux设备驱动中的字符设备,val则代表该设备中的一个寄存器,我们这个驱动程序的作用就是给用户层应用程序提供读写这个val寄存器的方法。
创建example.c文件,其内容如下:
1#include <linux/init.h>2#include <linux/module.h>3#include <linux/types.h>4#include <linux/fs.h>5#include <linux/proc_fs.h>6#include <linux/device.h>7#include <asm/uaccess.h>89#include "example.h" 10 11static int example_major = EXAMPLE_MAJOR; 12static int example_minor = 0; 13 14static struct class* example_class = NULL; 15static struct example_dev* example_dev = NULL; 16 17module_param(example_major, int, S_IRUGO); 18module_param(example_minor, int, S_IRUGO); 19 20static int example_open(struct inode* inode, struct file* filp) 21{ 22 struct example_dev* dev; 23 24 /* 25 * 取得设备结构体example_dev,保存在filp私有数据区中,以方便其它函数使用。 26 */ 27 dev = container_of(inode->i_cdev, struct example_dev, cdev); 28 filp->private_data = dev; 29 30 return 0; 31} 32 33static int example_release(struct inode* inode, struct file* filp) 34{ 35 return 0; 36} 37 38static ssize_t example_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) 39{ 40 struct example_dev* dev = filp->private_data; 41 ssize_t retval = 0; 42 43 /* 44 * 加锁 45 */ 46 if(down_interruptible(&(dev->sem))) 47 return -ERESTARTSYS; 48 49 if(count < sizeof(dev->val)) 50 goto out; 51 52 /* 53 * 读寄存器的值到用户空间。 54 */ 55 if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) 56 { 57 retval = -EFAULT; 58 goto out; 59 } 60 61 retval = sizeof(dev->val); 62 63out: 64 /* 65 * 解锁 66 */ 67 up(&(dev->sem)); 68 return retval; 69} 70 71static ssize_t example_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) 72{ 73 struct example_dev* dev = filp->private_data; 74 ssize_t retval = 0; 75 76 /* 77 * 加锁 78 */ 79 if(down_interruptible(&(dev->sem))) 80 return -ERESTARTSYS; 81 82 if(count != sizeof(dev->val)) 83 goto out; 84 85 /* 86 * 从用户空间读取并给寄存器赋值。 87 */ 88 if(copy_from_user(&(dev->val), buf, count)) 89 { 90 retval = -EFAULT; 91 goto out; 92 } 93 94 retval = sizeof(dev->val); 95 96out: 97 /* 98 * 解锁 99 */100 up(&(dev->sem));101 return retval;102}103104/*105 * 设备操作函数集106 */107static struct file_operations example_fops =108{109 .owner = THIS_MODULE,110 .open = example_open,111 .release = example_release,112 .read = example_read,113 .write = example_write,114};115116117/*118 * 在同步状态下读取寄存器的值。119 */120static ssize_t __example_get_val(struct example_dev* dev, char* buf)121{122 int val = 0;123124 if(down_interruptible(&(dev->sem)))125 return -ERESTARTSYS;126127 val = dev->val;128 up(&(dev->sem));129130 return snprintf(buf, 30, "%d\n", val);131}132133/*134 * 在同步状态下设置寄存器的值。135 */136static ssize_t __example_set_val(struct example_dev* dev, const char* buf, size_t count)137{138 int val = 0;139140 val = (int)simple_strtol(buf, NULL, 10);141142 if(down_interruptible(&(dev->sem)))143 return -ERESTARTSYS;144145 dev->val = val;146 up(&(dev->sem));147148 return count;149}150151/*152 * 对属性文件的读取操作函数。153 */154static ssize_t example_val_show(struct device* dev, struct device_attribute* attr, char* buf)155{156 struct example_dev* hdev = (struct example_dev*)dev_get_drvdata(dev);157158 return __example_get_val(hdev, buf);159}160161/*162 * 对属性文件的写操作函数。163 */164static ssize_t example_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)165{166 struct example_dev* hdev = (struct example_dev*)dev_get_drvdata(dev);167168 return __example_set_val(hdev, buf, count);169}170171/*172 * DEVICE_ATTR宏展开后生成的是dev_attr_val。173 * 指定属性名为"val“,对应的读写函数分别是example_val_show和example_val_store。174 */175static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, example_val_show, example_val_store);176177/*178 * /proc节点的读操作函数。179 */180static ssize_t example_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data)181{182 if(off > 0)183 {184 *eof = 1;185 return 0;186 }187188 return __example_get_val(example_dev, page);189}190191/*192 * /proc节点的写操作函数。193 */194static ssize_t example_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data)195{196 int err = 0;197 char* page = NULL;198199 if(len > PAGE_SIZE)200 {201 printk(KERN_ALERT"The buff is too large: %lu.\n", len);202 return -EFAULT;203 }204205 page = (char*)__get_free_page(GFP_KERNEL);206 if(!page)207 {208 printk(KERN_ALERT"Failed to alloc page.\n");209 return -ENOMEM;210 }211212 if(copy_from_user(page, buff, len))213 {214 printk(KERN_ALERT"Failed to copy buff from user.\n");215 err = -EFAULT;216 goto out;217 }218219 err = __example_set_val(example_dev, page, len);220221out:222 free_page((unsigned long)page);223 return err;224}225226/*227 * 创建/proc节点228 */229static void example_create_proc(void)230{231 struct proc_dir_entry* entry;232233 entry = create_proc_entry(EXAMPLE_DEVICE_PROC_NAME, 0, NULL);234 if(entry)235 {236 entry->owner = THIS_MODULE;237 entry->read_proc = example_proc_read;238 entry->write_proc = example_proc_write;239 }240}241242/*243 * 删除/proc节点244 */245static void example_remove_proc(void)246{247 remove_proc_entry(EXAMPLE_DEVICE_PROC_NAME, NULL);248}249250/*251 * 初始化设备结构体example_dev。252 */253static int__example_setup_dev(struct example_dev* dev)254{255 int retval;256257 /*258 * 取得设备编号259 */260 dev_t devno = MKDEV(example_major, example_minor);261262 /*263 * 将设备结构体内存空间初始化为0。264 */265 memset(dev, 0, sizeof(struct example_dev));266267 /*268 * 初始化设备结构体的cdev成员,指定owner和操作函数集。269 */270 cdev_init(&(dev->cdev), &example_fops);271 dev->cdev.owner = THIS_MODULE;272 dev->cdev.ops = &example_fops;273274 /*275 * 调用cdev_add,通知内核该字符设备的存在。276 */277 retval = cdev_add(&(dev->cdev),devno, 1);278 if(retval)279 {280 return retval;281 }282283 /*284 * 初始化信号量285 */286 init_MUTEX(&(dev->sem));287288 /*289 *将寄存器val值初始化为0290 */291 dev->val = 0;292293 return 0;294}295296/*297 * 模块初始化函数。298 */299static int __init example_init(void)300{301 int retval = -1;302 dev_t dev = 0;303 struct device* device = NULL;304305 printk(KERN_ALERT"Initializing example device.\n");306307 /*308 * 如果用户指定了主设备号,即example_major不为0,则调用309 * register_chrdev_region分配指定的设备编号。310 * 如果用户没有指定主设备号,即example_major为0,则调用311 * alloc_chrdev_region动态分配设备编号。312 */313 if (example_major) {314 dev = MKDEV(example_major, example_minor);315 retval = register_chrdev_region(dev, 1, EXAMPLE_DEVICE_NODE_NAME);316 } else {317 retval = alloc_chrdev_region(&dev, example_minor, 1, EXAMPLE_DEVICE_NODE_NAME);318 }319 if (retval < 0) {320 printk(KERN_WARNING "can't get example_major %d\n", example_major);321 goto fail;322 }323324 /*325 * 取得主设备号和次设备号326 */327 example_major = MAJOR(dev);328 example_minor = MINOR(dev);329330 /*331 * 为设备结构体example_dev动态分配内存空间。332 */333 example_dev = kmalloc(sizeof(struct example_dev), GFP_KERNEL);334 if(!example_dev)335 {336 retval = -ENOMEM;337 printk(KERN_ALERT"Failed to alloc example_dev.\n");338 goto unregister;339 }340341 /*342 * 调用__example_setup_dev函数对example_dev结构体进行初始化。343 */344 retval = __example_setup_dev(example_dev);345 if(retval)346 {347 printk(KERN_ALERT"Failed to setup dev: %d.\n", retval);348 goto cleanup;349 }350351 /*352 * 创建类example,class_create函数执行成功后,在/sys/class目录下353 * 就会出现一个名为example的目录。354 */355 example_class = class_create(THIS_MODULE, EXAMPLE_DEVICE_CLASS_NAME);356 if(IS_ERR(example_class))357 {358 retval = PTR_ERR(example_class);359 printk(KERN_ALERT"Failed to create example class.\n");360 goto destroy_cdev;361 }362363 /*364 * 创建设备,device_create函数执行成功后,会生成/dev/example文件365 * 和/sys/class/example/example目录及相关文件。366 * 注意device的类型是struct device,代表一个设备。367 */368 device = device_create(example_class, NULL, dev, "%s", EXAMPLE_DEVICE_FILE_NAME);369 if(IS_ERR(device))370 {371 retval = PTR_ERR(device);372 printk(KERN_ALERT"Failed to create example device.");373 goto destroy_class;374 }375376 /*377 * 创建属性文件,对应的属性操作函数由dev_attr_val指定。378 */379 retval = device_create_file(device, &dev_attr_val);380 if(retval < 0)381 {382 printk(KERN_ALERT"Failed to create attribute val.");383 goto destroy_device;384 }385386 /*387 * 将example_dev保存在设备私有数据区中。388 */389 dev_set_drvdata(device, example_dev);390391 /*392 * 创建proc节点。393 */394 example_create_proc();395396 printk(KERN_ALERT"Succedded to initialize example device.\n");397 return 0;398399destroy_device:400 device_destroy(example_class, dev);401402destroy_class:403 class_destroy(example_class);404405destroy_cdev:406 cdev_del(&(example_dev->cdev));407408cleanup:409 kfree(example_dev);410411unregister:412 unregister_chrdev_region(MKDEV(example_major, example_minor), 1);413414fail:415 return retval;416}417418/*419 * 模块清理函数。420 */421static void __exit example_exit(void)422{423 dev_t dev = MKDEV(example_major, example_minor);424425 printk(KERN_ALERT"Destroy example device.\n");426427 example_remove_proc();428429 if(example_class)430 {431 device_destroy(example_class, MKDEV(example_major, example_minor));432 class_destroy(example_class);433 }434435 if(example_dev)436 {437 cdev_del(&(example_dev->cdev));438 kfree(example_dev);439 }440441 unregister_chrdev_region(dev, 1);442}443444MODULE_LICENSE("GPL");445446module_init(example_init);447module_exit(example_exit);
代码中的注释已经很详细,这里再简单说一下:
20 - 114行,定义了example_open、example_release、example_read、example_write这4个函数,并将这4个函数赋值给file_operations结构体变量example_fops,应用程序通过/dev/example设备节点访问设备时,就会相应调用到这几个函数。
117 - 175行,定义了通过/sys/class/example/example/val对设备进行访问时,相应会调用的函数。
177 - 248行,定义了通过/proc/example对设备进行访问时,需要调用的函数。
有了源码,下面我们需要编写一个Makefile,对这些源码进行编译。
Makefile内容如下:
obj-y += example.o
为什么Makefile能只有这一行,大家可以参考LDD3第29页的介绍,另外也可以参考我的博客文章LDD3源码分析之hello.c与Makefile模板。
下面我们需要通知内核我们增加了一个新的驱动程序example,以便在编译内核时会对example进行编译。通知内核的方法是修改内核drivers目录下的Makefile,在该文件的最后增加一句:
obj-y += example/
为什么增加这一句就能通知内核呢?这个是Linux内核编译机制决定的,如果展开分析会包括很多内容,这里不再细述。大家先知道这么改就可以了。
以上工作完成后,我们就可以重新编译Linux内核了,回到内核根目录下,执行如下命令:
make
编译成功后得到的内核镜像就已经包括了我们加入的example驱动了。
下面我们来测试一下新加入的example驱动程序是否工作正常。
首先用我们新编译出来的Linux内核启动Android模拟器:
# source build/envsetup.sh
# lunch
# emulator -kernel kernel/goldfish/arch/arm/boot/zImage
# adb shell
进入系统后,可以看到example驱动程序创建的设备节点:/dev/example、/proc/example以及/sys/devices/virtual/example/example目录。
执行如下命令:
# cat /proc/example
0
# echo 5 > /proc/example
# cat /proc/example
5
可以看到,我们可以通过/proc/example节点访问设备寄存器。
# cat /sys/class/example/example/val
5
# echo 6 > /sys/class/example/example/val
# cat /sys/class/example/example/val
6
可以看到,我们可以通过/sys/class/example/example/val节点访问设备寄存器。
我们也可以通过/dev/example节点对设备寄存器进行访问,下一篇博客中我们将写一个C程序通过/dev/example节点访问设备寄存器,同时演示Android系统下开发C程序的步骤。
---------------------
作者:liuhaoyutz
来源:CSDN
原文:https://blog.csdn.net/liuhaoyutz/article/details/8500300
版权声明:本文为博主原创文章,转载请附上博文链接!
页:
[1]