LINUX设备驱动模型分析之三 驱动(DRIVER)接口分析

     上一章我们分析了bus-driver-device模型中bus接口部分,本章我们将分析driver接口,在bus-driver-device模型中,driver接口是依附于bus上,而不像device有独立统一的kset,而driver接口是依附于具体类型的bus总线。针对bus-driver-device这三类模块之间的关联与区别,我们在总线接口分析时,已经分析过,此处不再赘述,仅引用《LINUX设备驱动模型分析之一 总体概念说明》中的一张图,试图再次说明bus-driver-device之间的关联。

     如下图所示,所有注册的设备均在sysfs的device(即通过devices_kset将所有device对应的kobject链接在一起),而具体bus_type->p->devices_kset->kobj则对该总线下所有注册的设备,创建链接目录,用以链接至devices_kset下汇聚的kobject。

        针对driver接口,相对来说内容也不复杂,本章我们还是从数据接口、driver的注册以及注销

接口进行分析驱动接口。

相关结构体分析

      针对driver子模块而言,涉及的结构体主要包括device_driver、driver_private,其中device_driver用于抽象驱动类,而具体的驱动可理解为一个驱动对象,而driver_private则主要用于链接至sysfs、bus模块、device模块。下面我们介绍这两个结构体变量。

struct device_driver 结构体分析

该结构体包含的内容可梳理为如下几个部分:

  1. 性能管理部分,主要与性能管理模块管理(非本次分析内容,暂不展开,这块我还不熟);
  2. 该驱动相关的接口指针,包括probe、remove等,其中
    1. probe为该驱动的探测接口,实现对设备的探测操作(包括设备相关的寄存器初始化、中断初始化等);
    2. remove为该驱动的移除接口,当需要移除一个驱动模块时,则调用该接口实现移除接口
    3. shutdown、suspend、resume主要为电源管理相关的接口;
    4. of_device_id类型的变量主要为设备树所用,该结构体变量中会定义该驱动所支持的of device,用compatible值表示支持的of device的名称。
  3. 该驱动所包含的属性,通过attribute_group类型的变量传递该驱动所有的默认属性;
  4. 通过driver_private类型的变量,包含了该驱动与sysfs、device、bus之间关联的关联;
    1. 通过定义kobject类型的变量,为该变量在sysfs文件系统中创建了该驱动对应的命令;
    2. 通过klist_device,链接所有该驱动支持且绑定的设备;
    3. 通过knode_bus将该驱动链接至总线的驱动链表中;
    4. 通过module_kobject类型的变量,将该驱动与module模块关联(本次不对module模块关联);
  5. 通过bus_type类型的关联,说明本驱动所依附的总线。
struct device_driver {
	const char		*name;/*驱动名称*/
	struct bus_type		*bus;/*该驱动所依附的总线*/

    /*该驱动所属module*/
	struct module		*owner;
    /*模块的名称*/
	const char		*mod_name;	/* used for built-in modules */

    
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

    /*设备树使用的设备id*/
	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);
    /*shutdown 、suspend、resume主要是对应电源管理方面的接口*/
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
    /*该驱动所相关的属性接口,主要用于与sysfs模块管理*/
	const struct attribute_group **groups;
    /*性能管理相关的内容*/
	const struct dev_pm_ops *pm;
    /*该驱动模块相关的私有变量,主要包括驱动对应的kobject、所属模块的kobject等*/
	struct driver_private *p;
};

struct driver_private结构体分析

该结构体变量主要是包含了与driver、device、bus、sysfs模块关联的结构体变量,其中

  1. 通过定义kobject类型的变量,为该变量在sysfs文件系统中创建了该驱动对应的命令;
  2. 通过klist_device,链接所有该驱动支持且绑定的设备;
  3. 通过knode_bus将该驱动链接至总线的驱动链表中;
  4. 通过module_kobject类型的变量,将该驱动与module模块关联(本次不对module模块关联);
struct driver_private {
    /*该驱动对应的kobject*/
	struct kobject kobj;
    /*用于汇聚该驱动所绑定的所有设备*/
	struct klist klist_devices;
    /*用于链接至xxx_bus_type的klist_drivers链表上*/
	struct klist_node knode_bus;
    /*该驱动所属模块的kobject*/
	struct module_kobject *mkobj;
    /*指向本driver_private类型变量所属的driver变量*/
	struct device_driver *driver;
};

       以上便是driver模块相关的结构体变量,为了让我们更好的理解bus-driver-device,我们现将bus_type、device_driver、kobject、driver_private等结构体变量的关联关系画出来,以此说明driver模块在bus-driver-device中的位置,以及是如何与sysfs进行关联的。

        如下图为 bus_type、device-driver、kobject相关结构体之间的关联,该图说明了bus_type、device-driver、kobject之间的关联。我们分几个方面进行说明:

  1. 系统下所有已注册bus_type类型的关联
  2. device_driver与bus_type、kobject之间的关联
    1. 已注册的device_driver类型的变量通过其driver_private类型的成员变量中的knode_bus成员,链接至其依附的总线变量的klist_drivers链表上,并且设置device_driver的成员变量bus指向为其所依附的总线变量;
    2. 已注册的device_driver类型的变量通过其driver_private类型的成员变量中的kobject变量,将该kobject链接至总线变量的driver_kset中,而总线变量通过其driver_kset变量,实现将所有已注册的设备驱动对应的kobject变量汇聚至一起,这是driver_kset-kobject的关联。
  3. device_driver类型变量与xxx_device_driver类型变量之间的关联,我们介绍的device_driver变量,在实际使用时,一般会嵌入至具体类型的驱动结构体中,如i2c_driver、platform_driver,以便针对具体类型的驱动,增加该驱动相关的一些特定属性以及接口等。

      上面针对driver-kobject-sysfs之间的关联,没有详细说明,下图则详细说明了这三者之间的关联。

  1. driver与kobject的关联,当注册一个driver时,即会创建一个kobject,用以与sysfs关联
  2. kobject通过sysfs_drient、sysfs_open_dirent、sysfs_buffer、kobj_type、sysfs_ops,实现了在sysfs目录下创建目录,并为该驱动对应的属性创建sysfs类型的文件。

driver-kobj_type相关说明

     在上面的图中,说明了driver-kobj_type,此处稍作说明。针对driver-kobject,其kobj_type为driver_ktype,该结构体定义了所有device-driver的通用sysfs_ops以及driver-koject的释放接口,其定义如下

static struct kobj_type driver_ktype = {

    .sysfs_ops        = &driver_sysfs_ops,

    .release        = driver_release,

};

其中driver_release的定义如下,主要是将driver_private类型的内存变量释放。

static void driver_release(struct kobject *kobj)

{

    struct driver_private *drv_priv = to_driver(kobj);



    pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);

    kfree(drv_priv);

}

而driver_sysfs_ops主要是driver属性的通用读写接口(show/store),这两个接口通

driver_attribute类型变量(该结构体变量在上一篇文章中已经介绍,此处不再赘述),实现调用具体驱动属性的读写接口(show/store)

static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,

     char *buf)

{

    struct driver_attribute *drv_attr = to_drv_attr(attr);

    struct driver_private *drv_priv = to_driver(kobj);

    ssize_t ret = -EIO;



    if (drv_attr->show)

        ret = drv_attr->show(drv_priv->driver, buf);

    return ret;

}



static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,

      const char *buf, size_t count)

{

    struct driver_attribute *drv_attr = to_drv_attr(attr);

    struct driver_private *drv_priv = to_driver(kobj);

    ssize_t ret = -EIO;



    if (drv_attr->store)

        ret = drv_attr->store(drv_priv->driver, buf, count);

    return ret;

}

驱动注册与注销接口分析说明

        上一小节说明了device-driver相关的结构体,以及这些结构体与kobject、bus、device之间

的关联。通过上面的关联图,我们大概也是可以知道驱动注册涉及哪些部分,简单的说明就是建立device-driver相关的结构体与kobject、sysfs、bus、device之间的关联,以及驱动与设备的绑定,并实现对设备的初始化等操作。下面我们就介绍驱动注册与注销接口。

 

 

驱动注册接口driver_register

驱动注册接口为driver_register,该接口实现的功能如下:

  1. 为该驱动创建私有变量(driver_private类型),并对其进行初始化,同时实现device-driver与driver_private类型变量的绑定操作
  2. 为该驱动创建kobject类型的变量,设置kobject的kobj_type为driver_ktype,将该kobject变量链接至所依附总线变量的driver_kset成员上(bus_type->p->drivers_kset);
  3. 将该驱动链接至其所依附总线变量的已注册驱动链表上(klist_drivers);
  4. 为该驱动创建通用的默认属性文件(即其所依附总线变量定义的默认驱动属性,这在上一篇文章中已经说明,需要了解的请查看上一篇文章《》)
  5. 为该驱动与模块建立联系(此处不细说,后续会单独分析module部分)
  6. 若该驱动依附的总线支持自动探测功能,则遍历该总线上已注册的所有设备,实现设备-驱动的绑定操作。
  7. 为该驱动私有的默认属性,创建对应的sysfs文件。
  8. 向应用层发送driver-kobj已添加的uevent,应用层的udev/mdev,会根据制定的策略,执行相应的动作。

       以上即是驱动注册接口的大致功能,即是实现device、driver、bus、kobject、sysfs等模块

相关结构体的关联操作。

 

        以上实现向bus中注册driver的主要接口为bus_add_driver,我们对该接口的实现再进行一下说明

bus_add_driver接口分析

        该接口即实现了上述说明的大部分功能,该接口的实现流程图如下,主要实现功能为

  1. 为该驱动创建私有变量(driver_private类型),并对其进行初始化,同时实现device-driver与driver_private类型变量的绑定操作
  2. 为该驱动创建kobject类型的变量,设置kobject的kobj_type为driver_ktype,将该kobject变量链接至所依附总线变量的driver_kset成员上(bus_type->p->drivers_kset);
  3. 将该驱动链接至其所依附总线变量的已注册驱动链表上(klist_drivers);
  4. 为该驱动创建通用的默认属性文件(即其所依附总线变量定义的默认驱动属性,这在上一篇文章中已经说明,需要了解的请查看上一篇文章《》)
  5. 为该驱动与模块建立联系(此处不细说,后续会单独分析module部分)
  6. 若该驱动依附的总线支持自动探测功能,则遍历该总线上已注册的所有设备,实现设备-驱动的绑定操作。

driver_attach接口分析

      在上面接口中,通过调用driver_attach实现已注册的device与driver的绑定,因此我们有必要介绍下这个接口,这个接口的实现流程如下,针对每一个已注册的设备,执行如下几个主要的操作:

  1. 调用driver->bus->match接口,判断设备与驱动是否匹配,不匹配则直接返回(一般匹配接口主要是对设备与驱动名称进行判断,若驱动与设备名称相同则认为匹配);
  2. 若驱动与设备匹配,且设备尚未绑定驱动:
    1. 调用bus->probe/driver-probe对设备进行初始化(一般bus的probe接口,也是调用driver的probe接口,实现对设备的初始化操作);
    2. 调用driver_bond实现设备与驱动绑定(包括将设备链接至驱动变量的klist_devices变量中,同时device->driver指针执行该驱动变量。同时向系统中发送device-driver绑定的通知)。

       以上driver注册相关的接口,其实现流程与上面流程图类似,基本上根据上述流程图即可理解相应的代码实现,此处不再列出对应的代码实现,对代码的每一行进行解释(以上流程分析建立在对sysfs文件系统熟悉的情况下,若对sysfs文件系统的实现不熟悉,建议阅读我之前写的分析文档《LINUX SYSFS文件系统分析之二 sysfs文件系统相关的结构体说明》、《LINUX SYSFS文件系统分析之四 文件处理及相关系统调用分析》等)。

 

 

驱动注销接口driver_unregister

      该接口主要是将驱动从总线上注销,相应的操作刚好与驱动注册相反。主要涉及的函数包括driver_unregister、driver_remove_groups、bus_remove_driver。相对来说也比较简单,主要实现的功能如下:

  1. 删除该驱动的私有默认属性对应sysfs文件,通过调用driver_remove_groups接口实现(sysfs_remove_group);
  2. 删除该驱动的通用属性对应的sysfs文件(通过调用driver_remove_attrs接口实现,最终通过调用sysfs_remove_file实现sysfs文件的删除);
  3. 将该驱动从bus->klist_drivers链表删除;
  4. 调用driver_detach接口,实现驱动与设备的解绑操作;
  5. 调用module_remove_driver,实现module与driver的解绑操作;
  6. 调用kobject_put,使driver-kobj的引用计数减一,若减一后为0,则会调用kobject_release对kobject进行释放,同时调用kobj_type->release,即driver_release接口,实现对driver_private类型变量的释放操作。
  7. 调用bus_put,使bus-kset-kobject的引用计数减一,若减一后为0,则对bus-kset-kobject调用kobject_release进行内存的释放等操作。

 

     该接口的实现相对来说也比较明晰,此处不再给出详细的流程图,由读者自己去实现。

 

以上完成了driver模块的分析操作,下一篇进行device模块的分析。

发布了140 篇原创文章 · 获赞 30 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/lickylin/article/details/102768085