Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android架構分析之Android驅動程序開發

Android架構分析之Android驅動程序開發

編輯:關於Android編程

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程序的步驟。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved