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文件,其內容如下:
[cpp]
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文件,其內容如下:
[cpp]
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內容如下:
[cpp]
obj-y += example.o
為什麼Makefile能只有這一行,大家可以參考LDD3第29頁的介紹,另外也可以參考我的博客文章LDD3源碼分析之hello.c與Makefile模板。
下面我們需要通知內核我們增加了一個新的驅動程序example,以便在編譯內核時會對example進行編譯。通知內核的方法是修改內核drivers目錄下的Makefile,在該文件的最後增加一句:
[cpp]
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程序的步驟。