1. sysfs中的bus/class/device/driver

首先通过实例来看bus/class/device/driver的关系。下图绘制的是qemu启动ARM64后,sysfs中platform总线下部分目录和文件的关系。

bus_driver_device_class

框图说明:

  1. 每个方框表示sysfs中的一个目录或文件
  2. 实线由父目录指向子目录
  3. 虚线由软链接指向实际的文件或目录

总结:

  1. bus_register注册的bus会在/sys/bus目录下生成子目录。
  2. device_add添加的device会在/sys/devices下生成子目录。
  3. 注册的driver会在/sys/bus所属的总线目录(如platform)下的drivers生成子目录。
  4. class_register注册的class会在/sys/class下生成子目录。
  5. driver有正在由其驱动的device,driver对应的目录下会有device的链接。
  6. device需要有驱动自身的driver,device目录下会有名为driver的文件链接到driver目录。
  7. device需要挂在bus下,/sys/bus下device所属的总线目录(如platform)下的devices会有对应device目录的链接。
  8. device会有所属的class,/sys/class具体的class目录下,也会有device的连接。
  9. 同是挂在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. 全局变量

设备模型的初始化主要就是完成一些全局变量的初始化。

device_global_data

说明:

  1. bus_register会调用bus_create_file来为bus创建uevent文件,属性由uevent_attr指定,文件操作函数由bus_attr_uevent指定。
  2. bus_ktype的release函数是bus_release,sysfs_ops是bus_sysfs_ops,show和store函数分别为bus_attr_show和bus_attr_store
  3. dirver_ktype与bus_type类似
  4. bus_kset由kset_create_and_add创建,uevent_ops为bus_uevent_ops
  5. devices_kset由kset_create_and_add创建,uevent_ops为device_uevent_ops
  6. bus_kset和devices_kset的parent都为NULL
  7. system_kset由kset_create_and_add创建,parent为devices_kset,uevent_ops为NULL
  8. bus_kset、devices_kset和system_kset的ktype都是默认的kset_ktype
  9. dev_kobj由kobject_create_and_add,作为sysfs_dev_block_kobj和sysfs_dev_char_kobj的parent
  10. sysfs_dev_block_kobj和sysfs_dev_char_kobj由kobject_create_and_add创建,parent都是dev_kobj
  11. 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间的关系,下面从内核源码的角度进行分析。

device_model

如下是数据结构之间关系的简要说明,详细内容见后续章节分析。

bus部分说明:

  1. bus_type使用subsys_private记录私有数据,subsys_private内嵌kset
  2. 内嵌kset的ktype指向全局变量bus_ktype
  3. 内嵌kset的kset指向全局变量bus_kset
  4. subsys_private记录kset_create_and_add创建的devices_kset和drivers_kset
  5. devices_kset和drivers_kset的parent指向subsys_private内嵌kset内的kobj
  6. subsys_private的klist_drivers记录挂在bus下的driver
  7. subsys_private的klist_devices记录挂在bus下的device

class部分说明:

  1. class使用subsys_private记录私有数据,subsys_private内嵌kset
  2. 内嵌kset的ktype指向全局变量class_ktype
  3. 内嵌kset的kset指向全局变量class_kset
  4. subsys_private的klist_devices记录属于该class的device

driver部分说明:

  1. device_driver使用driver_private记录私有数据,driver_private内嵌kobject
  2. 内嵌kobject的ktype指向全局变量driver_ktype
  3. 内嵌kobject的kset指向bus->p的drivers_kset
  4. driver_private的klist_devices记录与该device_driver匹配的device

device部分说明:

  1. device内嵌kobject
  2. 内嵌kobject的ktype指向全局变量device_ktype
  3. 内嵌kobject的kset指向全局变量devices_ktype
  4. device使用device_private记录私有数据
  5. device_private的knode_driver链接到device_driver的链表中
  6. device_private的knode_bus链接到bus的链表中
  7. device_private的knode_class链接到class的链表中

3.1. bus的注册

向系统注册总线是通过bus_register完成的,其主要工作如下:

  1. 将kobj的kset和ktype分别指向bus_kset和bus_ktype。
  2. 调用kset_register将bus对应的kset添加到系统。
  3. bus_create_file在sysfs中创建uevent文件,操作函数由bus_attr_uevent指定。
  4. 创建priv->devices_kset和priv->drivers_kset。
  5. 初始一些列表头和锁。
  6. add_probe_files向sysfs添加drivers_autoprobe和drivers_probe文件。
  7. 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来完成的,其主要流程如下:

  1. 通过driver_find来查看所属bus下是否由同名驱动,如果有,则证明已经注册过,不需要再进行注册。
  2. bus_add_driver向bus上添加驱动
  3. driver_add_groups向sysfs添加driver的属性组
  4. kobject_uevent向用户态上报KOBJ_ADD事件

3.2.2. bus_add_driver

bus_add_driver流程如下:

  1. bus_get增加bus内kset的引用计数
  2. 申请struct driver_private数据,记录到priv,并关联priv和driver
  3. 关联driver和bus,priv->kobj.kset = bus->p->drivers_kset
  4. kobject_init_and_add向系统添加priv->kobj,指定类型为driver_ktype
  5. 将driver添加到bus的列表中,klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers)
  6. 如果bus使能了drivers_autoprobe,调用driver_attach尝试匹配driver和device
  7. module_add_driver向module添加driver
  8. driver_create_file在sysfs创建uevent
  9. driver_add_groups添加bus的属性组
  10. 如果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主要流程如下:

  1. get_device增加device内嵌kobject的引用计数,防止操作过程中设备被删除
  2. device_private_init创建device私有数据并初始化,记录到dev->p
  3. 设置名字
  4. get_device(dev->parent)增加父设备的引用计数
  5. get_device_parent找到父设备的kobject,如果不为空,则赋值给dev->kobj.parent
  6. kobject_add(&dev->kobj, dev->kobj.parent, NULL)向系统添加dev->kobj,parent为dev->kobj.parent
  7. device_platform_notify完成一些通知工作
  8. device_create_file在sysfs创建uevent文件
  9. device_add_class_symlinks创建sysfs中device和class的的链接文件
  10. device_add_attrs添加属性组,包括class/device_type等
  11. 调用bus_add_device
  12. dpm_sysfs_add向sysfs添加device power manage相关文件
  13. device_pm_add根据需要决定是否将设备添加到dpm_list链表
  14. 如果dev->devt的MAJOR不为0,向sysfs添加dev文件,并创建devtmpfs节点
  15. 如果device挂载在bus下,blocking_notifier_call_chain向通知链发送BUS_NOTIFY_ADD_DEVICE通知
  16. kobject_uevent向用户态报告KOBJ_ADD
  17. 关联device和fwnode
  18. bus_probe_device,Automatically probe for a driver if the bus allows it
  19. 如果device有父设备,将device添加到父设备的链表中
  20. 如果device有所属的class,则关联device和class,见后续章节。
  21. put_device减小device的引用计数

3.3.2. bus_add_device

如果device有所属的bus,则执行如下操作:

  1. 增加bus的引用计数
  2. 向sysfs添加bus的属性组
  3. 在sysfs创建bus->p->devices_kset->kobj和dev->kobj的链接
  4. 在sysfs创建dev->kobj和dev->bus->p->subsys.kobj的链接
  5. 将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,其主要流程如下:

  1. 初始化class的设备列表klist_devices和interfaces列表
  2. 初始化mutex
  3. 设置class名
  4. 如果没有指定cls->dev_kobj,将其设置为sysfs_dev_char_kobj
  5. 根据需要,设置class的kset为class_kset
  6. 设置class的ktype为class_ktype
  7. kset_register注册cls->p->subsys
  8. class_add_groups在sysfs创建属性组
  9. class_put减小cls->p->subsys的引用计数,kset_register时引用计数为1,此时将其减为0

3.4.2. device和class的关联

如果device有所属的class,device_add会完成device和class的关联:

  1. 将设备添加到class的链表中
  2. 如果class有class_interface,遍历class->p->interfaces,调用class_intf->add_dev

在实际编码中,由如下两种方式来实现device和class的关联

  1. device_create时,指定class
  2. 创建device后,手动指定dev->class,然后调用device_register或device_add