说明
在linux中有很多总线,其中有和实体对应的总线,比如:media 总线、spi 总线、i2c总线、hid 输入子系统总线、eMMC 存储设备总线。也有虚拟出来的总线,比如:platform虚拟平台总线。以platform总线为例,它的灵魂是:device(设备)driver(驱动)platform_bus(platform总线),它的特点是设备,驱动分层动态的管理和加载,当我们将设备和驱动注册到虚拟总线上(内核)时,在他们注册时,会互相寻找一次对方。如果该设备是该驱动的设备,该驱动是该设备的驱动,那么驱动中的probe函数就会被调用,从而整个驱动就会运行起来,来驱动这个设备的运行。
数据结构
我们知道了总线的基本原理和作用后,先来看看代表它的数据结构:
struct bus_type {
const char *name; //bus的名字
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;
/* 这个函数是核心函数,它的参数分别是device和driver,作用就是判断
* dev和drv是否匹配,匹配的方式也有好几种。
*/
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 (*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);
const struct dev_pm_ops *pm;
const struct iommu_ops *iommu_ops;
struct subsys_private *p; //私有数据结构
struct lock_class_key lock_key;
};
可以先只关注name、match、probe、p这几个成员,其它的暂时忽略。
struct subsys_private {
struct kset subsys; //对应于/sys下的bus目录
struct kset *devices_kset; //对应于bus目录下的devices目录
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; //对应于bus目录下的drivers目录
/* 管理着注册到这个类型总线上的所有device */
struct klist klist_devices;
/* 管理着注册到这个类型总线上的所有driver */
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
从上面的结构体成员klist_devices、klist_drivers大概就能知道bus是怎么在管理设备和驱动了。设备注册和驱动注册的部分工作就是分别将设备和驱动添加到这两个链表上来。从这两个结构体也可以看出,系统中的bus之间是没有关联的(没有管理链表成员)。
总线注册
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
/* 分配内存 */
priv = kzalloc(sizeof(struct subsys_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);
/* 赋值 */
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
/* 创建bus目录 */
retval = bus_create_file(bus, &bus_attr_uevent);
/* 接着在其目录下创建devices目录 */
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
/*再在其目录下创建drivers目录 */
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
/* 初始化总线上的设备和驱动链表 */
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
/* 创建probe文件 */
retval = add_probe_files(bus);
retval = bus_add_groups(bus, bus->bus_groups);
..
}
大家如果有兴趣浏览下上面的代码,省略了部分代码。如果在掌握了sysfs文件系统相关的之后,看起来比较容易。接着我们就该关注match()函数了。如果设备和驱动成功match了,那么就会调用probe()函数。这个过程我们在后面的两篇文章里讲。到此总线的基本功能就实现了。这部分内容这样一看还是比较简单的。
另外需要注意的是一个驱动可以匹配多个设备,但是一个设备不能匹配多个驱动。为什么呢?其实道理很简单,如果一个设备匹配了多个驱动,那么一个设备受到多个不同的驱动控制,那不是乱套了?
查看总线
总线查看命令:
#:ls /sys/bus/
从下图中也可以确认