Android版本:2.3.7_r1
Linux内核版本: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 0 12 13struct example_dev { 14 struct semaphore sem; 15 struct cdev cdev; 16 int val; 17}; 18 19#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> 8 9#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} 103 104/* 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}; 115 116 117/* 118 * 在同步状态下读取寄存器的值。 119 */ 120static ssize_t __example_get_val(struct example_dev* dev, char* buf) 121{ 122 int val = 0; 123 124 if(down_interruptible(&(dev->sem))) 125 return -ERESTARTSYS; 126 127 val = dev->val; 128 up(&(dev->sem)); 129 130 return snprintf(buf, 30, "%d\n", val); 131} 132 133/* 134 * 在同步状态下设置寄存器的值。 135 */ 136static ssize_t __example_set_val(struct example_dev* dev, const char* buf, size_t count) 137{ 138 int val = 0; 139 140 val = (int)simple_strtol(buf, NULL, 10); 141 142 if(down_interruptible(&(dev->sem))) 143 return -ERESTARTSYS; 144 145 dev->val = val; 146 up(&(dev->sem)); 147 148 return count; 149} 150 151/* 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); 157 158 return __example_get_val(hdev, buf); 159} 160 161/* 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); 167 168 return __example_set_val(hdev, buf, count); 169} 170 171/* 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); 176 177/* 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 } 187 188 return __example_get_val(example_dev, page); 189} 190 191/* 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; 198 199 if(len > PAGE_SIZE) 200 { 201 printk(KERN_ALERT"The buff is too large: %lu.\n", len); 202 return -EFAULT; 203 } 204 205 page = (char*)__get_free_page(GFP_KERNEL); 206 if(!page) 207 { 208 printk(KERN_ALERT"Failed to alloc page.\n"); 209 return -ENOMEM; 210 } 211 212 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 } 218 219 err = __example_set_val(example_dev, page, len); 220 221out: 222 free_page((unsigned long)page); 223 return err; 224} 225 226/* 227 * 创建/proc节点 228 */ 229static void example_create_proc(void) 230{ 231 struct proc_dir_entry* entry; 232 233 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} 241 242/* 243 * 删除/proc节点 244 */ 245static void example_remove_proc(void) 246{ 247 remove_proc_entry(EXAMPLE_DEVICE_PROC_NAME, NULL); 248} 249 250/* 251 * 初始化设备结构体example_dev。 252 */ 253static int __example_setup_dev(struct example_dev* dev) 254{ 255 int retval; 256 257 /* 258 * 取得设备编号 259 */ 260 dev_t devno = MKDEV(example_major, example_minor); 261 262 /* 263 * 将设备结构体内存空间初始化为0。 264 */ 265 memset(dev, 0, sizeof(struct example_dev)); 266 267 /* 268 * 初始化设备结构体的cdev成员,指定owner和操作函数集。 269 */ 270 cdev_init(&(dev->cdev), &example_fops); 271 dev->cdev.owner = THIS_MODULE; 272 dev->cdev.ops = &example_fops; 273 274 /* 275 * 调用cdev_add,通知内核该字符设备的存在。 276 */ 277 retval = cdev_add(&(dev->cdev),devno, 1); 278 if(retval) 279 { 280 return retval; 281 } 282 283 /* 284 * 初始化信号量 285 */ 286 init_MUTEX(&(dev->sem)); 287 288 /* 289 *将寄存器val值初始化为0 290 */ 291 dev->val = 0; 292 293 return 0; 294} 295 296/* 297 * 模块初始化函数。 298 */ 299static int __init example_init(void) 300{ 301 int retval = -1; 302 dev_t dev = 0; 303 struct device* device = NULL; 304 305 printk(KERN_ALERT"Initializing example device.\n"); 306 307 /* 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 } 323 324 /* 325 * 取得主设备号和次设备号 326 */ 327 example_major = MAJOR(dev); 328 example_minor = MINOR(dev); 329 330 /* 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 } 340 341 /* 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 } 350 351 /* 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 } 362 363 /* 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 } 375 376 /* 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 } 385 386 /* 387 * 将example_dev保存在设备私有数据区中。 388 */ 389 dev_set_drvdata(device, example_dev); 390 391 /* 392 * 创建proc节点。 393 */ 394 example_create_proc(); 395 396 printk(KERN_ALERT"Succedded to initialize example device.\n"); 397 return 0; 398 399destroy_device: 400 device_destroy(example_class, dev); 401 402destroy_class: 403 class_destroy(example_class); 404 405destroy_cdev: 406 cdev_del(&(example_dev->cdev)); 407 408cleanup: 409 kfree(example_dev); 410 411unregister: 412 unregister_chrdev_region(MKDEV(example_major, example_minor), 1); 413 414fail: 415 return retval; 416} 417 418/* 419 * 模块清理函数。 420 */ 421static void __exit example_exit(void) 422{ 423 dev_t dev = MKDEV(example_major, example_minor); 424 425 printk(KERN_ALERT"Destroy example device.\n"); 426 427 example_remove_proc(); 428 429 if(example_class) 430 { 431 device_destroy(example_class, MKDEV(example_major, example_minor)); 432 class_destroy(example_class); 433 } 434 435 if(example_dev) 436 { 437 cdev_del(&(example_dev->cdev)); 438 kfree(example_dev); 439 } 440 441 unregister_chrdev_region(dev, 1); 442} 443 444MODULE_LICENSE("GPL"); 445 446module_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程序的步骤。