linux设备驱动模型一上层容器之bus

总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连

我们看一下一般我们的系统中都 注册了哪些总线:

总线由 bus_type 结构表示, 定义在 <linux/device.h>

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 (*resume)(struct device *dev);//恢复时调用

	const struct dev_pm_ops *pm;// 设备电源管理

	struct bus_type_private *p;//私有数据。完全由驱动核心初始化并使用。
};

struct bus_type_private {
	struct kset subsys;;/*与该总线相关的子系统*/
	struct kset *drivers_kset;;/*总线驱动程序的kset*/
	struct kset *devices_kset;;/* 挂在该总线的所有设备的kset*/
	struct klist klist_devices;/*挂接在该总线的设备链表*/
	struct klist klist_drivers;/*与该总线相关的驱动程序链表*/
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;/*处理热插拔、电源管理、探测和移除等事件的方法*/
	struct bus_type *bus;
};
linux设备驱动模型一上层容器之关系中我们看到了bus,devices,driver三者之间的关系,这里可以从上面的结构中看出。

看下三个默认属性的结构:

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

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

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_attrs表示默认的总线属性,dev_attrs表示默认的设备属性,对于每个将要注册到该总线上的设备,在设备注册时,默认添加dev_attrs数组指定的属性。drv_attrs默认驱动属性。对于每个将要注册到该总线上的驱动,在驱动注册时,默认添加drv_attrs数组指定的属性,注意这三个结构的show,store的参数是不一样的。

match匹配函数。
判定设备和驱动是否匹配,是总线体系相关的。驱动核心通过match和probe两个函数来完成匹配每当有设备添加到总线时,驱动核心遍历总线上的驱动链表(执行流程device_register->device_add->bus_probe_device->device_attach->bus_for_each_drv->match/probe)查找设备驱动;每当有驱动添加到总线时,驱动核心遍历总线上的设备链表(执行流程driver_register->bus_add_driver->driver_attach->bus_for_each_dev->match/probe)查找驱动可操控的设备。当前,match一般只执行总线特定的匹配处理,而在probe中,通过回调设备驱动probe,完成设备特定的匹配、设备初始化等。举例来说,对于PCI总线,match判断驱动支持的ID列表是否包含设备ID,如果包含则匹配,否则失配;probe会再次执行ID匹配判断,并回调驱动提供的probe函数。
match匹配成功则返回1,失配返回0。match函数指针为NULL,驱动核心返回1。
对于一次遍历匹配而言,如果match和probe均成功,则结束匹配成功;如果match成功而probe失配,继续遍历查找匹配;如果遍历结束而没有找到成功的匹配,对于驱动而言表示没有可操控设备,对于设备而言表示没有适当的驱动。

probe探测函数。
如在match中所述,probe执行设备相关的匹配探测、设备初始化、资源分配等。
需要注意,在probe调用时,dev->driver已经被设置为match成功匹配的驱动指针了,因此不再需要struct device_driver指针。

remove 移除设备。
设备移除时,调用该方法,完成部分清理工作。如删除设备驱动中,设备链表下的该设备。

shutdown系统关机
系统关机时,调用该方法关闭设备。
suspend设备休眠(挂起)。
设备休眠(挂起)时调用该方法。一般在该方法中设置设备为低耗电状态。
resume设备恢复。
设备从休眠中恢复时调用该方法。
pm电源管理。
一些设备有电源状态转换。结构体内部提供很多方法实现这个过程
p总线私有数据。
驱动核心设置并使用,总线驱动不必关心这个成员,并且一般不要去修改它。

总线注册通过调用bus_register

int bus_register(struct bus_type *bus)
{
	int retval;
	struct bus_type_private *priv;

	priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); //分配私有存储空间
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;//私有成员的bus回指该bus
	bus->p = priv; //初始化bus->p,即其私有属性

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
	
	//.由于struct bus_type也要在sysfs文件中表示一个节点,因此,它也内嵌也一个kset的结构.这就是priv->subsys. 
	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);//kset的名称赋值为bus的名称
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset; //设置总线子系统kobject的所属集合,其实对应sys/bus/ 目录
	priv->subsys.kobj.ktype = &bus_ktype;  //属性操作级别统一为bus_ktype
	priv->drivers_autoprobe = 1; //设置该标志,当有driver注册时,会自动匹配devices上的设备并用probe初始化
									//当有device注册时也同样找到  driver并会初始化
									
	retval = kset_register(&priv->subsys);//注册kset,创建目录结构,以及层次关系
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);//当前bus目录下生成bus_attr_uevent属性文件.
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj); //初始化bus目录下的devices目录,里面级联了该bus下设备,仍然以kset为原型
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);//初始化bus目录下的drivers目录,里面级联了该bus下设备的driver
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  //初始化klist_devices里的操作函数成员
	klist_init(&priv->klist_drivers, NULL, NULL); //klist_drivers里的操作函数置空

	retval = add_probe_files(bus);//增加bus_attr_drivers_probe和bus_attr_drivers_autoprobe
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);//增加默认的属性文件
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:					 //以下为错误处理
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
	kfree(bus->p);
out:
	bus->p = NULL;
	return retval;
}

其中的bus_type是在该bus初始化的时候创建的

int __init buses_init(void)
{
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;
	return 0;
}

再看一下bus_add_attrs

static int bus_add_attrs(struct bus_type *bus)
{
	int error = 0;
	int i;

	if (bus->bus_attrs) {
		for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
			error = bus_create_file(bus, &bus->bus_attrs[i]);
			if (error)
				goto err;
		}
	}
done:
	return error;
err:
	while (--i >= 0)
		bus_remove_file(bus, &bus->bus_attrs[i]);
	goto done;
}

这里主要是添加该bus默认的一些属性。


register流程图:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值