LINUX设备驱动模型分析之二 总线(BUS)接口分析

上一章我们对LINUX设备模型的概念进行了分析,也大概理清了bus、class、device、drivers之间的关联与区别,本章开始我们就进行具体概念的分析,首先分析总线的概念以及总线接口分析。在进行分析之前我们还是把上一章中的bus-device-drivers的关联图放上来,通过该图我们对总线接口的实现也会有一个大概的轮廓,即每一个具体的总线均包括设备与驱动两部分,而每一个具体总线的所有添加的设备均链接至device下,每一个总线的所有注册的驱动均链接至drivers,而bus接口所有实现的功能也可以大致分为总线的注册、设备的注册、驱动的注册这三个部分。因此本章也从这几个方面进行分析。

总线相关的结构体介绍

与总线相关的结构体主要包括bus_type、bus_attribute、device、driver、device_attribute、driver_attribute、

subsys_private、kset、kobject等,其中kset、kobject、subsys_private结构体我们之前已经介绍过,此处不再细说。

struct bus_type接口说明

该接口定义如下:

struct bus_type {
	/*总线名称*/
	const char		*name;
	/*总线对应设备名称*/
	const char		*dev_name;
	/*该总线对应的device*/
	struct device		*dev_root;
	struct bus_attribute	*bus_attrs;/*总线属性*/
	struct device_attribute	*dev_attrs;/*设备属性*/
	struct driver_attribute	*drv_attrs;/*驱动属性*/
	/*match接口,用于进行device与driver的匹配*/
	int (*match)(struct device *dev, struct device_driver *drv);
	/*uevent接口,用于发送kobject event,供应用层mdev/udev使用*/
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	/*总线的probe接口,该接口会调用具体驱动的probe接口*/
	int (*probe)(struct device *dev);
	/*总线的remove接口,一般该接口主要是调用具体驱动的remove接口*/
	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;/*pm相关*/

	struct iommu_ops *iommu_ops;
	/*该接口包含了该bus对应kobject、device对应的kset、driver对应的kset、
	链接所有设备的链表、链接所有驱动的链表等*/
	struct subsys_private *p;
	struct lock_class_key lock_key;
};

 

该结构体定义的目的也是实现上述bus-device-driver之间的关联,同时也与kobject、kset关联,进而实现与sysfs的关联。具体说明如下:一、bus-device-driver关联通过struct subsys_private类型的变量p,定义了device、driver对应的kset变量,完成将所有设备、驱动对应的kobject;

  1. 通过struct subsys_private类型的变量p,定义了device、driver对应的链表,将所有该总线注册或添加的驱动与设备链接在一起,实现上面图中所描述的bus-device-driver关联

二、总线自身属性以及与kobject、sysfs的关联

  1. 通过bus_attribute类型的变量,定义总线自身的属性参数(通过sysfs中的接口,完成sysfs文件的创建,并提供该属性的读写接口store/show等);
  2. 通过device_attribute类型的变量,完成该bus总线默认的设备属性参数(这些参数为该总线下每一个设备均包含的属性,通过sysfs中的接口,完成sysfs文件的创建,并提供该属性的读写接口store/show等);
  3. 通过driver_attribute类型的变量,完成该bus总线默认的驱动属性参数(这些参数为该总线下每一个驱动均包含的属性,通过sysfs中的接口,完成sysfs文件的创建,并提供该属性的读写接口store/show等);
  4. struct subsys_private类型的变量p,定义了该总线对应的kobject变量,从而为该bus总线在sysfs文件系统下创建该bus对应的目录,并借助该kobject->kref变量,提供针对该总线的引用计数;

三、总线相关的处理接口

  1. match接口完成总线中设备与驱动的匹配检测,匹配上的设备与驱动即完成绑定操作;
  2. probe接口,当设备与驱动匹配后,则调用总线的probe接口,进行设备的probe操作(该接口一般会调用驱动的probe接口,进行探测操作);
  3. remove接口,当移除驱动时,即会调用总线的remove接口,进行申请的remove操作(该接口一般会调用驱动的remove接口,进行移除操作);

struct bus_attribute结构体说明

该结构体通过包含通用属性结构体变量attr和store/show接口,实现针对bus类型属性的读写接口。

struct bus_attribute {
    /*通用属性结构体,struct sys_ops中的show/store接口即传递该类型的变量*/
	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);
};

 

 

为了理清该结构体变量与kobject、sysfs等的关联,特将它们之间的关联图画出。在调用总线注册接口bus_register时,会创建该bus总线默认的属性文件,并设置该bus总线对应kobject的kobj_type,而kobj_type中的操作接口指针即为 bus_sysfs_ops,其提供针对bus属性的通用读写接口,而创建的bus属性变量中,针对具体的属性定义特定接口,由bus_sysfs_ops中的读写接口调用。

其中的关联说明如下:

  1. 图中的kset对应bus_type->p->subsys,而kset中的kobject包含了引用计数以及kobj_type;
  2. 根据bus_type->p->subsys,通过调用sysfs中的kset_register,实现kset初始化,并根据其kobject变量在sysfs中创建目录,并调用sysfs_create_file将该bus相关的默认属性在sysfs中创建对应的文件;
  3. 应用层若要访问bus属性,则通过系统调用open、read、write接口,最终调用sysfs中注册的文件操作接口sysfs_open_file、sysfs_read_file、sysfs_write_file,最终调用bus_attribute中store/show接口(这块具体的实现可参考《

LINUX SYSFS文件系统分析之四 文件处理及相关系统调用分析》)。

上图没有画出文件描述符、VFS中dentry、inode节点与之的关联,在《

LINUX SYSFS文件系统分析之四 文件处理及相关系统调用分析》中有详尽的描述。

此处关联图是针对bus_type本身的kset、kobject、bus_attribute、sysfs_dirent、kobj_type、sysfs_ops的关联,而在bus_register时,也就是完成这些结构体类型变量之间的关联,实现bus目录、bus属性等在sysfs中表示,我们理解了下面这张图基本上也就属性了bus总线相关的功能了。

而针对bus总线对应kobject的通用kobj_type定义以及其sysfs_ops定义如下所示

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct bus_attribute *bus_attr = to_bus_attr(attr);
	struct subsys_private *subsys_priv = to_subsys_private(kobj);
	ssize_t ret = 0;

	if (bus_attr->show)
		ret = bus_attr->show(subsys_priv->bus, buf);
	return ret;
}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct bus_attribute *bus_attr = to_bus_attr(attr);
	struct subsys_private *subsys_priv = to_subsys_private(kobj);
	ssize_t ret = 0;

	if (bus_attr->store)
		ret = bus_attr->store(subsys_priv->bus, buf, count);
	return ret;
}

static const struct sysfs_ops bus_sysfs_ops = {
	.show	= bus_attr_show,
	.store	= bus_attr_store,
};
static struct kobj_type bus_ktype = {
	.sysfs_ops	= &bus_sysfs_ops,
};

struct device_attribute 结构体说明

该结构体的定义如下,其定义实现与bus_attribute类似,而在bus_type->dev_attrs中定义的默认属性,属于添加到该总线上的所有设备的默认属性,即每一个添加到该总线上的设备,其对应kobject上均会包含这些默认属性。

 

/* interface for exporting device attributes */
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);
};

在进行bus_add_device接口时,除了创建设备对应的kobject外,还会将该kobject链接到bus_type->klist_devices的list上,同时会调用sysfs_create_file将默认属性在该device对应目录下创建sysfs文件,其处理流程和上面bus_attribute类似,唯一不同的是此时创建的kobject是链接的kset->list上,而不是kset->kobj的,如下图所示。

 

 

针对device设备而言,其kobject对应的默认kobj_type以及默认通用sysfs_ops的定义如下

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = kobj_to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->show)
		ret = dev_attr->show(dev, dev_attr, buf);
	if (ret >= (ssize_t)PAGE_SIZE) {
		print_symbol("dev_attr_show: %s returned bad count\n",
				(unsigned long)dev_attr->show);
	}
	return ret;
}

static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = kobj_to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->store)
		ret = dev_attr->store(dev, dev_attr, buf, count);
	return ret;
}

static const struct sysfs_ops dev_sysfs_ops = {
	.show	= dev_attr_show,
	.store	= dev_attr_store,
};
static struct kobj_type device_ktype = {
	.release	= device_release,
	.sysfs_ops	= &dev_sysfs_ops,
	.namespace	= device_namespace,
};

struct driver_attribute 结构体说明

该结构体的定义与device_attribute类似,在bus_type->drv_attrs中定义的默认属性,属于添加到该总线上的所有设备的默认属性,即每一个添加到该总线上的设备,其对应kobject上均会包含这些默认属性。

在bus_add_driver中会进行driver的添加以及对应kobject、driver_attribute对应属性文件的创建等信息,其流程图与上面device的处理流程类似,此处不再赘述。

/* sysfs interface for exporting driver attributes */

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

上面分析了bus相关的结构体变量(没有分析struct device 、struct device_driver变量,后面分析),下面就开始进行相关接口的分析操作,主要设置bus注册、注销、bus模块初始化、设备添加到总线、驱动添加到总线上等。

总线模块初始化接口分析

该接口主要通过调用kset_create_and_add接口创建一个kset类型的变量bus_kset,并在sysfs的根目录下创建bus名称的目录(该接口的实现也比较简单,流程图如下所示,而关于kset_create_and_add接口的实现,可参考文件《LINUX SYSFS文件系统分析之五 kobject、kset相关接口分析》)。该kset变量会作为bus子模块的父目录,同时也会将所有注册的bus类型变量汇聚在一起,而bus_kset与各具体bus的kset的关联如下下图所示,通过kset->list将所有已注册的xxx_bus_kset变量链接在一起。

 

总线注册与注销接口分析说明

总线注册接口bus_register

总线注册接口的流程图如下,其实结合上面的结构体分析,我们也可以大概总结出总线注册接口所实现的功能,下面我们详细分析之。

  1. 创建subsys_private类型的变量,赋值给该bus变量的subsys_private类型的成员变量p;
  2. 设置bus总线对应的kset、kobject,并调用kset_register接口在sysfs文件系统的/bus/目录下创建该bus子目录,并将该bus对应的kobject链接到全局变量bus_kset的链表中;
  3. 调用bus_create_file接口,为该bus默认的bus_uevent属性创建对应的sysfs文件;
  4. 调用kset_create_and_add为该bus创建kset类型的devices_kset、drivers_kset,并在该bus目录下创建对应的devices、drivers 的sysfs子目录,而这两个变量,分别用于将注册到该总线上的所有设备、驱动链接在一起;
  5. 调用add_probe_files、bus_add_attrs接口,创建该bus相关的bus_attr_drivers_probe、bus_attr_drivers_autoprobe以及该bus默认属性bus_attrs对应的sysfs文件等。

 

至此,即完成了bus类型的注册。而针对bus注册相关的bus_ktype接口,其定义如下,主要为bus_ktype、bus_sysfs_ops的定义,针对bus_sysfs_ops为bus总线属性读写的通用接口,在上面bus_attribute结构体的定义中,也进行了详细说明,bus_ktype、bus_sysfs_ops与kobject以及sysfs模块相关的结构体结合后,即完成了针对bus属性相关的系统调用关联。关于关联的详细说明请参考《

LINUX SYSFS文件系统分析之四 文件处理及相关系统调用分析

总线注销接口bus_unregister

针对该接口,主要就是bus_register中的相反操作,通过调用kset_unregister、bus_remove_file、bus_remove_attrs、device_unregister以及kfree接口实现针对bus_register中注册的kset以及创建的sysfs文件的remove操作,同时释放在bus_register中申请的subsys_private类型的内存空间。

至此完成了总线注册与注销接口的分析,而针对bus_add_device、bus_add_driver接口的分析以及device_add、device_init等接口的实现分析,将在后面的章节中介绍与说明。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动总线设备模型(Driver model)是一种抽象的设备模型,用于描述系统中各种设备设备之间的关系。它提供了一种标准的设备访问接口,使得设备驱动程序可以独立于硬件平台而存在,从而提高了设备驱动程序的可移植性和可维护性。 在Linux中,驱动总线设备模型包括以下几个主要的概念: 1. 总线Bus):总线是一种连接设备的物理或逻辑结构,用于实现设备之间的通信。Linux支持多种总线类型,例如PCI、USB、I2C等。 2. 设备(Device):设备是指在总线上注册的硬件设备,每个设备都有一个唯一的设备树路径和设备标识符。设备可以包括子设备和属性,例如硬件资源、中断、供电等信息。 3. 驱动程序(Driver):驱动程序是指用于控制设备的软件程序,它通过向设备发送命令和读取设备的状态来实现对设备的控制。驱动程序可以注册到总线上,当设备被插入到总线上时,总线会自动匹配相应的驱动程序并加载它。 4. 类(Class):类是一组具有相似功能的设备的集合,例如输入设备、网络设备、存储设备等。类提供了一些通用的接口和属性,使得驱动程序可以更加方便地操作设备Linux驱动总线设备模型的主要优点包括: 1. 支持多种总线类型,使得驱动程序可以在不同的硬件平台上运行。 2. 提供标准的设备访问接口,使得驱动程序可以独立于硬件平台而存在。 3. 支持设备热插拔和动态配置,使得系统更加灵活和可扩展。 4. 提供了类的概念,使得驱动程序可以更加方便地操作设备

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值