字符设备驱动详解

来源:互联网 发布:戴尔win10激活windows 编辑:程序博客网 时间:2024/05/16 19:23
[html] view plaincopy
  1. linux字符设备驱动结构  


linux内核中使用cdev结构体来描述字符设备,cdev结构体的定义如下:
[html] view plaincopy
  1. struct cdev  
  2. {  
  3.   struct kobject kobj;       //内嵌的kobject对象  
  4.   struct module *owner;  //所属模块  
  5.   struct file_operations *ops;  //文件操作结构体  
  6.   struct list_head list;    
  7.   dev_t dev;                    //设备号  
  8.   unsigned int count;  
  9. };  

cdev结构体的dev_t成员定义了设备号,为32位,其中高12位为主设备号,低20位为此设备号。file_operations则定义了字符设备驱动提供给虚拟文件系统的接口函数。
linux内核提供了一组函数用于操作cdev结构体:
[html] view plaincopy
  1. void cdev_init(struct cdev *,struct file_operations * );  
  2. struct cdev *cdev_alloc(void);  
  3. void cdev_put(struct cdev *p);  
  4. int cdev_add(struct cdev *,unsigned dev_t);  
  5. void cdev_del(struct cdev * );  

cdev_init()函数用于初始化cdev的成员,并建立cdev和file_operation之间的连接。
cdev_alloc函数用于动态申请一个cdev内存。
cdev_add()函数和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销。对cdev_add()的调用通常发生在字符设备驱动模块加载函数中,而对cdev_del()函数的调用则通常发生在字符设备驱动模块卸载函数中。

在调用cdev_add()函数向系统注册字符设备之前,应首先调用
[html] view plaincopy
  1. int register_chrdev_region(dev_t from,unsigned count,const char * name);  
  2. int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char * name);  
前者用于已知起始设备的设备号的情况,后者用于设备号未知,向系统动态申请未被占用的设备用的情况。
释放设备号用:
[html] view plaincopy
  1. void unregister_chrdev_region(dev_t from, unsigned count);  

file_operation结构体的成员函数是字符设备驱动程序设计的主体内容。


linux字符设备驱动的组成

1.字符设备驱动模块加载与卸载函数(以rtc为例)
[html] view plaincopy
  1. module_init(s3c_rtc_init);  
  2. static int __init s3c_rtc_init(void)  
  3. {  
  4.     printk(banner);  
  5.     return platform_driver_register(&s3c2410_rtc_driver);  
  6. }  
  7. module_exit(s3c_rtc_exit);  
  8. static void __exit s3c_rtc_exit(void)  
  9. {  
  10.     platform_driver_unregister(&s3c2410_rtc_driver);  
  11. }  
2.字符设备驱动的file_operation结构体中成员函数
[html] view plaincopy
  1. 1  struct file_operations   
  2. 2  {   
  3. 3    struct module *owner;   
  4. 4      // 拥有该结构的模块的指针,一般为THIS_MODULES   
  5. 5    loff_t(*llseek)(struct file *, loff_t, int);   
  6. 6      // 用来修改文件当前的读写位置    
  7. 7    ssize_t(*read)(struct file *, char _  _user *, size_t, loff_t*);   
  8. 8      // 从设备中同步读取数据   
  9. 9    ssize_t(*aio_read)(struct  kiocb  *,  char  _  _user  *,  size_t,  loff_t);   
  10. 10     // 初始化一个异步的读取操作   
  11. 11   ssize_t(*write)(struct  file  *,  const  char  _  _user  *,  size_t,   
  12. loff_t*);   
  13. 12     // 向设备发送数据   
  14. 13   ssize_t(*aio_write)(struct kiocb *, const char _  _user *, size_t,   
  15. loff_t);   
  16. 14     // 初始化一个异步的写入操作   
  17. 15   int(*readdir)(struct file *, void *, filldir_t);   
  18. 16     // 仅用于读取目录,对于设备文件,该字段为 NULL   
  19. 17   unsigned int(*poll)(struct file *, struct poll_table_struct*);   
  20. 18     // 轮询函数,判断目前是否可以进行非阻塞的读取或写入   
  21. 19   int(*ioctl)(struct  inode  *, struct  file *, unsigned  int, unsigned   
  22. long);   
  23. 20     // 执行设备I/O控制命令   
  24. 21   long(*unlocked_ioctl)(struct  file  *,  unsigned  int,  unsigned  long);   
  25. 22     // 不使用BLK文件系统,将使用此种函数指针代替ioctl   
  26. 23   long(*compat_ioctl)(struct file *, unsigned int, unsigned long);   
  27. 24     // 在64位系统上,32位的ioctl调用将使用此函数指针代替   
  28. 25   int(*mmap)(struct file *, struct vm_area_struct*);   
  29. 26     // 用于请求将设备内存映射到进程地址空间   
  30. 27   int(*open)(struct inode *, struct file*);   
  31. 28     // 打开   
  32. 29   int(*flush)(struct file*);   
  33. 30   int(*release)(struct inode *, struct file*);   
  34. 31     // 关闭   
  35. 32   int(*synch)(struct file *, struct dentry *, int datasync);   
  36. 33     // 刷新待处理的数据   
  37. 34   int(*aio_fsync)(struct kiocb *, int datasync);   
  38. 35     // 异步fsync   
  39. 36   int(*fasync)(int, struct file *, int);   
  40. 37     // 通知设备FASYNC标志发生变化   
  41. 38   int(*lock)(struct file *, int, struct file_lock*);   
  42. 39   ssize_t(*readv)(struct  file  *,  const  struct  iovec  *,  unsigned  long,   
  43. loff_t*);   
  44. 40   ssize_t(*writev)(struct  file  *,  const  struct  iovec  *,  unsigned  long,  
  45. loff_t*);   
  46. 41     // readv和writev:分散/聚集型的读写操作   
  47. 42   ssize_t(*sendfile)(struct  file  *,  loff_t  *,  size_t,  read_actor_t,   
  48. void*);   
  49. 43     // 通常为NULL   
  50. 44   ssize_t(*sendpage)(struct file *, struct page *, int, size_t,   
  51. loff_t *, int);   
  52. 45     // 通常为NULL   
  53. 46   unsigned long(*get_unmapped_area)(struct file *,unsigned long,   
  54. unsigned long,   
  55. 47     unsigned long, unsigned long);   
  56. 48     // 在进程地址空间找到一个将底层设备中的内存段映射的位置   
  57. 49   int(*check_flags)(int);   
  58. 50     // 允许模块检查传递给fcntl(F_SETEL...)调用的标志   
  59. 51   int(*dir_notify)(struct file *filp, unsigned long arg);   
  60. 52     // 仅对文件系统有效,驱动程序不必实现   
  61. 53   int(*flock)(struct file *, int, struct file_lock*);    
  62. 54 };   

字符设备驱动的结构如下:

设备驱动程序编写流程
设备驱动程序可以使用模块的方式加载的方式加载到内核中去,驱动开发时时没有main()函数,模块在调用insmod命令时被加载,此时的入口点式init_module()函数,通常在该函数中完成设备的注册。同样,模块在调用rmmod命令时被卸载,此时的入口点是cleanup_module()函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如open()、read()、write()等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作。

设备功能是由file_operation()函数定义的。

proc文件系统
/proc文件系统是一个伪文件系统,它是一种内核和内核模块用来向进程发送信息的机制。这个伪文件系统让用户可以和内核内部数据结构进行交互,获取有关系统和进程的有用信息,在运行时通过改变内核参数来改变设置。与其他文件系统不同,/proc存在于内存之中而不是在硬盘之中。/proc文件系统体现了内核及进程运行的内容,在加载模块成功后,读者可以通过查看/proc/device文件获得相关设备的主设备号


通常情况下,字符驱动程序主要还是根据linux中已有的模板进行更改。
0 0
原创粉丝点击