Linux设备模型

Linux设备驱动模型
我们在写最简单的设备驱动程序的时候,我们将所有的硬件信息都保存在了驱动代码中,这样有一个非常明显的不足:会导致驱动程序的通用性极差,一旦硬件平台或硬件连接有锁改变,就一定要修改驱动代码。为了解决这个问题,Linux在2.6版本之后,添加了“总线—设备—驱动”的Linux设备模型,有效地实现了设备和驱动的分离。

该设备模型通过几个数据结构来反映当前系统中总线、设备以及驱动的工作状况,提出了以下几个重要概念:

  • 设备(device):挂载在某个总线的物理设备;
  • 驱动(driver):与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;
  • 总线(bus):负责管理挂载对应总线的设备以及驱动;
  • 类(class):对于具有相同功能的设备,归结到一种类别,进行分类管理;

1、总线
在实际的应用中,大部分的外设都是连接在总线上,它们之间的物理连接,如下图所示:

在这里插入图片描述
在驱动设计中,总线驱动的主要工作是负责管理两个链表。分别是添加到该总线的设备链表以及注册到该总线的驱动链表。当你向总线添加(移除)一个设备(驱动)时,便会在对应的列表上添加新的节点,同时对挂载在该总线的驱动以及设备进行匹配,在匹配过程中会忽略掉那些已经有驱动匹配的设备。
在这里插入图片描述
前面我们说过,内核是以数据结构的方式来管理总线、设备和驱动的,那么在内核中总线的数据结构是怎样的呢?内核中使用结构体bus_type来表示总线,具体内容如下:

struct bus_type {
    const char              *name;
    const struct attribute_group **bus_groups;
    const struct attribute_group **dev_groups;
    const struct attribute_group **drv_groups;
      
    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);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;

    struct subsys_private *p;
    
};
  • name:总线的名称,当新注册一种总线的时候,系统会自动在/sys/bus目录下创建一个同名的目录。
  • drv_groups 、dev_groups 、bus_groups: 分别表示驱动、设备以及总线的属性。这些属性可以是内部变量、字符串等等。通常会对应的/sys目录下在以文件的形式存在,对于驱动而言,在目录/sys/bus/< bus-name >/driver/< driver-name >存放了设备的默认属性;设备则在目录/sys/bus/< bus-name >/devices/< driver-name >中。这些文件一般是可读写的,用户可以通过读写操作来获取和设置这些attribute的值。
  • match : 函数指针,当向总线注册一个新的设备或者是新的驱动时,会自动调用该回调函数进行设备和驱动的匹配。
  • uevent:总线上的设备发生添加、移除或者其它动作时,就会调用该函数。
  • probe : 当总线将设备以及驱动相匹配match之后,执行该回调函数,最终会调用驱动提供的probe函数。
  • remove : 当设备从总线移除时,调用该回调函数。
  • suspend、resume : 电源管理的相关函数,当总线进入睡眠模式时,会调用suspend回调函数;而resume回调函数则是在唤醒总线的状态下执行。
  • p:该结构体用于存放特定的私有数据,其成员klist_devices和klist_drivers记录了挂载在该总线的设备和驱动。

我们可以通过下面两个函数向内核注册或注销一个总线。

//注册总线
int bus_register(struct bus_type *bus);
//注销总线
void bus_unregister(struct bus_type *bus);

但是,Linux内核已经为我们写好了大部分总线驱动,我们一般不用自行去注册一个新的总线。
/sys/bus/目录中包含了当前系统中已经注册了的所有总线,例如i2c,spi,platform等。每个总线目录都拥有两个子目录devices和drivers,分别记录着挂载在该总线的所有设备以及驱动。

2、设备
在内核使用device结构体来描述我们的物理设备,如下所示:

struct device {
        const char *init_name;  
        struct device *parent;
        struct bus_type *bus;
        struct device_driver *driver;
        void *platform_data;
        void *driver_data;
        struct device_node *of_node;
        dev_t devt;
        struct class *class;
        void (*release)(struct device *dev);
        const struct attribute_group **groups;  /* optional groups */
};
  • init_name:指定该设备的名称,总线匹配时,其中一种方式就是比较名字,来进行配对;
  • parent:表示该设备的父对象;
  • bus:表示该设备依赖于哪个总线,注册设备时,内核会将该设备注册到对应的总线;
  • of_node:存放设备树中匹配的设备节点。当内核使能设备树,总线负责将驱动的of_match_table以及设备树的compatible属性进行比较之后,将匹配的节点保存到该变量;
  • platform_data:特定设备的私有数据,通常定义在板级文件中;
  • driver_data:同上,驱动层可通过dev_set/get_drvdata函数来获取该成员;
  • class:指向了该设备对应类;
  • dev:设备的设备号;
  • release:回调函数,当设备被注销时,会调用该函数;(注意:如果未定义该函数,移除设备时,会报错; 若设备注销时无需处理,可定义一个空的release函数)。
  • group:指向struct attribute_group类型的指针,指定该设备的属性;

我们可以通过下面两个内核API函数向内核注册或注销一个设备:

//注册设备
int device_register(struct device *dev);
//注销设备
void device_unregister(struct device *dev);

当成功注册总线时,会在/sys/bus目录下创建对应总线的目录,该目录下有两个子目录,分别是drivers和devices,我们使用device_register注册的设备从属于某个总线时,该总线的devices目录下便会存在该设备文件。

3、驱动
设备能否正常工作,取决于驱动。驱动需要告诉内核,自己可以驱动哪些设备,如何初始化设备。在内核中,使用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 */

        const struct of_device_id       *of_match_table;
        const struct acpi_device_id     *acpi_match_table;

        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);

        const struct attribute_group **groups;
};
  • name:指定驱动名称,总线进行匹配时,利用该成员与设备名进行比较;
  • bus:表示该驱动依赖于哪个总线,内核需要保证在驱动执行之前,对应的总线能够正常工作;
  • suppress_bind_attrs:布尔量,用于指定是否通过sysfs导出bind与unbind文件,bind与unbind文件是驱动用于绑定/解绑关联的设备;
  • owner:表示该驱动的拥有者,一般设置为THIS_MODULE;
  • of_match_table:指定该驱动支持的设备类型。当内核使能设备树时,会利用该成员与设备树中的compatible属性进行比较;
  • remove:当设备从操作系统中拔出或者是系统重启时,会调用该回调函数;
  • probe:当驱动以及设备匹配后,会执行该回调函数,对设备进行初始化。通常的代码,都是以main函数开始执行的,但是在内核的驱动代码,都是从probe函数开始的;
  • group:指向struct attribute_group类型的指针,指定该驱动的属性;

内核提供了driver_register函数以及driver_unregister函数来注册/注销驱动,成功注册的驱动会记录在/sys/bus/< bus >/drivers目录,函数原型如下所示:

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值