Firefly开源社区

打印 上一主题 下一主题

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

10

积分

0

威望

0

贡献

技术小白

积分
10

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

发表于 2019-3-13 12:32:11      浏览:5569 | 回复:0        打印      只看该作者   [复制链接] 楼主
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 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>  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}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
版权声明:本文为博主原创文章,转载请附上博文链接!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

友情链接 : 爱板网 电子发烧友论坛 云汉电子社区 粤ICP备14022046号-2
快速回复 返回顶部 返回列表