作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz
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
5#include
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
2#include
3#include
4#include
5#include
6#include
7#include
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程序的步骤。