從java層到framework到JNI到HAL到kernel的hello 例子

 
在智能手機時代,每個品牌的手機都有自己的個性特點。正是依靠這種與衆不同的個性來吸引用戶,營造品牌凝聚力和用戶忠城度,典型的代表非iphone莫屬了。據統計,截止2011年5月,AppStore的應用軟件數量達381062個,位居第一,而Android Market的應用軟件數量達294738,緊隨AppStore後面,並有望在8月份越過AppStore。隨着Android系統逐步擴大市場佔有率,終端設備的多樣性亟需更多的移動開發人員的參與。據業內統計,Android研發人才缺口至少30萬。目前,對Android人才需求一類是偏向硬件驅動的Android人才需求,一類是偏向軟件應用的Android人才需求。總的來說,對有志於從事Android硬件驅動的開發工程師來說,現在是一個大展拳腳的機會。那麼,就讓我們一起來看看如何爲Android系統編寫內核驅動程序吧。

        這裏,我們不會爲真實的硬件設備編寫內核驅動程序。爲了方便描述爲Android系統編寫內核驅動程序的過程,我們使用一個虛擬的硬件設備,這個設備只有一個4字節的寄存器,它可讀可寫。想起我們第一次學習程序語言時,都喜歡用“Hello, World”作爲例子,這裏,我們就把這個虛擬的設備命名爲“hello”,而這個內核驅動程序也命名爲hello驅動程序。其實,Android內核驅動程序和一般Linux內核驅動程序的編寫方法是一樣的,都是以Linux模塊的形式實現的,具體可參考前面Android學習啓動篇一文中提到的Linux Device Drivers一書。不過,這裏我們還是從Android系統的角度來描述Android內核驅動程序的編寫和編譯過程。

       一. 參照前面兩篇文章在Ubuntu上下載、編譯和安裝Android最新源代碼在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)準備好Android內核驅動程序開發環境。

       二. 進入到kernel/common/drivers目錄,新建hello目錄:

       USER-NAME@MACHINE-NAME:~/Android$ cd kernel/common/drivers

       USER-NAME@MACHINE-NAME:~/Android/kernel/common/drivers$ mkdir hello

       三. 在hello目錄中增加hello.h文件:

  1. #ifndef _HELLO_ANDROID_H_  
  2. #define _HELLO_ANDROID_H_  
  3.   
  4. #include <linux/cdev.h>  
  5. #include <linux/semaphore.h>  
  6.   
  7. #define HELLO_DEVICE_NODE_NAME  "hello"  
  8. #define HELLO_DEVICE_FILE_NAME  "hello"  
  9. #define HELLO_DEVICE_PROC_NAME  "hello"  
  10. #define HELLO_DEVICE_CLASS_NAME "hello"  
  11.   
  12. struct hello_android_dev {  
  13.     int val;  
  14.     struct semaphore sem;  
  15.     struct cdev dev;  
  16. };  
  17.   
  18. #endif  
#ifndef _HELLO_ANDROID_H_ #define _HELLO_ANDROID_H_ #include <linux/cdev.h> #include <linux/semaphore.h> #define HELLO_DEVICE_NODE_NAME "hello" #define HELLO_DEVICE_FILE_NAME "hello" #define HELLO_DEVICE_PROC_NAME "hello" #define HELLO_DEVICE_CLASS_NAME "hello" struct hello_android_dev { int val; struct semaphore sem; struct cdev dev; }; #endif

   這個頭文件定義了一些字符串常量宏,在後面我們要用到。此外,還定義了一個字符設備結構體hello_android_dev,這個就是我們虛擬的硬件設備了,val成員變量就代表設備裏面的寄存器,它的類型爲int,sem成員變量是一個信號量,是用同步訪問寄存器val的,dev成員變量是一個內嵌的字符設備,這個Linux驅動程序自定義字符設備結構體的標準方法。

   四.在hello目錄中增加hello.c文件,這是驅動程序的實現部分。驅動程序的功能主要是向上層提供訪問設備的寄存器的值,包括讀和寫。這裏,提供了三種訪問設備寄存器的方法,一是通過proc文件系統來訪問,二是通過傳統的設備文件的方法來訪問,三是通過devfs文件系統來訪問。下面分段描述該驅動程序的實現。

   首先是包含必要的頭文件和定義三種訪問設備的方法:

  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 "hello.h"  
  10.   
  11. /*主設備和從設備號變量*/  
  12. static int hello_major = 0;  
  13. static int hello_minor = 0;  
  14.   
  15. /*設備類別和設備變量*/  
  16. static struct class* hello_class = NULL;  
  17. static struct hello_android_dev* hello_dev = NULL;  
  18.   
  19. /*傳統的設備文件操作方法*/  
  20. static int hello_open(struct inode* inode, struct file* filp);  
  21. static int hello_release(struct inode* inode, struct file* filp);  
  22. static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);  
  23. static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);  
  24.   
  25. /*設備文件操作方法表*/  
  26. static struct file_operations hello_fops = {  
  27.     .owner = THIS_MODULE,  
  28.     .open = hello_open,  
  29.     .release = hello_release,  
  30.     .read = hello_read,  
  31.     .write = hello_write,   
  32. };  
  33.   
  34. /*訪問設置屬性方法*/  
  35. static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr,  char* buf);  
  36. static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);  
  37.   
  38. /*定義設備屬性*/  
  39. static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);  
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/device.h> #include <asm/uaccess.h> #include "hello.h" /*主設備和從設備號變量*/ static int hello_major = 0; static int hello_minor = 0; /*設備類別和設備變量*/ static struct class* hello_class = NULL; static struct hello_android_dev* hello_dev = NULL; /*傳統的設備文件操作方法*/ static int hello_open(struct inode* inode, struct file* filp); static int hello_release(struct inode* inode, struct file* filp); static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos); static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); /*設備文件操作方法表*/ static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write, }; /*訪問設置屬性方法*/ static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf); static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); /*定義設備屬性*/ static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);

        定義傳統的設備文件訪問方法,主要是定義hello_open、hello_release、hello_read和hello_write這四個打開、釋放、讀和寫設備文件的方法:

  1. /*打開設備方法*/  
  2. static int hello_open(struct inode* inode, struct file* filp) {  
  3.     struct hello_android_dev* dev;          
  4.       
  5.     /*將自定義設備結構體保存在文件指針的私有數據域中,以便訪問設備時拿來用*/  
  6.     dev = container_of(inode->i_cdev, struct hello_android_dev, dev);  
  7.     filp->private_data = dev;  
  8.       
  9.     return 0;  
  10. }  
  11.   
  12. /*設備文件釋放時調用,空實現*/  
  13. static int hello_release(struct inode* inode, struct file* filp) {  
  14.     return 0;  
  15. }  
  16.   
  17. /*讀取設備的寄存器val的值*/  
  18. static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {  
  19.     ssize_t err = 0;  
  20.     struct hello_android_dev* dev = filp->private_data;          
  21.   
  22.     /*同步訪問*/  
  23.     if(down_interruptible(&(dev->sem))) {  
  24.         return -ERESTARTSYS;  
  25.     }  
  26.   
  27.     if(count < sizeof(dev->val)) {  
  28.         goto out;  
  29.     }          
  30.   
  31.     /*將寄存器val的值拷貝到用戶提供的緩衝區*/  
  32.     if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {  
  33.         err = -EFAULT;  
  34.         goto out;  
  35.     }  
  36.   
  37.     err = sizeof(dev->val);  
  38.   
  39. out:  
  40.     up(&(dev->sem));  
  41.     return err;  
  42. }  
  43.   
  44. /*寫設備的寄存器值val*/  
  45. static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {  
  46.     struct hello_android_dev* dev = filp->private_data;  
  47.     ssize_t err = 0;          
  48.   
  49.     /*同步訪問*/  
  50.     if(down_interruptible(&(dev->sem))) {  
  51.         return -ERESTARTSYS;          
  52.     }          
  53.   
  54.     if(count != sizeof(dev->val)) {  
  55.         goto out;          
  56.     }          
  57.   
  58.     /*將用戶提供的緩衝區的值寫到設備寄存器去*/  
  59.     if(copy_from_user(&(dev->val), buf, count)) {  
  60.         err = -EFAULT;  
  61.         goto out;  
  62.     }  
  63.   
  64.     err = sizeof(dev->val);  
  65.   
  66. out:  
  67.     up(&(dev->sem));  
  68.     return err;  
  69. }  
/*打開設備方法*/ static int hello_open(struct inode* inode, struct file* filp) { struct hello_android_dev* dev; /*將自定義設備結構體保存在文件指針的私有數據域中,以便訪問設備時拿來用*/ dev = container_of(inode->i_cdev, struct hello_android_dev, dev); filp->private_data = dev; return 0; } /*設備文件釋放時調用,空實現*/ static int hello_release(struct inode* inode, struct file* filp) { return 0; } /*讀取設備的寄存器val的值*/ static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) { ssize_t err = 0; struct hello_android_dev* dev = filp->private_data; /*同步訪問*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count < sizeof(dev->val)) { goto out; } /*將寄存器val的值拷貝到用戶提供的緩衝區*/ if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; } /*寫設備的寄存器值val*/ static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) { struct hello_android_dev* dev = filp->private_data; ssize_t err = 0; /*同步訪問*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count != sizeof(dev->val)) { goto out; } /*將用戶提供的緩衝區的值寫到設備寄存器去*/ if(copy_from_user(&(dev->val), buf, count)) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err; }

        定義通過devfs文件系統訪問方法,這裏把設備的寄存器val看成是設備的一個屬性,通過讀寫這個屬性來對設備進行訪問,主要是實現hello_val_show和hello_val_store兩個方法,同時定義了兩個內部使用的訪問val值的方法__hello_get_val和__hello_set_val:

  1. /*讀取寄存器val的值到緩衝區buf中,內部使用*/  
  2. static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) {  
  3.     int val = 0;          
  4.   
  5.     /*同步訪問*/  
  6.     if(down_interruptible(&(dev->sem))) {                  
  7.         return -ERESTARTSYS;          
  8.     }          
  9.   
  10.     val = dev->val;          
  11.     up(&(dev->sem));          
  12.   
  13.     return snprintf(buf, PAGE_SIZE, "%d\n", val);  
  14. }  
  15.   
  16. /*把緩衝區buf的值寫到設備寄存器val中去,內部使用*/  
  17. static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) {  
  18.     int val = 0;          
  19.   
  20.     /*將字符串轉換成數字*/          
  21.     val = simple_strtol(buf, NULL, 10);          
  22.   
  23.     /*同步訪問*/          
  24.     if(down_interruptible(&(dev->sem))) {                  
  25.         return -ERESTARTSYS;          
  26.     }          
  27.   
  28.     dev->val = val;          
  29.     up(&(dev->sem));  
  30.   
  31.     return count;  
  32. }  
  33.   
  34. /*讀取設備屬性val*/  
  35. static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {  
  36.     struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);          
  37.   
  38.     return __hello_get_val(hdev, buf);  
  39. }  
  40.   
  41. /*寫設備屬性val*/  
  42. static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {   
  43.     struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);    
  44.       
  45.     return __hello_set_val(hdev, buf, count);  
  46. }  
/*讀取寄存器val的值到緩衝區buf中,內部使用*/ static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) { int val = 0; /*同步訪問*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } val = dev->val; up(&(dev->sem)); return snprintf(buf, PAGE_SIZE, "%d\n", val); } /*把緩衝區buf的值寫到設備寄存器val中去,內部使用*/ static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) { int val = 0; /*將字符串轉換成數字*/ val = simple_strtol(buf, NULL, 10); /*同步訪問*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } dev->val = val; up(&(dev->sem)); return count; } /*讀取設備屬性val*/ static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) { struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); return __hello_get_val(hdev, buf); } /*寫設備屬性val*/ static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); return __hello_set_val(hdev, buf, count); }

        定義通過proc文件系統訪問方法,主要實現了hello_proc_read和hello_proc_write兩個方法,同時定義了在proc文件系統創建和刪除文件的方法hello_create_proc和hello_remove_proc:

  1. /*讀取設備寄存器val的值,保存在page緩衝區中*/  
  2. static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {  
  3.     if(off > 0) {  
  4.         *eof = 1;  
  5.         return 0;  
  6.     }  
  7.   
  8.     return __hello_get_val(hello_dev, page);  
  9. }  
  10.   
  11. /*把緩衝區的值buff保存到設備寄存器val中去*/  
  12. static ssize_t hello_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {  
  13.     int err = 0;  
  14.     char* page = NULL;  
  15.   
  16.     if(len > PAGE_SIZE) {  
  17.         printk(KERN_ALERT"The buff is too large: %lu.\n", len);  
  18.         return -EFAULT;  
  19.     }  
  20.   
  21.     page = (char*)__get_free_page(GFP_KERNEL);  
  22.     if(!page) {                  
  23.         printk(KERN_ALERT"Failed to alloc page.\n");  
  24.         return -ENOMEM;  
  25.     }          
  26.   
  27.     /*先把用戶提供的緩衝區值拷貝到內核緩衝區中去*/  
  28.     if(copy_from_user(page, buff, len)) {  
  29.         printk(KERN_ALERT"Failed to copy buff from user.\n");                  
  30.         err = -EFAULT;  
  31.         goto out;  
  32.     }  
  33.   
  34.     err = __hello_set_val(hello_dev, page, len);  
  35.   
  36. out:  
  37.     free_page((unsigned long)page);  
  38.     return err;  
  39. }  
  40.   
  41. /*創建/proc/hello文件*/  
  42. static void hello_create_proc(void) {  
  43.     struct proc_dir_entry* entry;  
  44.       
  45.     entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);  
  46.     if(entry) {  
  47.         entry->owner = THIS_MODULE;  
  48.         entry->read_proc = hello_proc_read;  
  49.         entry->write_proc = hello_proc_write;  
  50.     }  
  51. }  
  52.   
  53. /*刪除/proc/hello文件*/  
  54. static void hello_remove_proc(void) {  
  55.     remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);  
  56. }  
/*讀取設備寄存器val的值,保存在page緩衝區中*/ static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) { if(off > 0) { *eof = 1; return 0; } return __hello_get_val(hello_dev, page); } /*把緩衝區的值buff保存到設備寄存器val中去*/ static ssize_t hello_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) { int err = 0; char* page = NULL; if(len > PAGE_SIZE) { printk(KERN_ALERT"The buff is too large: %lu.\n", len); return -EFAULT; } page = (char*)__get_free_page(GFP_KERNEL); if(!page) { printk(KERN_ALERT"Failed to alloc page.\n"); return -ENOMEM; } /*先把用戶提供的緩衝區值拷貝到內核緩衝區中去*/ if(copy_from_user(page, buff, len)) { printk(KERN_ALERT"Failed to copy buff from user.\n"); err = -EFAULT; goto out; } err = __hello_set_val(hello_dev, page, len); out: free_page((unsigned long)page); return err; } /*創建/proc/hello文件*/ static void hello_create_proc(void) { struct proc_dir_entry* entry; entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL); if(entry) { entry->owner = THIS_MODULE; entry->read_proc = hello_proc_read; entry->write_proc = hello_proc_write; } } /*刪除/proc/hello文件*/ static void hello_remove_proc(void) { remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL); }

   最後,定義模塊加載和卸載方法,這裏只要是執行設備註冊和初始化操作:

  1. /*初始化設備*/  
  2. static int  __hello_setup_dev(struct hello_android_dev* dev) {  
  3.     int err;  
  4.     dev_t devno = MKDEV(hello_major, hello_minor);  
  5.   
  6.     memset(dev, 0, sizeof(struct hello_android_dev));  
  7.   
  8.     cdev_init(&(dev->dev), &hello_fops);  
  9.     dev->dev.owner = THIS_MODULE;  
  10.     dev->dev.ops = &hello_fops;          
  11.   
  12.     /*註冊字符設備*/  
  13.     err = cdev_add(&(dev->dev),devno, 1);  
  14.     if(err) {  
  15.         return err;  
  16.     }          
  17.   
  18.     /*初始化信號量和寄存器val的值*/  
  19.     init_MUTEX(&(dev->sem));  
  20.     dev->val = 0;  
  21.   
  22.     return 0;  
  23. }  
  24.   
  25. /*模塊加載方法*/  
  26. static int __init hello_init(void){   
  27.     int err = -1;  
  28.     dev_t dev = 0;  
  29.     struct device* temp = NULL;  
  30.   
  31.     printk(KERN_ALERT"Initializing hello device.\n");          
  32.   
  33.     /*動態分配主設備和從設備號*/  
  34.     err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);  
  35.     if(err < 0) {  
  36.         printk(KERN_ALERT"Failed to alloc char dev region.\n");  
  37.         goto fail;  
  38.     }  
  39.   
  40.     hello_major = MAJOR(dev);  
  41.     hello_minor = MINOR(dev);          
  42.   
  43.     /*分配helo設備結構體變量*/  
  44.     hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);  
  45.     if(!hello_dev) {  
  46.         err = -ENOMEM;  
  47.         printk(KERN_ALERT"Failed to alloc hello_dev.\n");  
  48.         goto unregister;  
  49.     }          
  50.   
  51.     /*初始化設備*/  
  52.     err = __hello_setup_dev(hello_dev);  
  53.     if(err) {  
  54.         printk(KERN_ALERT"Failed to setup dev: %d.\n", err);  
  55.         goto cleanup;  
  56.     }          
  57.   
  58.     /*在/sys/class/目錄下創建設備類別目錄hello*/  
  59.     hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);  
  60.     if(IS_ERR(hello_class)) {  
  61.         err = PTR_ERR(hello_class);  
  62.         printk(KERN_ALERT"Failed to create hello class.\n");  
  63.         goto destroy_cdev;  
  64.     }          
  65.   
  66.     /*在/dev/目錄和/sys/class/hello目錄下分別創建設備文件hello*/  
  67.     temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);  
  68.     if(IS_ERR(temp)) {  
  69.         err = PTR_ERR(temp);  
  70.         printk(KERN_ALERT"Failed to create hello device.");  
  71.         goto destroy_class;  
  72.     }          
  73.   
  74.     /*在/sys/class/hello/hello目錄下創建屬性文件val*/  
  75.     err = device_create_file(temp, &dev_attr_val);  
  76.     if(err < 0) {  
  77.         printk(KERN_ALERT"Failed to create attribute val.");                  
  78.         goto destroy_device;  
  79.     }  
  80.   
  81.     dev_set_drvdata(temp, hello_dev);          
  82.   
  83.     /*創建/proc/hello文件*/  
  84.     hello_create_proc();  
  85.   
  86.     printk(KERN_ALERT"Succedded to initialize hello device.\n");  
  87.     return 0;  
  88.   
  89. destroy_device:  
  90.     device_destroy(hello_class, dev);  
  91.   
  92. destroy_class:  
  93.     class_destroy(hello_class);  
  94.   
  95. destroy_cdev:  
  96.     cdev_del(&(hello_dev->dev));  
  97.   
  98. cleanup:  
  99.     kfree(hello_dev);  
  100.   
  101. unregister:  
  102.     unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);  
  103.   
  104. fail:  
  105.     return err;  
  106. }  
  107.   
  108. /*模塊卸載方法*/  
  109. static void __exit hello_exit(void) {  
  110.     dev_t devno = MKDEV(hello_major, hello_minor);  
  111.   
  112.     printk(KERN_ALERT"Destroy hello device.\n");          
  113.   
  114.     /*刪除/proc/hello文件*/  
  115.     hello_remove_proc();          
  116.   
  117.     /*銷燬設備類別和設備*/  
  118.     if(hello_class) {  
  119.         device_destroy(hello_class, MKDEV(hello_major, hello_minor));  
  120.         class_destroy(hello_class);  
  121.     }          
  122.   
  123.     /*刪除字符設備和釋放設備內存*/  
  124.     if(hello_dev) {  
  125.         cdev_del(&(hello_dev->dev));  
  126.         kfree(hello_dev);  
  127.     }          
  128.   
  129.     /*釋放設備號*/  
  130.     unregister_chrdev_region(devno, 1);  
  131. }  
  132.   
  133. MODULE_LICENSE("GPL");  
  134. MODULE_DESCRIPTION("First Android Driver");  
  135.   
  136. module_init(hello_init);  
  137. module_exit(hello_exit);  
/*初始化設備*/ static int __hello_setup_dev(struct hello_android_dev* dev) { int err; dev_t devno = MKDEV(hello_major, hello_minor); memset(dev, 0, sizeof(struct hello_android_dev)); cdev_init(&(dev->dev), &hello_fops); dev->dev.owner = THIS_MODULE; dev->dev.ops = &hello_fops; /*註冊字符設備*/ err = cdev_add(&(dev->dev),devno, 1); if(err) { return err; } /*初始化信號量和寄存器val的值*/ init_MUTEX(&(dev->sem)); dev->val = 0; return 0; } /*模塊加載方法*/ static int __init hello_init(void){ int err = -1; dev_t dev = 0; struct device* temp = NULL; printk(KERN_ALERT"Initializing hello device.\n"); /*動態分配主設備和從設備號*/ err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME); if(err < 0) { printk(KERN_ALERT"Failed to alloc char dev region.\n"); goto fail; } hello_major = MAJOR(dev); hello_minor = MINOR(dev); /*分配helo設備結構體變量*/ hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL); if(!hello_dev) { err = -ENOMEM; printk(KERN_ALERT"Failed to alloc hello_dev.\n"); goto unregister; } /*初始化設備*/ err = __hello_setup_dev(hello_dev); if(err) { printk(KERN_ALERT"Failed to setup dev: %d.\n", err); goto cleanup; } /*在/sys/class/目錄下創建設備類別目錄hello*/ hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME); if(IS_ERR(hello_class)) { err = PTR_ERR(hello_class); printk(KERN_ALERT"Failed to create hello class.\n"); goto destroy_cdev; } /*在/dev/目錄和/sys/class/hello目錄下分別創建設備文件hello*/ temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create hello device."); goto destroy_class; } /*在/sys/class/hello/hello目錄下創建屬性文件val*/ err = device_create_file(temp, &dev_attr_val); if(err < 0) { printk(KERN_ALERT"Failed to create attribute val."); goto destroy_device; } dev_set_drvdata(temp, hello_dev); /*創建/proc/hello文件*/ hello_create_proc(); printk(KERN_ALERT"Succedded to initialize hello device.\n"); return 0; destroy_device: device_destroy(hello_class, dev); destroy_class: class_destroy(hello_class); destroy_cdev: cdev_del(&(hello_dev->dev)); cleanup: kfree(hello_dev); unregister: unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1); fail: return err; } /*模塊卸載方法*/ static void __exit hello_exit(void) { dev_t devno = MKDEV(hello_major, hello_minor); printk(KERN_ALERT"Destroy hello device.\n"); /*刪除/proc/hello文件*/ hello_remove_proc(); /*銷燬設備類別和設備*/ if(hello_class) { device_destroy(hello_class, MKDEV(hello_major, hello_minor)); class_destroy(hello_class); } /*刪除字符設備和釋放設備內存*/ if(hello_dev) { cdev_del(&(hello_dev->dev)); kfree(hello_dev); } /*釋放設備號*/ unregister_chrdev_region(devno, 1); } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("First Android Driver"); module_init(hello_init); module_exit(hello_exit);

    五.在hello目錄中新增Kconfig和Makefile兩個文件,其中Kconfig是在編譯前執行配置命令make menuconfig時用到的,而Makefile是執行編譯命令make是用到的:

       Kconfig文件的內容

       config HELLO
           tristate "First Android Driver"
           default n
           help
           This is the first android driver.
      Makefile文件的內容
      obj-$(CONFIG_HELLO) += hello.o
      在Kconfig文件中,tristate表示編譯選項HELLO支持在編譯內核時,hello模塊支持以模塊、內建和不編譯三種編譯方法,默認是不編譯,因此,在編譯內核前,我們還需要執行make menuconfig命令來配置編譯選項,使得hello可以以模塊或者內建的方法進行編譯。
      在Makefile文件中,根據選項HELLO的值,執行不同的編譯方法。
      六. 修改arch/arm/Kconfig和drivers/kconfig兩個文件,在menu "Device Drivers"和endmenu之間添加一行:
      source "drivers/hello/Kconfig"
        這樣,執行make menuconfig時,就可以配置hello模塊的編譯選項了。. 
        七. 修改drivers/Makefile文件,添加一行:
        obj-$(CONFIG_HELLO) += hello/
        八. 配置編譯選項:
        USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make menuconfig
        找到"Device Drivers" => "First Android Drivers"選項,設置爲y。
        注意,如果內核不支持動態加載模塊,這裏不能選擇m,雖然我們在Kconfig文件中配置了HELLO選項爲tristate。要支持動態加載模塊選項,必須要在配置菜單中選擇Enable loadable module support選項;在支持動態卸載模塊選項,必須要在Enable loadable module support菜單項中,選擇Module unloading選項。
        九. 編譯:
        USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make
        編譯成功後,就可以在hello目錄下看到hello.o文件了,這時候編譯出來的zImage已經包含了hello驅動。
        十. 參照在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)一文所示,運行新編譯的內核文件,驗證hello驅動程序是否已經正常安裝:
        USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &
        USER-NAME@MACHINE-NAME:~/Android$ adb shell
        進入到dev目錄,可以看到hello設備文件:
        root@android:/ # cd dev
        root@android:/dev # ls
        進入到proc目錄,可以看到hello文件:
        root@android:/ # cd proc
        root@android:/proc # ls
        訪問hello文件的值:
        root@android:/proc # cat hello
        0
        root@android:/proc # echo '5' > hello
        root@android:/proc # cat hello
        5
        進入到sys/class目錄,可以看到hello目錄:
        root@android:/ # cd sys/class
        root@android:/sys/class # ls
        進入到hello目錄,可以看到hello目錄:
        root@android:/sys/class # cd hello
        root@android:/sys/class/hello # ls
        進入到下一層hello目錄,可以看到val文件:
        root@android:/sys/class/hello # cd hello
        root@android:/sys/class/hello/hello # ls
        訪問屬性文件val的值:
        root@android:/sys/class/hello/hello # cat val
        5
        root@android:/sys/class/hello/hello # echo '0'  > val
        root@android:/sys/class/hello/hello # cat val
        0
        至此,我們的hello內核驅動程序就完成了,並且驗證一切正常。這裏我們採用的是系統提供的方法和驅動程序進行交互,也就是通過proc文件系統和devfs文件系統的方法,下一篇文章中,我們將通過自己編譯的C語言程序來訪問/dev/hello文件來和hello驅動程序交互,敬請期待。
 
-------------------------------分割線-------------------------------------------------
 在前一篇文章中,我們介紹瞭如何在Ubuntu上爲Android系統編寫Linux內核驅動程序。在這個名爲hello的Linux內核驅動程序中,創建三個不同的文件節點來供用戶空間訪問,分別是傳統的設備文件/dev/hello、proc系統文件/proc/hello和devfs系統屬性文件/sys/class/hello/hello/val。進一步,還通過cat命令來直接訪問/proc/hello和/sys/class/hello/hello/val文件來,以驗證驅動程序的正確性。在這一篇文章裏,我們將通過自己編寫的C可執行程序來訪問設備文件/dev/hello。可能讀者會覺得奇怪,怎麼能在Android系統中用C語言來編寫應用程序呢?Android系統上的應用程序不都是Java應用程序嗎?其實是可以的,讀者不妨用adb shell命令連上Android模擬器,在/system/bin目錄下可以看到很多C可執行程序,如cat命令。今天,我們就來學習一下怎麼在Android系統中添加用C語言編寫的可執行程序吧。

      一. 參照在Ubuntu上爲Android系統編寫Linux內核驅動程序一文,準備好Linux驅動程序。使用Android模擬器加載包含這個Linux驅動程序的內核文件,並且使用adb shell命令連接上模擬,驗證在/dev目錄中存在設備文件hello。

      二. 進入到Android源代碼工程的external目錄,創建hello目錄:

      USER-NAME@MACHINE-NAME:~/Android$ cd external

      USER-NAME@MACHINE-NAME:~/Android/external$ mkdir hello

      三. 在hello目錄中新建hello.c文件:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <fcntl.h>  
  4. #define DEVICE_NAME "/dev/hello"  
  5. int main(int argc, char** argv)  
  6. {  
  7.     int fd = -1;  
  8.     int val = 0;  
  9.     fd = open(DEVICE_NAME, O_RDWR);  
  10.     if(fd == -1) {  
  11.         printf("Failed to open device %s.\n", DEVICE_NAME);  
  12.         return -1;  
  13.     }  
  14.       
  15.     printf("Read original value:\n");  
  16.     read(fd, &val, sizeof(val));  
  17.     printf("%d.\n\n", val);  
  18.     val = 5;  
  19.     printf("Write value %d to %s.\n\n", val, DEVICE_NAME);  
  20.         write(fd, &val, sizeof(val));  
  21.       
  22.     printf("Read the value again:\n");  
  23.         read(fd, &val, sizeof(val));  
  24.         printf("%d.\n\n", val);  
  25.     close(fd);  
  26.     return 0;  
  27. }  
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #define DEVICE_NAME "/dev/hello" int main(int argc, char** argv) { int fd = -1; int val = 0; fd = open(DEVICE_NAME, O_RDWR); if(fd == -1) { printf("Failed to open device %s.\n", DEVICE_NAME); return -1; } printf("Read original value:\n"); read(fd, &val, sizeof(val)); printf("%d.\n\n", val); val = 5; printf("Write value %d to %s.\n\n", val, DEVICE_NAME); write(fd, &val, sizeof(val)); printf("Read the value again:\n"); read(fd, &val, sizeof(val)); printf("%d.\n\n", val); close(fd); return 0; }

      這個程序的作用中,打開/dev/hello文件,然後先讀出/dev/hello文件中的值,接着寫入值5到/dev/hello中去,最後再次讀出/dev/hello文件中的值,看看是否是我們剛纔寫入的值5。從/dev/hello文件讀寫的值實際上就是我們虛擬的硬件的寄存器val的值。

      四. 在hello目錄中新建Android.mk文件:

      LOCAL_PATH := $(call my-dir)

      include $(CLEAR_VARS)

      LOCAL_MODULE_TAGS := optional

      LOCAL_MODULE := hello

      LOCAL_SRC_FILES := $(call all-subdir-c-files)

      include $(BUILD_EXECUTABLE)

      注意,BUILD_EXECUTABLE表示我們要編譯的是可執行程序。 

      五. 參照如何單獨編譯Android源代碼中的模塊一文,使用mmm命令進行編譯:

      USER-NAME@MACHINE-NAME:~/Android$ mmm ./external/hello

      編譯成功後,就可以在out/target/product/gerneric/system/bin目錄下,看到可執行文件hello了。

      六. 重新打包Android系統文件system.img:

      USER-NAME@MACHINE-NAME:~/Android$ make snod

      這樣,重新打包後的system.img文件就包含剛纔編譯好的hello可執行文件了。

      七. 運行模擬器,使用/system/bin/hello可執行程序來訪問Linux內核驅動程序:

      USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &

      USER-NAME@MACHINE-NAME:~/Android$ adb shell

      root@android:/ # cd system/bin

      root@android:/system/bin # ./hello

      Read the original value:

      0.

      Write value 5 to /dev/hello.

      Read the value again:

      5.

      看到這個結果,就說我們編寫的C可執行程序可以訪問我們編寫的Linux內核驅動程序了。

      介紹完了如何使用C語言編寫的可執行程序來訪問我們的Linux內核驅動程序,讀者可能會問,能不能在Android的Application Frameworks提供Java接口來訪問Linux內核驅動程序呢?可以的,接下來的幾篇文章中,我們將介紹如何在Android的Application Frameworks中,增加Java接口來訪問Linux內核驅動程序,敬請期待

----------------------------------分割線-----------------------------------------------------

Android硬件抽象層(HAL)概要介紹和學習計劃一文中,我們簡要介紹了在Android系統爲爲硬件編寫驅動程序的方法。簡單來說,硬件驅動程序一方面分佈在Linux內核中,另一方面分佈在用戶空間的硬件抽象層中。接着,在Ubuntu上爲Android系統編寫Linux內核驅動程序一文中舉例子說明了如何在Linux內核編寫驅動程序。在這一篇文章中,我們將繼續介紹Android系統硬件驅動程序的另一方面實現,即如何在硬件抽象層中增加硬件模塊來和內核驅動程序交互。在這篇文章中,我們還將學習到如何在Android系統創建設備文件時用類似Linux的udev規則修改設備文件模式的方法。

      一. 參照在Ubuntu上爲Android系統編寫Linux內核驅動程序一文所示,準備好示例內核驅動序。完成這個內核驅動程序後,便可以在Android系統中得到三個文件,分別是/dev/hello、/sys/class/hello/hello/val和/proc/hello。在本文中,我們將通過設備文件/dev/hello來連接硬件抽象層模塊和Linux內核驅動程序模塊。

      二. 進入到在hardware/libhardware/include/hardware目錄,新建hello.h文件:

      USER-NAME@MACHINE-NAME:~/Android$ cd hardware/libhardware/include/hardware

      USER-NAME@MACHINE-NAME:~/Android/hardware/libhardware/include/hardware$ vi hello.h

      hello.h文件的內容如下:

      

  1. #ifndef ANDROID_HELLO_INTERFACE_H  
  2. #define ANDROID_HELLO_INTERFACE_H  
  3. #include <hardware/hardware.h>  
  4.   
  5. __BEGIN_DECLS  
  6.   
  7. /*定義模塊ID*/  
  8. #define HELLO_HARDWARE_MODULE_ID "hello"  
  9.   
  10. /*硬件模塊結構體*/  
  11. struct hello_module_t {  
  12.     struct hw_module_t common;  
  13. };  
  14.   
  15. /*硬件接口結構體*/  
  16. struct hello_device_t {  
  17.     struct hw_device_t common;  
  18.     int fd;  
  19.     int (*set_val)(struct hello_device_t* dev, int val);  
  20.     int (*get_val)(struct hello_device_t* dev, int* val);  
  21. };  
  22.   
  23. __END_DECLS  
  24.   
  25. #endif  
#ifndef ANDROID_HELLO_INTERFACE_H #define ANDROID_HELLO_INTERFACE_H #include <hardware/hardware.h> __BEGIN_DECLS /*定義模塊ID*/ #define HELLO_HARDWARE_MODULE_ID "hello" /*硬件模塊結構體*/ struct hello_module_t { struct hw_module_t common; }; /*硬件接口結構體*/ struct hello_device_t { struct hw_device_t common; int fd; int (*set_val)(struct hello_device_t* dev, int val); int (*get_val)(struct hello_device_t* dev, int* val); }; __END_DECLS #endif


 

      這裏按照Android硬件抽象層規範的要求,分別定義模塊ID、模塊結構體以及硬件接口結構體。在硬件接口結構體中,fd表示設備文件描述符,對應我們將要處理的設備文件"/dev/hello",set_val和get_val爲該HAL對上提供的函數接口。

      三. 進入到hardware/libhardware/modules目錄,新建hello目錄,並添加hello.c文件。 hello.c的內容較多,我們分段來看。

      首先是包含相關頭文件和定義相關結構:

      

  1. #define LOG_TAG "HelloStub"  
  2.   
  3. #include <hardware/hardware.h>  
  4. #include <hardware/hello.h>  
  5. #include <fcntl.h>  
  6. #include <errno.h>  
  7. #include <cutils/log.h>  
  8. #include <cutils/atomic.h>  
  9.   
  10. #define DEVICE_NAME "/dev/hello"  
  11. #define MODULE_NAME "Hello"  
  12. #define MODULE_AUTHOR "[email protected]"  
  13.   
  14. /*設備打開和關閉接口*/  
  15. static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);  
  16. static int hello_device_close(struct hw_device_t* device);  
  17.   
  18. /*設備訪問接口*/  
  19. static int hello_set_val(struct hello_device_t* dev, int val);  
  20. static int hello_get_val(struct hello_device_t* dev, int* val);  
  21.   
  22. /*模塊方法表*/  
  23. static struct hw_module_methods_t hello_module_methods = {  
  24.     open: hello_device_open  
  25. };  
  26.   
  27. /*模塊實例變量*/  
  28. struct hello_module_t HAL_MODULE_INFO_SYM = {  
  29.     common: {  
  30.         tag: HARDWARE_MODULE_TAG,  
  31.         version_major: 1,  
  32.         version_minor: 0,  
  33.         id: HELLO_HARDWARE_MODULE_ID,  
  34.         name: MODULE_NAME,  
  35.         author: MODULE_AUTHOR,  
  36.         methods: &hello_module_methods,  
  37.     }  
  38. };  
#define LOG_TAG "HelloStub" #include <hardware/hardware.h> #include <hardware/hello.h> #include <fcntl.h> #include <errno.h> #include <cutils/log.h> #include <cutils/atomic.h> #define DEVICE_NAME "/dev/hello" #define MODULE_NAME "Hello" #define MODULE_AUTHOR "[email protected]" /*設備打開和關閉接口*/ static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device); static int hello_device_close(struct hw_device_t* device); /*設備訪問接口*/ static int hello_set_val(struct hello_device_t* dev, int val); static int hello_get_val(struct hello_device_t* dev, int* val); /*模塊方法表*/ static struct hw_module_methods_t hello_module_methods = { open: hello_device_open }; /*模塊實例變量*/ struct hello_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: HELLO_HARDWARE_MODULE_ID, name: MODULE_NAME, author: MODULE_AUTHOR, methods: &hello_module_methods, } };


 

      這裏,實例變量名必須爲HAL_MODULE_INFO_SYM,tag也必須爲HARDWARE_MODULE_TAG,這是Android硬件抽象層規範規定的。

      定義hello_device_open函數:

      

  1. static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {  
  2.     struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));  
  3.       
  4.     if(!dev) {  
  5.         LOGE("Hello Stub: failed to alloc space");  
  6.         return -EFAULT;  
  7.     }  
  8.   
  9.     memset(dev, 0, sizeof(struct hello_device_t));  
  10.     dev->common.tag = HARDWARE_DEVICE_TAG;  
  11.     dev->common.version = 0;  
  12.     dev->common.module = (hw_module_t*)module;  
  13.     dev->common.close = hello_device_close;  
  14.     dev->set_val = hello_set_val;dev->get_val = hello_get_val;  
  15.   
  16.     if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {  
  17.         LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev);  
  18.         return -EFAULT;  
  19.     }  
  20.   
  21.     *device = &(dev->common);  
  22.     LOGI("Hello Stub: open /dev/hello successfully.");  
  23.   
  24.     return 0;  
  25. }  
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t)); if(!dev) { LOGE("Hello Stub: failed to alloc space"); return -EFAULT; } memset(dev, 0, sizeof(struct hello_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (hw_module_t*)module; dev->common.close = hello_device_close; dev->set_val = hello_set_val;dev->get_val = hello_get_val; if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev); return -EFAULT; } *device = &(dev->common); LOGI("Hello Stub: open /dev/hello successfully."); return 0; }


 

      DEVICE_NAME定義爲"/dev/hello"。由於設備文件是在內核驅動裏面通過device_create創建的,而device_create創建的設備文件默認只有root用戶可讀寫,而hello_device_open一般是由上層APP來調用的,這些APP一般不具有root權限,這時候就導致打開設備文件失敗:

      Hello Stub: failed to open /dev/hello -- Permission denied.
      解決辦法是類似於Linux的udev規則,打開Android源代碼工程目錄下,進入到system/core/rootdir目錄,裏面有一個名爲ueventd.rc文件,往裏面添加一行:
      /dev/hello 0666 root root
      定義hello_device_close、hello_set_val和hello_get_val這三個函數:
      
  1. static int hello_device_close(struct hw_device_t* device) {  
  2.     struct hello_device_t* hello_device = (struct hello_device_t*)device;  
  3.   
  4.     if(hello_device) {  
  5.         close(hello_device->fd);  
  6.         free(hello_device);  
  7.     }  
  8.       
  9.     return 0;  
  10. }  
  11.   
  12. static int hello_set_val(struct hello_device_t* dev, int val) {  
  13.     LOGI("Hello Stub: set value %d to device.", val);  
  14.   
  15.     write(dev->fd, &val, sizeof(val));  
  16.   
  17.     return 0;  
  18. }  
  19.   
  20. static int hello_get_val(struct hello_device_t* dev, int* val) {  
  21.     if(!val) {  
  22.         LOGE("Hello Stub: error val pointer");  
  23.         return -EFAULT;  
  24.     }  
  25.   
  26.     read(dev->fd, val, sizeof(*val));  
  27.   
  28.     LOGI("Hello Stub: get value %d from device", *val);  
  29.   
  30.     return 0;  
  31. }  
static int hello_device_close(struct hw_device_t* device) { struct hello_device_t* hello_device = (struct hello_device_t*)device; if(hello_device) { close(hello_device->fd); free(hello_device); } return 0; } static int hello_set_val(struct hello_device_t* dev, int val) { LOGI("Hello Stub: set value %d to device.", val); write(dev->fd, &val, sizeof(val)); return 0; } static int hello_get_val(struct hello_device_t* dev, int* val) { if(!val) { LOGE("Hello Stub: error val pointer"); return -EFAULT; } read(dev->fd, val, sizeof(*val)); LOGI("Hello Stub: get value %d from device", *val); return 0; }
      四. 繼續在hello目錄下新建Android.mk文件:
      LOCAL_PATH := $(call my-dir)
      include $(CLEAR_VARS)
      LOCAL_MODULE_TAGS := optional
      LOCAL_PRELINK_MODULE := false
      LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
      LOCAL_SHARED_LIBRARIES := liblog
      LOCAL_SRC_FILES := hello.c
      LOCAL_MODULE := hello.default
      include $(BUILD_SHARED_LIBRARY)
      注意,LOCAL_MODULE的定義規則,hello後面跟有default,hello.default能夠保證我們的模塊總能被硬象抽象層加載到。
      五. 編譯:
      USER-NAME@MACHINE-NAME:~/Android$ mmm hardware/libhardware/modules/hello
      編譯成功後,就可以在out/target/product/generic/system/lib/hw目錄下看到hello.default.so文件了。
      六. 重新打包Android系統鏡像system.img:
      USER-NAME@MACHINE-NAME:~/Android$ make snod
      重新打包後,system.img就包含我們定義的硬件抽象層模塊hello.default了。
      雖然我們在Android系統爲我們自己的硬件增加了一個硬件抽象層模塊,但是現在Java應用程序還不能訪問到我們的硬件。我們還必須編寫JNI方法和在Android的Application Frameworks層增加API接口,才能讓上層Application訪問我們的硬件。在接下來的文章中,我們還將完成這一系統過程,使得我們能夠在Java應用程序中訪問我們自己定製的硬件。

-------------------------------------分割線------------------------------------------

在上兩篇文章中,我們介紹瞭如何爲Android系統的硬件編寫驅動程序,包括如何在Linux內核空間實現內核驅動程序和在用戶空間實現硬件抽象層接口。實現這兩者的目的是爲了向更上一層提供硬件訪問接口,即爲Android的Application Frameworks層提供硬件服務。我們知道,Android系統的應用程序是用Java語言編寫的,而硬件驅動程序是用C語言來實現的,那麼,Java接口如何去訪問C接口呢?衆所周知,Java提供了JNI方法調用,同樣,在Android系統中,Java應用程序通過JNI來調用硬件抽象層接口。在這一篇文章中,我們將介紹如何爲Android硬件抽象層接口編寫JNI方法,以便使得上層的Java應用程序能夠使用下層提供的硬件服務。

      一. 參照在Ubuntu上爲Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序一文,準備好硬件抽象層模塊,確保Android系統鏡像文件system.img已經包含hello.default模塊。

      二. 進入到frameworks/base/services/jni目錄,新建com_android_server_HelloService.cpp文件:

      USER-NAME@MACHINE-NAME:~/Android$ cd frameworks/base/services/jni

      USER-NAME@MACHINE-NAME:~/Android/frameworks/base/services/jni$ vi com_android_server_HelloService.cpp

      在com_android_server_HelloService.cpp文件中,實現JNI方法。注意文件的命令方法,com_android_server前綴表示的是包名,表示硬件服務HelloService是放在frameworks/base/services/java目錄下的com/android/server目錄的,即存在一個命令爲com.android.server.HelloService的類。這裏,我們暫時略去HelloService類的描述,在下一篇文章中,我們將回到HelloService類來。簡單地說,HelloService是一個提供Java接口的硬件訪問服務類。

      首先是包含相應的頭文件:

  1. #define LOG_TAG "HelloService"  
  2. #include "jni.h"  
  3. #include "JNIHelp.h"  
  4. #include "android_runtime/AndroidRuntime.h"  
  5. #include <utils/misc.h>  
  6. #include <utils/Log.h>  
  7. #include <hardware/hardware.h>  
  8. #include <hardware/hello.h>  
  9. #include <stdio.h>  
#define LOG_TAG "HelloService" #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> #include <utils/Log.h> #include <hardware/hardware.h> #include <hardware/hello.h> #include <stdio.h>

      接着定義hello_init、hello_getVal和hello_setVal三個JNI方法:

  1. namespace android  
  2. {  
  3.     /*在硬件抽象層中定義的硬件訪問結構體,參考<hardware/hello.h>*/  
  4.         struct hello_device_t* hello_device = NULL;  
  5.     /*通過硬件抽象層定義的硬件訪問接口設置硬件寄存器val的值*/  
  6.         static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {  
  7.         int val = value;  
  8.         LOGI("Hello JNI: set value %d to device.", val);  
  9.         if(!hello_device) {  
  10.             LOGI("Hello JNI: device is not open.");  
  11.             return;  
  12.         }  
  13.           
  14.         hello_device->set_val(hello_device, val);  
  15.     }  
  16.         /*通過硬件抽象層定義的硬件訪問接口讀取硬件寄存器val的值*/  
  17.     static jint hello_getVal(JNIEnv* env, jobject clazz) {  
  18.         int val = 0;  
  19.         if(!hello_device) {  
  20.             LOGI("Hello JNI: device is not open.");  
  21.             return val;  
  22.         }  
  23.         hello_device->get_val(hello_device, &val);  
  24.           
  25.         LOGI("Hello JNI: get value %d from device.", val);  
  26.       
  27.         return val;  
  28.     }  
  29.         /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設備*/  
  30.     static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {  
  31.         return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
  32.     }  
  33.         /*通過硬件模塊ID來加載指定的硬件抽象層模塊並打開硬件*/  
  34.     static jboolean hello_init(JNIEnv* env, jclass clazz) {  
  35.         hello_module_t* module;  
  36.           
  37.         LOGI("Hello JNI: initializing......");  
  38.         if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
  39.             LOGI("Hello JNI: hello Stub found.");  
  40.             if(hello_device_open(&(module->common), &hello_device) == 0) {  
  41.                 LOGI("Hello JNI: hello device is open.");  
  42.                 return 0;  
  43.             }  
  44.             LOGE("Hello JNI: failed to open hello device.");  
  45.             return -1;  
  46.         }  
  47.         LOGE("Hello JNI: failed to get hello stub module.");  
  48.         return -1;        
  49.     }  
  50.         /*JNI方法表*/  
  51.     static const JNINativeMethod method_table[] = {  
  52.         {"init_native""()Z", (void*)hello_init},  
  53.         {"setVal_native""(I)V", (void*)hello_setVal},  
  54.         {"getVal_native""()I", (void*)hello_getVal},  
  55.     };  
  56.         /*註冊JNI方法*/  
  57.     int register_android_server_HelloService(JNIEnv *env) {  
  58.             return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));  
  59.     }  
  60. };  
namespace android { /*在硬件抽象層中定義的硬件訪問結構體,參考<hardware/hello.h>*/ struct hello_device_t* hello_device = NULL; /*通過硬件抽象層定義的硬件訪問接口設置硬件寄存器val的值*/ static void hello_setVal(JNIEnv* env, jobject clazz, jint value) { int val = value; LOGI("Hello JNI: set value %d to device.", val); if(!hello_device) { LOGI("Hello JNI: device is not open."); return; } hello_device->set_val(hello_device, val); } /*通過硬件抽象層定義的硬件訪問接口讀取硬件寄存器val的值*/ static jint hello_getVal(JNIEnv* env, jobject clazz) { int val = 0; if(!hello_device) { LOGI("Hello JNI: device is not open."); return val; } hello_device->get_val(hello_device, &val); LOGI("Hello JNI: get value %d from device.", val); return val; } /*通過硬件抽象層定義的硬件模塊打開接口打開硬件設備*/ static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) { return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device); } /*通過硬件模塊ID來加載指定的硬件抽象層模塊並打開硬件*/ static jboolean hello_init(JNIEnv* env, jclass clazz) { hello_module_t* module; LOGI("Hello JNI: initializing......"); if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) { LOGI("Hello JNI: hello Stub found."); if(hello_device_open(&(module->common), &hello_device) == 0) { LOGI("Hello JNI: hello device is open."); return 0; } LOGE("Hello JNI: failed to open hello device."); return -1; } LOGE("Hello JNI: failed to get hello stub module."); return -1; } /*JNI方法表*/ static const JNINativeMethod method_table[] = { {"init_native", "()Z", (void*)hello_init}, {"setVal_native", "(I)V", (void*)hello_setVal}, {"getVal_native", "()I", (void*)hello_getVal}, }; /*註冊JNI方法*/ int register_android_server_HelloService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table)); } };

      注意,在hello_init函數中,通過Android硬件抽象層提供的hw_get_module方法來加載模塊ID爲HELLO_HARDWARE_MODULE_ID的硬件抽象層模塊,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定義的。Android硬件抽象層會根據HELLO_HARDWARE_MODULE_ID的值在Android系統的/system/lib/hw目錄中找到相應的模塊,然後加載起來,並且返回hw_module_t接口給調用者使用。在jniRegisterNativeMethods函數中,第二個參數的值必須對應HelloService所在的包的路徑,即com.android.server.HelloService。

      三. 修改同目錄下的onload.cpp文件,首先在namespace android增加register_android_server_HelloService函數聲明:

      namespace android {

      ..............................................................................................

      int register_android_server_HelloService(JNIEnv *env);

      };

      在JNI_onLoad增加register_android_server_HelloService函數調用:
      extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
      {
       .................................................................................................
       register_android_server_HelloService(env);
       .................................................................................................
      }
      這樣,在Android系統初始化時,就會自動加載該JNI方法調用表。
      四. 修改同目錄下的Android.mk文件,在LOCAL_SRC_FILES變量中增加一行:
      LOCAL_SRC_FILES:= \
      com_android_server_AlarmManagerService.cpp \
      com_android_server_BatteryService.cpp \
      com_android_server_InputManager.cpp \
      com_android_server_LightsService.cpp \
      com_android_server_PowerManagerService.cpp \
      com_android_server_SystemServer.cpp \
      com_android_server_UsbService.cpp \
      com_android_server_VibratorService.cpp \
      com_android_server_location_GpsLocationProvider.cpp \
      com_android_server_HelloService.cpp /
      onload.cpp
      五. 編譯和重新找億system.img:
      USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base/services/jni
      USER-NAME@MACHINE-NAME:~/Android$ make snod
      這樣,重新打包的system.img鏡像文件就包含我們剛纔編寫的JNI方法了,也就是我們可以通過Android系統的Application Frameworks層提供的硬件服務HelloService來調用這些JNI方法,進而調用低層的硬件抽象層接口去訪問硬件了。前面提到,在這篇文章中,我們暫時忽略了HelloService類的實現,在下一篇文章中,我們將描述如何實現硬件服務HelloService,敬請關注
 
---------------------------------分割線---------------------------------------------------
數字科技日新月異的今天,軟件和硬件的完美結合,造就了智能移動設備的流行。今天大家對iOS和Android系統的趨之若鶩,一定程度上是由於這兩個系統上有着豐富多彩的各種應用軟件。因此,軟件和硬件的關係,在一定程度上可以說,硬件是爲軟件服務的。硬件工程師研發出一款硬件設備,自然少了軟件工程師爲其編寫驅動程序;而驅動程序的最終目的,是爲了使得最上層的應用程序能夠使用這些硬件提供的服務來爲用戶提供軟件功能。對Android系統上的應用軟件來說,就是要在系統的Application Frameworks層爲其提供硬件服務。在前面的幾篇文章中,我們着重介紹了Linux內核層、硬件抽象層和運行時庫層提供的自定義硬件服務接口,這些接口都是通過C或者C++語言來實現的。在這一篇文章中,我們將介紹如何在Android系統的Application Frameworks層提供Java接口的硬件服務。

      一. 參照在Ubuntu爲Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口一文所示,爲硬件抽象層模塊準備好JNI方法調用層。

      二. 在Android系統中,硬件服務一般是運行在一個獨立的進程中爲各種應用程序提供服務。因此,調用這些硬件服務的應用程序與這些硬件服務之間的通信需要通過代理來進行。爲此,我們要先定義好通信接口。進入到frameworks/base/core/java/android/os目錄,新增IHelloService.aidl接口定義文件:

      USER-NAME@MACHINE-NAME:~/Android$ cd frameworks/base/core/java/android/os

      USER-NAME@MACHINE-NAME:~/Android/frameworks/base/core/java/android/os$ vi IHelloService.aidl

      IHelloService.aidl定義了IHelloService接口:

  1. package android.os;  
  2.    
  3. interface IHelloService {  
  4.     void setVal(int val);  
  5.     int getVal();  
  6. }  
package android.os; interface IHelloService { void setVal(int val); int getVal(); }

IHelloService接口主要提供了設備和獲取硬件寄存器val的值的功能,分別通過setVal和getVal兩個函數來實現。

三.返回到frameworks/base目錄,打開Android.mk文件,修改LOCAL_SRC_FILES變量的值,增加IHelloService.aidl源文件:

## READ ME: ########################################################

   ##

   ## When updating this list of aidl files, consider if that aidl is

   ## part of the SDK API. If it is, also add it to the list below that

   ## is preprocessed and distributed with the SDK. This list should

   ## not contain any aidl files for parcelables, but the one below should

   ## if you intend for 3rd parties to be able to send those objects

   ## across process boundaries.

   ##

   ## READ ME: ########################################################

   LOCAL_SRC_FILES += /

   ....................................................................

   core/java/android/os/IVibratorService.aidl /

   core/java/android/os/IHelloService.aidl /

   core/java/android/service/urlrenderer/IUrlRendererService.aidl /

   .....................................................................

    四. 編譯IHelloService.aidl接口:
    USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base

   這樣,就會根據IHelloService.aidl生成相應的IHelloService.Stub接口。

   五.進入到frameworks/base/services/java/com/android/server目錄,新增HelloService.java文件:

  1. package com.android.server;  
  2. import android.content.Context;  
  3. import android.os.IHelloService;  
  4. import android.util.Slog;  
  5. public class HelloService extends IHelloService.Stub {  
  6.     private static final String TAG = "HelloService";  
  7.     HelloService() {  
  8.         init_native();  
  9.     }  
  10.     public void setVal(int val) {  
  11.         setVal_native(val);  
  12.     }     
  13.     public int getVal() {  
  14.         return getVal_native();  
  15.     }  
  16.       
  17.     private static native boolean init_native();  
  18.         private static native void setVal_native(int val);  
  19.     private static native int getVal_native();  
  20. };  
package com.android.server; import android.content.Context; import android.os.IHelloService; import android.util.Slog; public class HelloService extends IHelloService.Stub { private static final String TAG = "HelloService"; HelloService() { init_native(); } public void setVal(int val) { setVal_native(val); } public int getVal() { return getVal_native(); } private static native boolean init_native(); private static native void setVal_native(int val); private static native int getVal_native(); };

   HelloService主要是通過調用JNI方法init_native、setVal_native和getVal_native(見在Ubuntu爲Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口一文)來提供硬件服務。

     六. 修改同目錄的SystemServer.java文件,在ServerThread::run函數中增加加載HelloService的代碼:

     @Override

     public void run() {

     ....................................................................................

            try {

                  Slog.i(TAG, "DiskStats Service");

                  ServiceManager.addService("diskstats", new DiskStatsService(context));

            } catch (Throwable e) {

                  Slog.e(TAG, "Failure starting DiskStats Service", e);

            }

            try {

                  Slog.i(TAG, "Hello Service");

                  ServiceManager.addService("hello", new HelloService());

            } catch (Throwable e) {

                  Slog.e(TAG, "Failure starting Hello Service", e);

            }

     ......................................................................................

     }      

     七. 編譯HelloService和重新打包system.img:

     USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base/services/java

     USER-NAME@MACHINE-NAME:~/Android$ make snod

     這樣,重新打包後的system.img系統鏡像文件就在Application Frameworks層中包含了我們自定義的硬件服務HelloService了,並且會在系統啓動的時候,自動加載HelloService。這時,應用程序就可以通過Java接口來訪問Hello硬件服務了。我們將在下一篇文章中描述如何編寫一個Java應用程序來調用這個HelloService接口來訪問硬件,敬請期待

------------------------------------分割線-----------------------------------------------

我們在Android系統增加硬件服務的目的是爲了讓應用層的APP能夠通過Java接口來訪問硬件服務。那麼, APP如何通過Java接口來訪問Application Frameworks層提供的硬件服務呢?在這一篇文章中,我們將在Android系統的應用層增加一個內置的應用程序,這個內置的應用程序通過ServiceManager接口獲取指定的服務,然後通過這個服務來獲得硬件服務。

        一. 參照在Ubuntu上爲Android系統的Application Frameworks層增加硬件訪問服務一文,在Application Frameworks層定義好自己的硬件服務HelloService,並提供IHelloService接口提供訪問服務。

       二. 爲了方便開發,我們可以在IDE環境下使用Android SDK來開發Android應用程序。開發完成後,再把程序源代碼移植到Android源代碼工程目錄中。使用Eclipse的Android插件ADT創建Android工程很方便,這裏不述,可以參考網上其它資料。工程名稱爲Hello,下面主例出主要文件:

    主程序是src/shy/luo/hello/Hello.java:

  1. package shy.luo.hello;  
  2.   
  3. import shy.luo.hello.R;  
  4. import android.app.Activity;  
  5. import android.os.ServiceManager;  
  6. import android.os.Bundle;  
  7. import android.os.IHelloService;  
  8. import android.os.RemoteException;  
  9. import android.util.Log;  
  10. import android.view.View;  
  11. import android.view.View.OnClickListener;  
  12. import android.widget.Button;  
  13. import android.widget.EditText;  
  14.   
  15. public class Hello extends Activity implements OnClickListener {  
  16.     private final static String LOG_TAG = "shy.luo.renju.Hello";  
  17.       
  18.     private IHelloService helloService = null;  
  19.   
  20.     private EditText valueText = null;  
  21.     private Button readButton = null;  
  22.     private Button writeButton = null;  
  23.     private Button clearButton = null;  
  24.       
  25.     /** Called when the activity is first created. */  
  26.     @Override  
  27.     public void onCreate(Bundle savedInstanceState) {  
  28.         super.onCreate(savedInstanceState);  
  29.         setContentView(R.layout.main);  
  30.   
  31.     helloService = IHelloService.Stub.asInterface(  
  32.         ServiceManager.getService("hello"));  
  33.           
  34.         valueText = (EditText)findViewById(R.id.edit_value);  
  35.         readButton = (Button)findViewById(R.id.button_read);  
  36.         writeButton = (Button)findViewById(R.id.button_write);  
  37.         clearButton = (Button)findViewById(R.id.button_clear);  
  38.   
  39.     readButton.setOnClickListener(this);  
  40.     writeButton.setOnClickListener(this);  
  41.     clearButton.setOnClickListener(this);  
  42.           
  43.         Log.i(LOG_TAG, "Hello Activity Created");  
  44.     }  
  45.       
  46.     @Override  
  47.     public void onClick(View v) {  
  48.         if(v.equals(readButton)) {  
  49.         try {  
  50.                 int val = helloService.getVal();  
  51.                 String text = String.valueOf(val);  
  52.                 valueText.setText(text);  
  53.         } catch (RemoteException e) {  
  54.             Log.e(LOG_TAG, "Remote Exception while reading value from device.");  
  55.         }         
  56.         }  
  57.         else if(v.equals(writeButton)) {  
  58.         try {  
  59.                 String text = valueText.getText().toString();  
  60.                 int val = Integer.parseInt(text);  
  61.             helloService.setVal(val);  
  62.         } catch (RemoteException e) {  
  63.             Log.e(LOG_TAG, "Remote Exception while writing value to device.");  
  64.         }  
  65.         }  
  66.         else if(v.equals(clearButton)) {  
  67.             String text = "";  
  68.             valueText.setText(text);  
  69.         }  
  70.     }  
  71. }  
package shy.luo.hello; import shy.luo.hello.R; import android.app.Activity; import android.os.ServiceManager; import android.os.Bundle; import android.os.IHelloService; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class Hello extends Activity implements OnClickListener { private final static String LOG_TAG = "shy.luo.renju.Hello"; private IHelloService helloService = null; private EditText valueText = null; private Button readButton = null; private Button writeButton = null; private Button clearButton = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); helloService = IHelloService.Stub.asInterface( ServiceManager.getService("hello")); valueText = (EditText)findViewById(R.id.edit_value); readButton = (Button)findViewById(R.id.button_read); writeButton = (Button)findViewById(R.id.button_write); clearButton = (Button)findViewById(R.id.button_clear); readButton.setOnClickListener(this); writeButton.setOnClickListener(this); clearButton.setOnClickListener(this); Log.i(LOG_TAG, "Hello Activity Created"); } @Override public void onClick(View v) { if(v.equals(readButton)) { try { int val = helloService.getVal(); String text = String.valueOf(val); valueText.setText(text); } catch (RemoteException e) { Log.e(LOG_TAG, "Remote Exception while reading value from device."); } } else if(v.equals(writeButton)) { try { String text = valueText.getText().toString(); int val = Integer.parseInt(text); helloService.setVal(val); } catch (RemoteException e) { Log.e(LOG_TAG, "Remote Exception while writing value to device."); } } else if(v.equals(clearButton)) { String text = ""; valueText.setText(text); } } }

    程序通過ServiceManager.getService("hello")來獲得HelloService,接着通過IHelloService.Stub.asInterface函數轉換爲IHelloService接口。其中,服務名字“hello”是系統啓動時加載HelloService時指定的,而IHelloService接口定義在android.os.IHelloService中,具體可以參考在Ubuntu上爲Android系統的Application Frameworks層增加硬件訪問服務一文。這個程序提供了簡單的讀定自定義硬件有寄存器val的值的功能,通過IHelloService.getVal和IHelloService.setVal兩個接口實現。

界面佈局文件res/layout/main.xml:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.        android:orientation="vertical"  
  4.        android:layout_width="fill_parent"  
  5.        android:layout_height="fill_parent">  
  6.        <LinearLayout  
  7.           android:layout_width="fill_parent"  
  8.           android:layout_height="wrap_content"  
  9.           android:orientation="vertical"   
  10.           android:gravity="center">  
  11.           <TextView   
  12.              android:layout_width="wrap_content"  
  13.              android:layout_height="wrap_content"   
  14.              android:text="@string/value">  
  15.           </TextView>  
  16.           <EditText   
  17.              android:layout_width="fill_parent"  
  18.              android:layout_height="wrap_content"   
  19.              android:id="@+id/edit_value"  
  20.              android:hint="@string/hint">  
  21.           </EditText>  
  22.        </LinearLayout>  
  23.        <LinearLayout  
  24.           android:layout_width="fill_parent"  
  25.           android:layout_height="wrap_content"  
  26.           android:orientation="horizontal"   
  27.           android:gravity="center">  
  28.           <Button   
  29.              android:id="@+id/button_read"  
  30.              android:layout_width="wrap_content"  
  31.              android:layout_height="wrap_content"  
  32.              android:text="@string/read">  
  33.           </Button>  
  34.           <Button   
  35.              android:id="@+id/button_write"  
  36.              android:layout_width="wrap_content"  
  37.              android:layout_height="wrap_content"  
  38.              android:text="@string/write">  
  39.           </Button>  
  40.           <Button   
  41.              android:id="@+id/button_clear"  
  42.              android:layout_width="wrap_content"  
  43.              android:layout_height="wrap_content"  
  44.              android:text="@string/clear">  
  45.           </Button>  
  46.        </LinearLayout>  
  47.     </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/value"> </TextView> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/edit_value" android:hint="@string/hint"> </EditText> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center"> <Button android:id="@+id/button_read" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/read"> </Button> <Button android:id="@+id/button_write" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/write"> </Button> <Button android:id="@+id/button_clear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/clear"> </Button> </LinearLayout> </LinearLayout>
字符串文件res/values/strings.xml:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.     <resources>  
  3.        <string name="app_name">Hello</string>  
  4.        <string name="value">Value</string>  
  5.        <string name="hint">Please input a value...</string>  
  6.        <string name="read">Read</string>  
  7.        <string name="write">Write</string>  
  8.        <string name="clear">Clear</string>  
  9.     </resources>  
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Hello</string> <string name="value">Value</string> <string name="hint">Please input a value...</string> <string name="read">Read</string> <string name="write">Write</string> <string name="clear">Clear</string> </resources>
程序描述文件AndroidManifest.xml:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.     <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="shy.luo.hello"  
  4.       android:versionCode="1"  
  5.       android:versionName="1.0">  
  6.       <application android:icon="@drawable/icon" android:label="@string/app_name">  
  7.         <activity android:name=".Hello"  
  8.                   android:label="@string/app_name">  
  9.             <intent-filter>  
  10.                 <action android:name="android.intent.action.MAIN" />  
  11.                 <category android:name="android.intent.category.LAUNCHER" />  
  12.             </intent-filter>  
  13.         </activity>  
  14.       </application>  
  15.     </manifest>   
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="shy.luo.hello" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Hello" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>三. 將Hello目錄拷貝至packages/experimental目錄,新增Android.mk文件:
    USER-NAME@MACHINE-NAME:~/Android/packages/experimental$ vi Android.mk
Android.mk的文件內容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Hello
include $(BUILD_PACKAGE)
四. 編譯:
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Hello
編譯成功後,便可以在out/target/product/generic/system/app目錄下看到Hello.apk文件了。
    五. 重新打包系統鏡像文件system.img:
USER-NAME@MACHINE-NAME:~/Android$ make snod
    重新打包後的system.img文件就內置了Hello.apk文件了。
六. 運行Android模擬器:
USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel kernel/common/arch/arm/boot/zImage &
在Home Screen中可以看到Hello應用程序:

打開Hello應用程序:

點擊Read按鈕,可以從HelloService中讀取硬件寄存器val的值;點擊Clear按鈕,可以清空文本框的值;在文本框中輸入一個數值,再點擊Write按鈕,便可以將這個值寫入到硬件寄存器val中去,可以再次點擊Read按鈕來驗證是否正確寫入了值。
至此,我們就完整地學習了在Android的Linux內核空間添加硬件驅動程序、在Android的硬件抽象層添加硬件接口、在Android的Application Frameworks層提供硬件服務以及在Android的應用層調用硬件服務的整個過程了,希望能爲讀者進入Android系統提供入門幫助。重新學習整個過程,請參考Android硬件抽象層(HAL)概要介紹和學習計劃
 
發佈了8 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章