总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连
我们看一下一般我们的系统中都 注册了哪些总线:
总线由 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流程图: