模型框架

221 阅读7分钟

4月「掘金·日新计划」第23天

模型框架

image.png

3.1、概念

3.1.1、什么是设备驱动模型

  1. 设备驱动模型其实是Linux内核为了管理硬件上的设备和对应的驱动制定的一套软件体系
  2. 类(class)、总线(bus)、设备(device)、驱动(driver)、udev(自动创建设备节点和设备类)sysfs等都属于设备驱动模型的范畴
  3. 类(class)、总线(bus)、设备(device)、驱动(driver) 都是 Linux 里面的一个结构体。 每一个结构体变量都能代表一个实例
  4. kobject和对象生命周期 kobject:kernel object 处在最高层的模型,包含所有的模型。 对象生命周期:有一种机制,让对象可以自动 free,自动释放内存空间。
  5. sysfs 可以 cat 、echo
  6. udev 自动创建设备节点和设备类。(class_creat 、 device_creat)

3.1.2、为什么需要设备驱动模型

  1. 设备驱动模型负责统一实现和维护一些特性,诸如:电源管理热插拔对象生命周期用户空间和驱动空间的交互、等基础设施
  2. 设备驱动模型目的是简化驱动程序编写,但是客观上设备驱动模型本身设计和实现很复杂。
  3. 代码重用。将对象抽象为总线、驱动、设备三种,各司其职。同一总线的多个驱动使用相同的总线对象。同一驱动可以关联驱动多个设备。
  4. 通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。同时sysfs文件系统还提供了方便的同用户控件交互的接口。

3.1.3、底层架构

  1. kobject结构体 位置:Linux/kobject.h

  2. 这个结构体相当于一个总基类,被其他结构体所继承。 所以其他结构体当中包含这个基类

    struct kobject {
        const char      *name; //kobject的名字,且作为一个目录的名字
        struct list_head    entry; //连接下一个kobject结构
        struct kobject      *parent; //指向父亲kobject结构体,如果父设备存在
        struct kset     *kset;  //指向kset集合
        struct kobj_type    *ktype;  //指向kobject的属性描述符
        struct sysfs_dirent *sd;     //对应sysfs的文件目录
        struct kref     kref;   //kobject的引用计数
        unsigned int state_initialized:1; //表示该kobject是否初始化
        unsigned int state_in_sysfs:1;   //表示是否加入sysfs中
        unsigned int state_add_uevent_sent:1;
        unsigned int state_remove_uevent_sent:1;
        unsigned int uevent_suppress:1;
    };
    
  3. 各对象最基本单元,提供:每个对象,都会包含一个kobject结构体类

    1. 对象引用计数,自动释放功能
    2. 维护对象链表
    3. 对象上锁
    4. 用户空间表示
  4. 每一个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录(class目录),而不是文件。

  5. kobj_type结构体

    struct kobj_type {
        void (*release)(struct kobject *kobj);//释放kobject和其占用的函数
        const struct sysfs_ops *sysfs_ops;  //操作一个属性数组的方法
        struct attribute **default_attrs;  //属性数组的方法
        const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
        const void *(*namespace)(struct kobject *kobj);
    };
    
    1. 每一个kobject都需要绑定一个ktype来提供相应功能

    2. sysfs_ops,提供该对象在sysfs中的操作方法(show和store)

      struct sysfs_ops
      {
          ssize t (*show) (struct kobject *, struct attribute *, char *);/*读属性操作函数*/
          ssize t (*store) (struct kobject *,struct attribute *,const char *buf, size_t count);/*写属性操作函数*/
      }
      
    3. attribute,提供在sysfs中以文件形式存在的属性,其实就是应用接口

      struct attribute {
          const char      *name;  //属性的名字
          struct module       *owner;//指向用于该属性的模块,已经不常使用
          mode_t          mode;  //属性读写权限
      };
      
  6. kset结构体

    struct kset {
        struct list_head list;  //连接链表
        spinlock_t list_lock;  //链表的自旋锁
        struct kobject kobj;  //内嵌的kobject结构体,说明kset本身也是一个目录
        const struct kset_uevent_ops *uevent_ops;  //热插拔事件
    };
    
    1. 同样也是被每一个kobject都需要绑定的结构体,但是 kset 当中包含kobject
    2. kset的主要作用是做顶层kobject的容器类 用来装载 kobject
    3. kset的主要目的是将各个kobject(代表着各个对象)组织出目录层次架构
    4. 可以认为kset就是为了在sysfs中弄出目录,从而让设备驱动模型中的多个对象能够有层次有逻辑性的组织在一起

3.2、总线设备组织方式

3.2.1、总线

  1. 总线,管理设备和驱动2条分支

  2. sys/bus/里面device和driver

  3. bus_type结构体

    struct bus_type {
        const char      *name;   //总线类型名
        struct bus_attribute    *bus_attrs;  //总线属性和导出到sysfs中的方法
        struct device_attribute *dev_attrs;  //设备属性和导出到sysfs中的方法
        struct driver_attribute *drv_attrs;  //驱动程序属性和导出到sysfs中的方法
        //匹配函数,检验参数2中的驱动是否支持参数1中的设备
        int (*match)(struct device *dev, struct device_driver *drv);
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        int (*probe)(struct device *dev);  //探测设备
        int (*remove)(struct device *dev); //移除设备
        void (*shutdown)(struct device *dev); //关闭函数
        int (*suspend)(struct device *dev, pm_message_t state);//改变设备供电状态,使其节能
        int (*resume)(struct device *dev);  //恢复供电状态,使其正常工作
        const struct dev_pm_ops *pm;  //关于电源管理的操作符
        struct bus_type_private *p;  //总线的私有数据
    };
    
    1. 关键是 match 函数和 uevent 函数

      1. Linux系统内核使用 bus_type 结构体表示总线,例子:内核中的platform总线以及pci总线
      struct bus_type platform_bus_type = {
          .name       = "platform",
          .dev_groups = platform_dev_groups,
          .match      = platform_match,
          .uevent     = platform_uevent,
          .pm     = &platform_dev_pm_ops,
      };
      ​
      struct bus_type pci_bus_type = {
          .name       = "pci",
          .match      = pci_bus_match,
          .uevent     = pci_uevent,
          .probe      = pci_device_probe,
          .remove     = pci_device_remove,
          .shutdown   = pci_device_shutdown,
          .dev_groups = pci_dev_groups,
          .bus_groups = pci_bus_groups,
          .drv_groups = pci_drv_groups,
          .pm     = PCI_PM_OPS_PTR,
      };
      

3.2.2、设备

  1. struct device 是硬件设备在内核驱动框架中的抽象

  2. device_register 用于向内核驱动框架注册一个设备

  3. 通常device不会单独使用,而是被包含在一个具体设备结构体中,如 struct usb_device

    是一个基类,会被其他类所继承。

  4. 设备驱动模型中的总线式设计中会包含设备和驱动两个

    platform总线:platform_deviceplatform_driver

    struct device {
        struct klist_klist children;/*连接子设备的链表*/
        struct device *parent;/*指向父设备的指针*/
        struct kobject kobj;/*内嵌的kobject结构体*/
        char bus_id[BUS ID SIZE];/*连接到总线上的位置*/ 
        unsigned uevent suppress:1;/*是否支持热插拔事件*/
        const char init_name;/*设备的初始化名字*/
        struct device_type *type;/*设备相关的特殊处理函数*/
        struct bus_type *bus;/*指向连接的总线指针*/
        struct device_driver *driver;/*指向该设备的驱动程序*/
        void *driver data;/*指向驱动程序私有数据的指针*/
        struct dev_pm info power;/*电源管理信息*/ 
        dev t deyt;/*设备号*/
        struct class *class;/*指向设备所属类*/ 
        struct attribute_group **groups;/*设备的组属性*/ 
        void (*release) (struct device *dev);/*释放设备描述符的回调函数*/
    }
    
  5. 设备属性

    struct device_attribute {
        struct attribute    attr;
        ssize_t (*show)(struct device *dev, struct device_attribute *attr,
                char *buf);
        ssize_t (*store)(struct device *dev, struct device_attribute *attr,
                 const char *buf, size_t count);
    };
    

3.2.3、驱动

  1. struct device_driver 是驱动程序在内核驱动框架中的抽象

    struct device_driver {
        const char      *name;//设备驱动程序的名字
        struct bus_type     *bus;//指向驱动属于的总线
     
        struct module       *owner;//设备驱动自身模块
        const char      *mod_name;  /* used for built-in modules */
     
        bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
     
    #if defined(CONFIG_OF)
        const struct of_device_id   *of_match_table;
    #endif
     
        int (*probe) (struct device *dev);//探测设备的方法,并检测设备驱动可以控制哪些设备
        int (*remove) (struct device *dev);//移除设备调用的方法
        void (*shutdown) (struct device *dev);//关闭设备的方法
        int (*suspend) (struct device *dev, pm_message_t state);//设备处于低功耗的方法
        int (*resume) (struct device *dev);//恢复正常的方法
        const struct attribute_group **groups;//属性组
     
        const struct dev_pm_ops *pm;//电源管理
     
        struct driver_private *p;//设备驱动私有数据
    };
    
  2. name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据

  3. probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理

3.2.4、类

  1. 另一种管理设备和驱动的方式,和总线一样 一个是/class下 一个是/bus下 管理的文件是同一个

  2. 相关结构体:struct classstruct class_device

  3. udev 的使用离不开class

    struct class {
        const char      *name; //类名称
        struct module       *owner;
     
        struct class_attribute      *class_attrs; //class给自己添加的属性
        const struct attribute_group    **dev_groups; //class给所包含的设备添加的属性
        struct kobject          *dev_kobj;
     
        int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
        char *(*devnode)(struct device *dev, umode_t *mode);
     
        void (*class_release)(struct class *class);
        void (*dev_release)(struct device *dev);
     
        int (*suspend)(struct device *dev, pm_message_t state); //设备休眠时调用
        int (*resume)(struct device *dev);
     
        const struct kobj_ns_type_operations *ns_type;
        const void *(*namespace)(struct device *dev);
     
        const struct dev_pm_ops *pm; //电源管理
     
        struct subsys_private *p;
    };
    
  4. class的真正意义在于作为同属于一个class的多个设备的容器