总线 设备 驱动
1.总线是物理总线和的抽象,虚拟总线的描述
2.设备驱动模型中驱动程序依附在总线上
在上一节说到sys下每一个目录都由kobject映射
sys目录下有一个bus目录,所有总线都保存在bus下,一般一个总线目录下都会有一些设备目录和驱动目录(kobject)以及一些总线属性文件(ktype)
设备目录中包含挂接在该总线上的设备
驱动目录包含挂接在该总线上的驱动程序,都是使用链表去组织
设备和驱动程序之间通过指针相互联系

在设备驱动模型中 总线使用bus_type描述。只要有一条新总线,就需要由bus_type结构实例化出一个对象出来:
源码地址:/include/linux/device/bus.h
name对应总线的名称
bus_attrs, dev_attrs drv_attrs对应则上一节中的attribute类型的变量,与他类似,但是包含了更定制化的信息。
probe ---- 探测设备函数
match ----匹配函数,检验参数 2 中的驱动是否支持参数 1 中的设备
uevent,remove,suspend,resume都是电源管理相关的函数
在linux系统中电源管理一直都是非常重要的部分 省电模式模式下系统的设备以一定的先后顺序挂起
全速工作模式下系统中的设备以一定的先后顺序恢复运行 一条总线上所有设备都挂起时总线才会挂起 一条总线上有任意一个设备要恢复之前总线必须恢复
由于这个管理功能对于大多模块都是必须的,所以将他抽象到更深的层次上
dev_pm_ops *pm是关于电源管理的操作符
bus_type_private表示总线私有数据
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
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);
void (*sync_state)(struct device *dev);
void (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*num_vf)(struct device *dev);
int (*dma_configure)(struct device *dev);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
bool need_parent_lock;
};
使用bus_type实例化对象时,就是对物理总线进行抽象,也是对虚拟总线进行描述的过程
(大名鼎鼎的platform平台设备总线就是虚拟总线,往后会介绍)
bus_type的实例化十分简单,因为有很多成员使用不到,ac97声卡的总线定义:
struct bus_type ac97_bus_type = {
.name = "ac97",
.match = ac97_bus_match,
#ifdef CONFIG_PM
.suspend = ac97_bus_suspend,
.resume = ac97_bus_resume,
#endif /* CONFIG_PM */
};
使用bus_type_private实例化对象时,最主要的是填写前三个kset变量的信息:
subsys代表该 bus 子系统,里面的 kobj 是该 bus 的主kobj,也就是最顶层
drivers_kset指向挂接到该总线上的所有驱动集合
devices_kset;挂接到该总线上的所有设备集合
经过对上面两个重要结构体的填写,已经基本具备了总线驱动设备模型的框架
此时只需要对总线进行注册:bus_register()
bus_register()函数对 bus_type 进行注册,当从系统中删除一条总线时,应该使用
bus_unregister()函数。
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->p = priv; /*总线的私有数据*/
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);/*初始化通知链表*/
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
/*设置总线的名字,例如 PCI*/
if (retval) /*失败则返回*/
goto out;
priv->subsys.kobj.kset = bus_kset;
/*指向其父 kset,bus_kset 在 buses_init()例程中添加*/
priv->subsys.kobj.ktype = &bus_ktype; /*设置读取总线属性文件的默认方法*/
priv->drivers_autoprobe = 1; /*驱动程序注册时,可以探测(probe)设备*/
retval = kset_register(&priv->subsys); /*注册总线容器 priv->subsys*/
if (retval) /*失败返回*/
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
/*建立 uevent 属性文件*/
if (retval) /**/
goto bus_uevent_fail;
/*创建一个 devices_kset 容器。也就是在新的总线目录下创建一个 devices 的目录,其
父目录就是 priv->subsys.kobj 对应的总线目录*/
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) { /*创建失败则返回*/
retval = -ENOMEM;
goto bus_devices_fail;
}
/*创建一个 drivers_kset 容器。也就是在新的总线目录下创建一个 drivers 的目录,其
父目录就是 priv->subsys.kobj 对应的总线目录*/
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) { /*创建失败则返回*/
retval = -ENOMEM;
goto bus_drivers_fail;
}
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_
put); /*初始化设备链表*/
klist_init(&priv->klist_drivers, NULL, NULL); /*初始化驱动程序链表*/
retval = add_probe_files(bus); /*与热插拔相关的探测文件*/
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:
return retval;
}