收藏 分享(赏)

在Ubuntu上为Android系统编写Linux内核驱动程序.doc

上传人:weiwoduzun 文档编号:1822999 上传时间:2018-08-27 格式:DOC 页数:13 大小:47.75KB
下载 相关 举报
在Ubuntu上为Android系统编写Linux内核驱动程序.doc_第1页
第1页 / 共13页
在Ubuntu上为Android系统编写Linux内核驱动程序.doc_第2页
第2页 / 共13页
在Ubuntu上为Android系统编写Linux内核驱动程序.doc_第3页
第3页 / 共13页
在Ubuntu上为Android系统编写Linux内核驱动程序.doc_第4页
第4页 / 共13页
在Ubuntu上为Android系统编写Linux内核驱动程序.doc_第5页
第5页 / 共13页
点击查看更多>>
资源描述

1、 在智能手机时代,每个品牌的手机都有自己的个性特点。正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非 iphone 莫属了。据统计,截止 2011 年 5 月,AppStore 的应用软件数量达 381062 个,位居第一,而 Android Market 的应用软件数量达 294738,紧随 AppStore 后面,并有望在 8 月份越过 AppStore。随着 Android 系统逐步扩大市场占有率,终端设备的多样性亟需更多的移动开发人员的参与。据业内统计,Android 研发人才缺口至少 30万。目前,对 Android 人才需求一类是偏向硬件驱动的 And

2、roid 人才需求,一类是偏向软件应用的 Android 人才需求。总的来说,对有志于从事 Android 硬件驱动的开发工程师来说,现在是一个大展拳脚的机会。那么,就让我们一起来看看如何为Android 系统编写内核驱动程序吧。这里,我们不会为真实的硬件设备编写内核驱动程序。为了方便描述为Android 系统编写内核驱动程序的过程,我们使用一个虚拟的硬件设备,这个设备只有一个 4 字节的寄存器,它可读可写。想起我们第一次学习程序语言时,都喜欢用“Hello, World”作为例子,这里,我们就把这个虚拟的设备命名为“hello”,而这个内核驱动程序也命名为 hello 驱动程序。其实,And

3、roid 内核驱动程序和一般 Linux内核驱动程序的编写方法是一样的,都是以 Linux 模块的形式实现的,具体可参考前面 Android 学习启动篇一文中提到的 Linux Device Drivers 一书。不过,这里我们还是从 Android 系统的角度来描述 Android 内核驱动程序的编写和编译过程。一. 参照前面两篇文章 在 Ubuntu 上下载、编译和安装 Android 最新源代码和在 Ubuntu 上下载、编译和安装 Android 最新内核源代码(Linux Kernel)准备好Android 内核驱动程序开发环境。二. 进入到 kernel/common/driver

4、s 目录,新建 hello 目录:USER-NAMEMACHINE-NAME:/Android$ cd kernel/common/driversUSER-NAMEMACHINE-NAME:/Android/kernel/common/drivers$ mkdir hello三. 在 hello 目录中增加 hello.h 文件:view plain1. #ifndef _HELLO_ANDROID_H_ 2. #define _HELLO_ANDROID_H_ 3. 4. #include 5. #include 6. 7. #define HELLO_DEVICE_NODE_NAME “h

5、ello“ 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 这个头文件定义了一些字符串常量宏,在后面我们要用到。此外,还定义了一个字符设备结构体 hello_and

6、roid_dev,这个就是我们虚拟的硬件设备了,val 成员变量就代表设备里面的寄存器,它的类型为 int,sem 成员变量是一个信号量,是用同步访问寄存器 val 的,dev 成员变量是一个内嵌的字符设备,这个 Linux 驱动程序自定义字符设备结构体的标准方法。四.在 hello 目录中增加 hello.c 文件,这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。这里,提供了三种访问设备寄存器的方法,一是通过 proc 文件系统来访问,二是通过传统的设备文件的方法来访问,三是通过 devfs 文件系统来访问。下面分段描述该驱动程序的实现。首先是包含必要

7、的头文件和定义三种访问设备的方法:view plain1. #include 2. #include 3. #include 4. #include 5. #include 6. #include 7. #include 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

8、 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);

9、 23. static ssize_t hello_write(struct file* filp, const char _user *buf, size_tcount, 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

10、, 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 |

11、 S_IWUSR, hello_val_show, hello_val_store);定义传统的设备文件访问方法,主要是定义 hello_open、hello_release、hello_read 和 hello_write 这四个打开、释放、读和写设备文件的方法:view plain1. /*打开设备方法 */ 2. static int hello_open(struct inode* inode, struct file* filp) 3. struct hello_android_dev* dev; 4. 5. /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*

12、/ 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

13、* 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( 25. 26. 27. if(count val) 28. goto out; 29. 30. 31. /*将寄存器 val 的值拷贝到用户提供的缓冲区*/ 32. if(copy_to_user(buf, 34. goto out; 35. 36.

14、37. err = sizeof(dev-val); 38. 39. out: 40. up( 41. return err; 42. 43. 44. /*写设备的寄存器值 val*/ 45. static ssize_t hello_write(struct file* filp, const char _user *buf, size_tcount, loff_t* f_pos) 46. struct hello_android_dev* dev = filp-private_data; 47. ssize_t err = 0; 48. 49. /*同步访问 */ 50. if(down_

15、interruptible( 52. 53. 54. if(count != sizeof(dev-val) 55. goto out; 56. 57. 58. /*将用户提供的缓冲区的值写到设备寄存器去*/ 59. if(copy_from_user( 61. goto out; 62. 63. 64. err = sizeof(dev-val); 65. 66. out: 67. up( 68. return err; 69. 定义通过 devfs 文件系统访问方法,这里把设备的寄存器 val 看成是设备的一个属性,通过读写这个属性来对设备进行访问,主要是实现 hello_val_show

16、 和 hello_val_store 两个方法,同时定义了两个内部使用的访问 val 值的方法_hello_get_val和_hello_set_val:view plain1. /*读取寄存器 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( 8. 9. 10. val = dev-val; 11. up( 12. 13. retur

17、n 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_interru

18、ptible( 26. 27. 28. dev-val = val; 29. up( 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_

19、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. 定义

20、通过 proc 文件系统访问方法,主要实现了 hello_proc_read 和 hello_proc_write 两个方法,同时定义了在 proc 文件系统创建和删除文件的方法 hello_create_proc 和 hello_remove_proc:view plain1. /*读取设备寄存器 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 =

21、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_

22、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“

23、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.

24、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

25、_PROC_NAME, NULL); 56. 最后,定义模块加载和卸载方法,这里只要是执行设备注册和初始化操作:view plain1. /*初始化设备 */ 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( 9. dev-dev.owner = THIS_

26、MODULE; 10. dev-dev.ops = 11. 12. /*注册字符设备 */ 13. err = cdev_add( 14. if(err) 15. return err; 16. 17. 18. /*初始化信号量和寄存器 val 的值*/ 19. init_MUTEX( 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

27、 = NULL; 30. 31. printk(KERN_ALERT“Initializing hello device./n“); 32. 33. /*动态分配主设备和从设备号*/ 34. err = alloc_chrdev_region( 35. if(err 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;

28、 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(hel

29、lo_class, MKDEV(hello_major, hello_minor); 120. class_destroy(hello_class); 121. 122. 123. /*删除字符设备和释放设备内存*/ 124. if(hello_dev) 125. cdev_del( 126. kfree(hello_dev); 127. 128. 129. /*释放设备号*/ 130. unregister_chrdev_region(devno, 1); 131. 132. 133. MODULE_LICENSE(“GPL“); 134. MODULE_DESCRIPTION(“First

30、 Android Driver“); 135. 136. module_init(hello_init); 137. module_exit(hello_exit); 五.在 hello 目录中新增 Kconfig 和 Makefile 两个文件,其中 Kconfig 是在编译前执行配置命令 make menuconfig 时用到的,而 Makefile 是执行编译命令 make 是用到的:Kconfig 文件的内容config HELLOtristate “First Android Driver“default nhelpThis is the first android driver.M

31、akefile 文件的内容obj-$(CONFIG_HELLO) += hello.o在 Kconfig 文件中,tristate 表示编译选项 HELLO 支持在编译内核时,hello 模块支持以模块、内建和不编译三种编译方法,默认是不编译,因此,在编译内核前,我们还需要执行 make menuconfig 命令来配置编译选项,使得 hello可以以模块或者内建的方法进行编译。在 Makefile 文件中,根据选项 HELLO 的值,执行不同的编译方法。六. 修改 arch/arm/Kconfig 和 drivers/kconfig 两个文件,在 menu “Device Drivers“和

32、 endmenu 之间添加一行:source “drivers/hello/Kconfig“这样,执行 make menuconfig 时,就可以配置 hello 模块的编译选项了。. 七 . 修改 drivers/Makefile 文件,添加一行:obj-$(CONFIG_HELLO) += hello/八 . 配置编译选项:USER-NAMEMACHINE-NAME:/Android/kernel/common$ make menuconfig找到 “Device Drivers“ = “First Android Drivers“选项,设置为 y。注意,如果内核不支持动态加载模块,这里不

33、能选择 m,虽然我们在 Kconfig文件中配置了 HELLO 选项为 tristate。要支持动态加载模块选项,必须要在配置菜单中选择 Enable loadable module support 选项;在支持动态卸载模块选项,必须要在 Enable loadable module support 菜单项中,选择 Module unloading 选项。九 . 编译:USER-NAMEMACHINE-NAME:/Android/kernel/common$ make编译成功后,就可以在 hello 目录下看到 hello.o 文件了,这时候编译出来的zImage 已经包含了 hello 驱动

34、。十 . 参照 在 Ubuntu 上下载、编译和安装 Android 最新内核源代码(Linux Kernel)一文所示,运行新编译的内核文件,验证 hello 驱动程序是否已经正常安装:USER-NAMEMACHINE-NAME:/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &USER-NAMEMACHINE-NAME:/Android$ adb shell进入到 dev 目录,可以看到 hello 设备文件:rootandroid:/ # cd devrootandroid:/dev # ls进入到 proc

35、 目录,可以看到 hello 文件:rootandroid:/ # cd procrootandroid:/proc # ls访问 hello 文件的值:rootandroid:/proc # cat hello0rootandroid:/proc # echo 5 hellorootandroid:/proc # cat hello5进入到 sys/class 目录,可以看到 hello 目录:rootandroid:/ # cd sys/classrootandroid:/sys/class # ls进入到 hello 目录,可以看到 hello 目录:rootandroid:/sys/c

36、lass # cd hellorootandroid:/sys/class/hello # ls进入到下一层 hello 目录,可以看到 val 文件:rootandroid:/sys/class/hello # cd hellorootandroid:/sys/class/hello/hello # ls访问属性文件 val 的值:rootandroid:/sys/class/hello/hello # cat val5rootandroid:/sys/class/hello/hello # echo 0 valrootandroid:/sys/class/hello/hello # cat val0至此,我们的 hello 内核驱动程序就完成了,并且验证一切正常。这里我们采用的是系统提供的方法和驱动程序进行交互,也就是通过 proc 文件系统和 devfs文件系统的方法,下一篇文章中,我们将通过自己编译的 C 语言程序来访问/dev/hello 文件来和 hello 驱动程序交互,敬请期待。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 经营企划

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报