zoukankan      html  css  js  c++  java
  • Linux 设备驱动模型

    Linux系统将设备和驱动归一到设备驱动模型中了来管理

     设备驱动程序功能:

        1,对硬件设备初始化和释放

        2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口

        3,读取应用程序传递给设备文件的数据或回送应用程序请求的数据

        4,检测或处理设备出现的错误

    设备驱动模型提供了硬件的抽象包括:

    1,电源管理

      其实,电源管理就是一些设备不工作的时候,让它歇一会,休眠一会(最低消耗),达到省电的目的

      它的一个重要的功能是:

       省电模式下,使系统中的设备以一定的先后顺序挂起

       在全速工作模式下,使系统的设备以一定的先后顺序恢复运行

          就是这个意思,一条总线上有n个设备,只用当n个设备都挂起的时候,那个总线才能挂起。但是,只要有一个设备恢复,总线就得恢复

    2,即插即用设备支持

      这个大家都深有体会,你把PS/2的鼠标,键盘拔出来,然后再插上去,看看是不是没反应了。但是把USB的鼠标键盘拔下来再插上去,可以继续用

      这就是传说中的即插即用的支持

    3,与用户空间的通信

       和用户间通信的方式很多,以前大名鼎鼎的proc文件系统,就是一个鲜明的代表。它给了用户一双千里眼。但是proc还是被后来者sysfs文件系统给拿下了,从此改朝换代。

      虽然proc依然在世,但是它的影响力已经下降。同时不得不说,proc得到过天下,肯定是有它的过人之处,那里sysfs可能会受挫。但是强者依然不是那么好动摇的

    Linux设备驱动模型有几个基本数据结构模型:kobject,kset,subsystem

    kobject:这是设备驱动模型的基础,就想是一座楼的地板砖和砖头。sysfs是它的子子孙孙,父父爷爷撑起来的

    struct kobject

    {

        const char *name;     //显示在sysfs中的名称

        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;  //是否初始化

        unsigned int state_in_sysfs:1;   //是否加入sysfs

        unsigned int state_add_uevent_sent:1;  //是否支持热插

        unsigned int state_remove_uevent_sent:1; //是否支持热拔

    }

    void  kobject_init(struct kobject *kobj,struct kobj_type *ktype)

    {

      char * err_str;

      if(!kobj)

      {

        err_str = "invalid kobject pointer!"

        goto error;

      }

      if(!ktype)

      {

        err_str = "must have a ktype to be initialized properly! ";

        goto error;

      }

      if(kobj->state_initialized)

      {

        printk(KERN_ERR"kobject (%p): tried to init an initialized"

                        "object ,something is seriously wrong. ",kobj);

        dump_stack();

      }

      kobject_init_internal(kobj);         //初始化kobject的内部成员

      kobj->ktype = ktype ;    //为kobject绑定一个ktype属性   

      return ;

    error:

       printk(KERN_ERR"kobject (%p) : %s ",kobj,err_str);

       dump_stack();

    }

    static void kobject_init_internal(struct koject *kobj)

    {

      if(!kobj)

        return ;

      kref_init(&kobj->kerf);

      INIT_LIST_HEAD(&kobj->entry);

      kobj->state_in_sysfs = 0;

      kobj->state_add_uevent_sent = 0;

      kobj->state_remove_uevent_sent = 0;

      kobj->state_initialized = 1;

    }

    内核接口:

        kobject_init();  始化kobject

        kobject_get();     增加kobject引用计数

        kobject_put();  减少kobject引用计数,计数为零时,调用kobject_release()释放,它在kobj_type里面

        kobject_set_name();   设置名字

        kobject_rename();    重命名

        kobject_add()      添加

        

    每个kobject都会有一个属性kobj_type

    struct kobj_type

    {

      void (*release)(struct kobject *kobj);    //释放kobject和其他占用资源的函数

      struct sysfs_ops *sysfs_ops;      //操作属性的方法

      struct attribute **default_attrs;      //属性数组

    };

    struct attribute

    {

      const char *name;       //属性的名称

      struct module *owner;    //只用拥有该属性的模块,已经不常使用

      mode_t mode;        //属性读写权限

    };

    struct sysfs_ops

    {

      ssize_t (*show)(struct kobject *,struct attribute *,char *);  //读属性操作函数

      ssize_t (*store)(struct kobject *,struct attribute *,const char *,size_t);  //写属性操作函数

    };

    struct kobject *kobject_get(struct kobject *kobj)

    {

      if(kobj)

        kref_get(&kobj->kerf);

      return kobj;

    }

    void kobject_put(struct kobject *kobj)

    {

      if(kobj)

      {

         if(!kobj->state_initialized)

          WARN(1,KERN_WARNING"kobject: ‘%s' (%p):is not initialized,yet kobject_put() is being called. ",kobject_name(kobj),kobj);

          kref_put(&kobj->kref,kobject_release);

      }

    }

    通常kobject类型的default_attr成员定义了kobjet拥有的所有默认属性。但是特殊情况下,可以添加一些默认的属性:

    添加属性文件:

    int sysfs_create_file(struct kobject *kobj,const struct attribute  *attr);

    删除属性文件:

    void sysfs_remove_file(struct kobject  *kobj , const   struct attribute  *attr);

    struct kset

    {

      struct list_head list;   //连接所包含的kobject对象的链表首地址

      spinlock_t  list_lock;   //维护list链表的自旋锁

      struct kobject kobj;  //内嵌kobject,说明kset本身也是一个目录

      struct kset_uevent_ops *uevent_ops;     //热插拔事件

    }; 

    struct kset_uevent_ops

    {

      int (*filter)(struct kset *kset,struct kobject *kobj);

      const char *(*name)(struct kset *kset,struct kobject *kobj);

      int (*uevent)(struct kset *kset,struct kobject *kobj,struct kobj_uevent_ent *env);

    };

    kset和kobject关系:

    1,kset集合包含了属于其的kobject结构体,kset.list链表用来 连接第一个和最后一个kobject对象。第一个kobject使用entry连接kset集合和第二个kobject对象。第二个kobject对象使用entry连接第一个kobject对象和第三个kobject对象,依次类推,最终形成了一个kobject对象的链表

    2,所有的kobject结构的parent指针指向kset包含的kobject对象,构成一个父子层次关系

    3,kobject的所有kset指针指向包含它的kset集合,所以通过kobject对象很容易就能找到kset集合

    4,kobject的kobj_type指针指向自身的kobj_type,每一个kobject都有一个单独的kobj_type结构。另外在kset集合中也有一个kobject结构体,该结构体的XXX也指向一个kobj_type结构体。可知,kobj_type中定义了一组属性和操作属性的方法。这里注意:kset中kobj_type的优先级要高于kobject对象中的kobj_type的优先级。如果两个kobj_type都存在,那么优先调用kset中的函数。如果kset中的kobj_type为空,才调用各个kobject结构体本身对应的kobj_type中的函数

    5,kset中的kobj也负责对kset的引用计数

    kset操作

    void kset_init(struct kset *k)  //初始化

    {

      kobject_init_internal(&k->kobj);

      INIT_LIST_HEAD(&k->list);

      spin_lock_init(&k->list_lock);

    }

    int kset_register(struct kset *k); //注册函数

    void kset_unregister(struct kset *k);  //注销函数

    static inline struct kset *kset_get(struct kset *k);

    static inline void kset_put(struct kset *k);

    设备驱动模型的三大组件

    总线:

     struct bus_type

    {

      const char *name;

      struct bus_attribute *bus_attrs;

      struct device_attribute *dev_attrs;

      struct driver_attribute *drv_attrs;

      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 (*suspend_late)(struct device *dev,pm_message_t state);

      int (*resume_early)(struct device *dev);

      

      struct dev_pm_ops *pm;

      struct bus_type_private *p;

    };

    struct bus_type_private

    {

      struct kset subsys;  //代表该bus子系统,里面的kobj是该bus的主kobj,也就是最顶层

      struct kset *drivers_kset;  //挂载到该总线上的所有驱动集合

      struct kset * devices_kset;  //挂载到该总线上的所有设备集合

      struct klist klist_devices;  //所有的设备列表

      struct klist klist_drivers;  //所有的驱动程序列表

      struct block_notifier_head bus_notifier;

      unsigned int drivers_autoprobe:1; //设置是否在驱动注册是,自动弹出设备

      struct bus_type *bus;  //回指向包含自己的总线

    };

    int bus_register(struct bus_type *bus);

    void bus_unregister(struct bus_type *bus);

    struct bus_attribute

    {

      struct attribute attr;

      ssize_t (*show)(struct bus_type *bus,char *buf);

      ssize_t (*store)(struct bus_type *bus,const char *buf,size_t count);

    };

    int bus_create_file(struct bus_type *bus,struct bus_attribute *attr);

    void bus_remove_file(struct bus_type *bus,struct bus_attribute *attr);

    设备:

     struct device

    {

      struct klist klist_children;   //连接子设备的链表

      struct device *parent;     //指向父设备的指针

      struct kobject kobj;      //内嵌的kobject

      char bus_id[BUS_ID_SIZE];   //连接到总线上的位置

      unsigned uevent_supress: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 devt;    //设备号

      struct class *class; //指向设备所属类

      struct attribute_group **groups; //设备的组属性

      void (*release)(struct device *dev);  //释放设备描述符的回调函数

      ...

    };

    int device_register(struct device *dev);

    void device_unregister(struct device *dev);

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

    };

    int device_create_file(struct device *device,struct device_attribute);

    void device_remove_file(struct device *dev,struct device_attribute *attr);

    驱动:

    struct device_driver

    {

      const char *name;  //设备驱动名字

      struct bus_type *bus;  //指向驱动属于的总线,总线上有很多设备

      struct module *owner;   //设备驱动自身模块

      const char *mod_name;  //设备驱动名字

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

      struct attribute_group **group;

      struct dev_pm_ops *pm;

      struct driver_private *p;

    };

    struct driver_private

    {

      struct kobject kobj;   //内嵌kobject结构,用来构建设备驱动程序模型

      struct klist klist_devices;  //该驱动支持的所有设备链表

      struct klist_node knode_bus;  //该驱动所属总线

      struct module_kobject *mkobj;  //驱动的模块

      struct device_driver *driver;  //指向驱动本身

    };

    int driver_register(struct device_driver *drv);

    void driver_unregister(struct device_driver *drv);

    struct driver_attribute

    {

      struct attribute attr;

      ssize_t (*show)(struct device_driver *driver ,char *buf);

      ssize_t (*store)(struct device_driver*driver,const char *buf,size_t count);

    };

    int driver_create_file(struct device_driver *drv,struct driver_attribute *attr);

    void driver_remove_file(struct device_driver *drv,struct driver_attribute *attr);

  • 相关阅读:
    在虚拟机上安装Docker并运行镜像下
    分红包思想
    从微信授权到JWT认证——玩转token之路
    .Net(C#)数据库访问连接方式
    Asp.Net导出Excel表格之二(HttpContext.Current.Response)
    我的ip_本机ip_本地ip_本机ip地址_公网ip_ip地址查询
    干货版“测试小品”欢乐场景
    家用宽带搭建Hmailserver邮箱服务器
    【microPython与esp8266】之一——呼吸灯与PWM
    截取长文本,显示省略号(text-overflow:ellipsis)
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7189695.html
Copyright © 2011-2022 走看看