1. sysfs中的bus/class/device/driver
首先通过实例来看bus/class/device/driver的关系。下图绘制的是qemu启动ARM64后,sysfs中platform总线下部分目录和文件的关系。
框图说明:
- 每个方框表示sysfs中的一个目录或文件
- 实线由父目录指向子目录
- 虚线由软链接指向实际的文件或目录
总结:
- bus_register注册的bus会在/sys/bus目录下生成子目录。
- device_add添加的device会在/sys/devices下生成子目录。
- 注册的driver会在/sys/bus所属的总线目录(如platform)下的drivers生成子目录。
- class_register注册的class会在/sys/class下生成子目录。
- driver有正在由其驱动的device,driver对应的目录下会有device的链接。
- device需要有驱动自身的driver,device目录下会有名为driver的文件链接到driver目录。
- device需要挂在bus下,/sys/bus下device所属的总线目录(如platform)下的devices会有对应device目录的链接。
- device会有所属的class,/sys/class具体的class目录下,也会有device的连接。
- 同是挂在platform总线下的不同设备,可以属于不同的class。
2. 设备模型的初始化
1/// drivers/base/init.c
2/**
3 * driver_init - initialize driver model.
4 *
5 * Call the driver model init functions to initialize their
6 * subsystems. Called early from init/main.c.
7 */
8void __init driver_init(void)
9{
10 /* These are the core pieces */
11 bdi_init(&noop_backing_dev_info);
12 devtmpfs_init();
13 devices_init(); /// 初始化devices_kset/dev_kobj/sysfs_dev_block_kobj/sysfs_dev_char_kobj
14 buses_init(); /// 初始化bus_kset/system_kset
15 classes_init(); /// 初始化class_kset
16 firmware_init();
17 hypervisor_init();
18
19 /* These are also core pieces, but must come after the
20 * core core pieces.
21 */
22 of_core_init(); /// 初始of_kset
23 platform_bus_init(); /// 注册platform_bus和platform_bus_type
24 auxiliary_bus_init();
25 cpu_dev_init();
26 memory_dev_init();
27 node_dev_init();
28 container_dev_init();
29}
2.1. 全局变量
设备模型的初始化主要就是完成一些全局变量的初始化。
说明:
- bus_register会调用bus_create_file来为bus创建uevent文件,属性由uevent_attr指定,文件操作函数由bus_attr_uevent指定。
- bus_ktype的release函数是bus_release,sysfs_ops是bus_sysfs_ops,show和store函数分别为bus_attr_show和bus_attr_store
- dirver_ktype与bus_type类似
- bus_kset由kset_create_and_add创建,uevent_ops为bus_uevent_ops
- devices_kset由kset_create_and_add创建,uevent_ops为device_uevent_ops
- bus_kset和devices_kset的parent都为NULL
- system_kset由kset_create_and_add创建,parent为devices_kset,uevent_ops为NULL
- bus_kset、devices_kset和system_kset的ktype都是默认的kset_ktype
- dev_kobj由kobject_create_and_add,作为sysfs_dev_block_kobj和sysfs_dev_char_kobj的parent
- sysfs_dev_block_kobj和sysfs_dev_char_kobj由kobject_create_and_add创建,parent都是dev_kobj
- class的dev_kobj成员会根据需要指向sysfs_dev_block_kobj或sysfs_dev_char_kobj,或者是自定义的kobject
3. bus/class/device/driver详解
通过《sysfs中的bus/class/device/driver》可以简单了解bus/class/device/driver间的关系,下面从内核源码的角度进行分析。
如下是数据结构之间关系的简要说明,详细内容见后续章节分析。
bus部分说明:
- bus_type使用subsys_private记录私有数据,subsys_private内嵌kset
- 内嵌kset的ktype指向全局变量bus_ktype
- 内嵌kset的kset指向全局变量bus_kset
- subsys_private记录kset_create_and_add创建的devices_kset和drivers_kset
- devices_kset和drivers_kset的parent指向subsys_private内嵌kset内的kobj
- subsys_private的klist_drivers记录挂在bus下的driver
- subsys_private的klist_devices记录挂在bus下的device
class部分说明:
- class使用subsys_private记录私有数据,subsys_private内嵌kset
- 内嵌kset的ktype指向全局变量class_ktype
- 内嵌kset的kset指向全局变量class_kset
- subsys_private的klist_devices记录属于该class的device
driver部分说明:
- device_driver使用driver_private记录私有数据,driver_private内嵌kobject
- 内嵌kobject的ktype指向全局变量driver_ktype
- 内嵌kobject的kset指向bus->p的drivers_kset
- driver_private的klist_devices记录与该device_driver匹配的device
device部分说明:
- device内嵌kobject
- 内嵌kobject的ktype指向全局变量device_ktype
- 内嵌kobject的kset指向全局变量devices_ktype
- device使用device_private记录私有数据
- device_private的knode_driver链接到device_driver的链表中
- device_private的knode_bus链接到bus的链表中
- device_private的knode_class链接到class的链表中
3.1. bus的注册
向系统注册总线是通过bus_register完成的,其主要工作如下:
- 将kobj的kset和ktype分别指向bus_kset和bus_ktype。
- 调用kset_register将bus对应的kset添加到系统。
- bus_create_file在sysfs中创建uevent文件,操作函数由bus_attr_uevent指定。
- 创建priv->devices_kset和priv->drivers_kset。
- 初始一些列表头和锁。
- add_probe_files向sysfs添加drivers_autoprobe和drivers_probe文件。
- bus_add_groups添加属性组。
3.2. driver的注册
driver注册时,需要指定是在哪个bus下。以platform总线为例,注册platform_driver需要调用platform_driver_register,函数内部会将bus设为platform_bus_type。
1/// include/linux/platform_device.h
2/*
3 * use a macro to avoid include chaining to get THIS_MODULE
4 */
5#define platform_driver_register(drv) \
6 __platform_driver_register(drv, THIS_MODULE)
7
8/// drivers/base/platform.c
9/**
10 * __platform_driver_register - register a driver for platform-level devices
11 * @drv: platform driver structure
12 * @owner: owning module/driver
13 */
14int __platform_driver_register(struct platform_driver *drv,
15 struct module *owner)
16{
17 drv->driver.owner = owner;
18 drv->driver.bus = &platform_bus_type;
19
20 return driver_register(&drv->driver);
21}
22EXPORT_SYMBOL_GPL(__platform_driver_register);
3.2.1. driver_register
可以看到,向系统注册驱动是由driver_register来完成的,其主要流程如下:
- 通过driver_find来查看所属bus下是否由同名驱动,如果有,则证明已经注册过,不需要再进行注册。
- bus_add_driver向bus上添加驱动
- driver_add_groups向sysfs添加driver的属性组
- kobject_uevent向用户态上报KOBJ_ADD事件
3.2.2. bus_add_driver
bus_add_driver流程如下:
- bus_get增加bus内kset的引用计数
- 申请struct driver_private数据,记录到priv,并关联priv和driver
- 关联driver和bus,
priv->kobj.kset = bus->p->drivers_kset
- kobject_init_and_add向系统添加priv->kobj,指定类型为driver_ktype
- 将driver添加到bus的列表中,
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)
- 如果bus使能了drivers_autoprobe,调用driver_attach尝试匹配driver和device
- module_add_driver向module添加driver
- driver_create_file在sysfs创建uevent
- driver_add_groups添加bus的属性组
- 如果driver没有指定suppress_bind_attrs,调用add_bind_files在sysfs创建bind/unbind文件。
3.3. device的注册
和device相关的函数主要有device_initialize和device_add,device_initialize负责device结构体的初始化,device_add负责向系统添加设备,并与bus/driver/class关联。
3.3.1. device_add
device_add主要流程如下:
- get_device增加device内嵌kobject的引用计数,防止操作过程中设备被删除
- device_private_init创建device私有数据并初始化,记录到dev->p
- 设置名字
- get_device(dev->parent)增加父设备的引用计数
- get_device_parent找到父设备的kobject,如果不为空,则赋值给dev->kobj.parent
kobject_add(&dev->kobj, dev->kobj.parent, NULL)
向系统添加dev->kobj,parent为dev->kobj.parent- device_platform_notify完成一些通知工作
- device_create_file在sysfs创建uevent文件
- device_add_class_symlinks创建sysfs中device和class的的链接文件
- device_add_attrs添加属性组,包括class/device_type等
- 调用bus_add_device
- dpm_sysfs_add向sysfs添加device power manage相关文件
- device_pm_add根据需要决定是否将设备添加到dpm_list链表
- 如果dev->devt的MAJOR不为0,向sysfs添加dev文件,并创建devtmpfs节点
- 如果device挂载在bus下,blocking_notifier_call_chain向通知链发送BUS_NOTIFY_ADD_DEVICE通知
- kobject_uevent向用户态报告KOBJ_ADD
- 关联device和fwnode
- bus_probe_device,Automatically probe for a driver if the bus allows it
- 如果device有父设备,将device添加到父设备的链表中
- 如果device有所属的class,则关联device和class,见后续章节。
- put_device减小device的引用计数
3.3.2. bus_add_device
如果device有所属的bus,则执行如下操作:
- 增加bus的引用计数
- 向sysfs添加bus的属性组
- 在sysfs创建bus->p->devices_kset->kobj和dev->kobj的链接
- 在sysfs创建dev->kobj和dev->bus->p->subsys.kobj的链接
- 将device添加到bus的链表中
3.4. class的注册以及device和class的关联
3.4.1. class的注册
1/// include/linux/device/class.h
2/* This is a #define to keep the compiler from merging different
3 * instances of the __key variable */
4#define class_register(class) \
5({ \
6 static struct lock_class_key __key; \
7 __class_register(class, &__key); \
8})
__class_register
负责向系统注册class,其主要流程如下:
- 初始化class的设备列表klist_devices和interfaces列表
- 初始化mutex
- 设置class名
- 如果没有指定cls->dev_kobj,将其设置为sysfs_dev_char_kobj
- 根据需要,设置class的kset为class_kset
- 设置class的ktype为class_ktype
- kset_register注册cls->p->subsys
- class_add_groups在sysfs创建属性组
- class_put减小cls->p->subsys的引用计数,kset_register时引用计数为1,此时将其减为0
3.4.2. device和class的关联
如果device有所属的class,device_add会完成device和class的关联:
- 将设备添加到class的链表中
- 如果class有class_interface,遍历class->p->interfaces,调用class_intf->add_dev
在实际编码中,由如下两种方式来实现device和class的关联
- device_create时,指定class
- 创建device后,手动指定dev->class,然后调用device_register或device_add