Android架构分析之Android驱动程序开发

作者:刘昊昱 

博客: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 <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程序的步骤。

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值