Linux设备驱动模型

2019-07-13 07:07发布

尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要。 Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。 在正式进入设备驱动模型的学习之前,有必要把documentation/filesystems/sysfs.txt读一遍(不能偷懒)。sysfs.txt主要描述/sys目录的创建及其属性,sys目录描述了设备驱动模型的层次关系,我们可以简略看一下/sys目录,
block:所有块设备 devices:系统所有设备(块设备特殊),对应struct device的层次结构 bus:系统中所有总线类型(指总线类型而不是总线设备,总线设备在devices下),bus的每个子目录都包含     --devices:包含到devices目录中设备的软链接     --drivers:与bus类型匹配的驱动程序 class:系统中设备类型(如声卡、网卡、显卡等) fs:一些文件系统,具体可参考filesystems /fuse.txt中例子 dev:包含2个子目录 --char:字符设备链接,链接到devices目录,以:命名 --block:块设备链接   Linux设备模型学习分为:Linux设备底层模型,描述设备的底层层次实现(kobject);Linux上层容器,包括总线类型(bus_type)、设备(device)和驱动(device_driver)。  

====  Linux设备底层模型 ====

      谨记:像上面看到的一样,设备模型是层次的结构,层次的每一个节点都是通过kobject实现的。在文件上则体现在sysfs文件系统。

kobject结构

内核中存在struct kobject数据结构,每个加载到系统中的kobject都唯一对应/sys或者子目录中的一个文件夹。可以这样说,许多kobject结构就构成设备模型的层次结构。每个kobject对应一个或多个struct attribute描述属性的结构。 点击(此处)折叠或打开
  1. struct kobject {
  2.     const char *name; /* 对应sysfs的目录名 */
  3.     struct list_head entry; /* kobjetct双向链表 */
  4.     struct kobject *parent; /* 指向kset中的kobject,相当于指向父目录 */
  5.     struct kset *kset; /*指向所属的kset */
  6.     struct kobj_type *ktype; /*负责对kobject结构跟踪*/
  7.     struct sysfs_dirent *sd; 
  8.     struct kref kref; /*kobject引用计数*/
  9.     unsigned int state_initialized:1;
  10.     unsigned int state_in_sysfs:1;
  11.     unsigned int state_add_uevent_sent:1;
  12.     unsigned int state_remove_uevent_sent:1;
  13.     unsigned int uevent_suppress:1;
  14. };

kobject结构是组成设备模型的基本结构,最初kobject设计只用来跟踪模块引用计数,现已增加支持, —— sysfs表述:在sysfs中的每个对象都有对应的kobject —— 数据结构关联:通过链接将不同的层次数据关联 —— 热插拔事件处理:kobject子系统将产生的热插拔事件通知用户空间 kobject一般不单独使用,而是嵌入到上层结构(比如struct devicestruct device_driver)当中使用。kobject的创建者需要直接或间接设置的成员有:ktypeksetparentkset我们后面再说,parent设置为NULL时,kobject默认创建到/sys顶层目录下,否则创建到对应的kobject目录中。重点来分析ktype成员的类型, 点击(此处)折叠或打开
  1. #include <kobject.h>
  2. struct kobj_type {
  3.     void (*release)(struct kobject *kobj); /* 释放 */
  4.     const struct sysfs_ops *sysfs_ops; /* 默认属性实现 */
  5.     struct attribute **default_attrs; /* 默认属性 */
  6.     const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
  7.     const void *(*namespace)(struct kobject *kobj);
  8. };

ktype包含了释放设备、默认属性以及属性的实现方法几个重要成员。每个kobject必须有一个release方法,并且kobject在该方法被调用之前必须保持不变(处于稳定状态)。默认属性的结构如下, 点击(此处)折叠或打开
  1. #include <linux/sysfs.h>
  2. struct attribute {
  3.     const char *name; /* 属性名称 */
  4.     mode_t mode; /* 属性保护:只读设为S_IRUGO,可写设为S_IWUSR */
  5. }

kobj_type中的default_attrs为二级结构指针,可以对每个kobject使用多个默认属性,最后一个属性使用NULL填充。struct sysfs_ops结构则如下, 点击(此处)折叠或打开
  1. struct sysfs_ops {
  2.     ssize_t (*show)(struct kobject *, struct attribute *,char *);
  3.     ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
  4. };

show方法用于将传入的指定属性编码后放到char *类型的buffer中,store则执行相反功能:将buffer中的编码信息解码后传递给struct attribute类型变量。两者都是返回实际的属性长度。 一个使用kobject的简单例子如下, 点击(此处)折叠或打开
  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/device.h>
  4. #include <linux/string.h>
  5. #include <linux/sysfs.h>
  6. #include <linux/kernel.h>
  7.  
  8. MODULE_AUTHOR("xhzuoxin");
  9. MODULE_LICENSE("Dual BSD/GPL");
  10.  
  11. void my_obj_release(struct kobject *kobj)
  12. {
  13.     printk("release ok.n");
  14. }
  15.  
  16. ssize_t my_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf)
  17. {
  18.     printk("my_sysfs_show.n");
  19.     printk("attrname:%s.n", attr->name);
  20.     sprintf(buf, "%s", attr->name);
  21.     return strlen(attr->name) + 1;
  22. }
  23.  
  24. ssize_t my_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buf,
  25.        size_t count)
  26. {
  27.     printk("my_sysfs_store.n");
  28.     printk("write:%sn", buf);
  29.  
  30.     return count;
  31. }
  32.  
  33. struct sysfs_ops my_sysfs_ops = {
  34.     .show = my_sysfs_show,
  35.     .store = my_sysfs_store,
  36. };
  37.  
  38. struct attribute my_attrs = {
  39.     .name = "zx_kobj",
  40.     .mode = S_IRWXUGO,
  41. };
  42.  
  43. struct attribute *my_attrs_def[] = {
  44.     &my_attrs,
  45.     NULL,
  46. };
  47. struct kobj_type my_ktype = {
  48.     .release = my_obj_release,
  49.     .sysfs_ops = &my_sysfs_ops,
  50.     .default_attrs = my_attrs_def,
  51. };
  52.  
  53. struct kobject my_kobj ;
  54.  
  55. int __init kobj_test_init(void)
  56. {
  57.     printk("kobj_test init.n");
  58.     kobject_init_and_add(&my_kobj, &my_ktype, NULL, "zx");
  59.  
  60.     return 0;
  61. }
  62.  
  63. void __exit kobj_test_exit(void)
  64. {
  65.     printk("kobj_test exit.n");
  66.     kobject_del(&my_kobj);
  67. }
  68.  
  69. module_init(kobj_test_init);
  70. module_exit(kobj_test_exit);

例子中有两个函数,用于初始化添加和删除kobject结构, 点击(此处)折叠或打开
  1. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
  2.             struct kobject *parent, const char *fmt, ...); /* fmt指定kobject名称 */
  3. void kobject_del(struct kobject *kobj);

加载模块后,在/sys目录下增加了一个叫zx达到目录,zx目录下创建了一个属性文件zx_kobj,使用tree /sys/zx查看。 内核提供了许多与kobject结构相关的函数,如下: 点击(此处)折叠或打开
  1. // kobject初始化函数
  2. void kobject_init(struct kobject * kobj);
  3. // 设置指定kobject的名称
  4. int kobject_set_name(struct kobject *kobj, const char *format, ...);
  5. // 将kobj 对象的引用计数加,同时返回该对象的指针
  6. struct kobject *kobject_get(struct kobject *kobj);
  7. // 将kobj对象的引用计数减,如果引用计数降为,则调用kobject release()释放该kobject对象
  8. void kobject_put(struct kobject * kobj);
  9. // 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引// 用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数
  10. int kobject_add(struct kobject * kobj);
  11. // kobject注册函数,调用kobject init()初始化kobj,再调用kobject_add()完成该内核对象的注册
  12. int kobject_register(struct kobject * kobj);
  13. // 从Linux设备层次(hierarchy)中删除kobj对象
  14. void kobject_del(struct kobject * kobj);
  15. // kobject注销函数. 与kobject register()相反,它首先调用kobject del从设备层次中删除该对象,再调// 用kobject put()减少该对象的引用计数,如果引用计数降为,则释放kobject对象
  16. void kobject_unregister(struct kobject * kobj);

kset结构

我们先看上图,kobject通过kset组织成层次化的结构,kset将一系列相同类型的kobject使用(双向)链表连接起来,可以这样 认为,kset充当链表头作用,kset内部内嵌了一个kobject结构。内核中用kset数据结构表示为: 点击(此处)折叠或打开
  1. #include <linux/kobject.h>
  2. struct kset {
  3.     struct list_head list; /* 用于连接kset中所有kobject的链表头 *