Linux设备驱动模型简述(源码剖析)
1. Linux设备驱动模型和sysfs文件系统
Linux内核在2.6版本中引入设备驱动模型,简化了驱动程序的编写。Linux设备驱动模型包含设备(device)、总线(bus)、类(class)和驱动(driver),它们之间相互关联。其中设备(device)和驱动(driver)通过总线(bus)绑定在一起。
Linux内核中,分别用 bus_type、 device_driver和 device结构来描述总线、驱动和设备,结构体定义详见 linux/device.h。设备和对应的驱动必须依附于同一种总线,因此 device_driver和 device结构中都包含 struct bus_type指针。
Linux sysfs 是一个虚拟的文件系统,它把连接在系统上的设备和总线组织成为一个分级的文件,可以由用户空间存取,向用户空间导出内核数据结构以及它们的属性。
sysfs展示出设备驱动模型中各个组件的层次关系,某个系统上的sysfs顶层目录展示如下:
1 /sys$ ll 2 total 0 3 drwxr-xr-x 2 root root 0 Aug 20 15:27 block/ 4 drwxr-xr-x 29 root root 0 Aug 20 15:27 bus/ 5 drwxr-xr-x 61 root root 0 Aug 20 15:27 class/ 6 drwxr-xr-x 4 root root 0 Aug 20 15:27 dev/ 7 drwxr-xr-x 14 root root 0 Aug 20 15:27 devices/ 8 drwxr-xr-x 4 root root 0 Aug 20 15:27 firmware/ 9 drwxr-xr-x 8 root root 0 Aug 20 15:27 fs/ 10 drwxr-xr-x 2 root root 0 Sep 2 17:08 hypervisor/ 11 drwxr-xr-x 8 root root 0 Aug 20 15:27 kernel/ 12 drwxr-xr-x 147 root root 0 Aug 20 15:27 module/ 13 drwxr-xr-x 2 root root 0 Aug 20 15:27 power/
重要子目录介绍:
- block: 包含所有的块设备,如ram,sda等
- bus: 包含系统中所有的总线类型,如pci,usb,i2c等
- class: 包含系统中的设备类型,如input,pci_bus,mmc_host等
- dev: 包含两个子目录:char和block,分别存放字符设备和块设备的主次设备号(major:minor),指向 /sys/devices 目录下的设备
- devices: 包含系统所有的设备
sysfs中显示的每一个对象都对应一个 kobject结构(完整定义位于 linux/kobject.h ,结构内部包含一个 parent 指针),而另一个相联系的结构为 kset 。 kset 是嵌入相同类型结构的 kobject 对象的集合。 内核用 kobject 、 kset 和 parent 之间的关系将各个对象连接起来组成一个分层的结构体系,从而与模型化的子系统相匹配。(有机会详细介绍)
sysfs中能清晰地看出 device 、 driver 和 bus 的相互联系,以某系统上PCI总线上的igb驱动为例。 /sys/bus/pci/ 下存在 devices 和 drivers 两个目录,分别包含了依附于PCI总线上的设备和驱动。进入igb驱动目录,可以发现存在指向设备的链接。
1 /sys/bus/pci/drivers/igb$ ll 2 total 0 3 ... 0 Sep 2 17:08 0000:07:00.0 -> ../../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.0/ 4 ... 0 Sep 2 17:08 0000:07:00.1 -> ../../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.1/ 5 ...
对应地,在 /sys/devices/ 目录下,可以看到设备存在一个指向igb的 driver 项:
1 /sys/devices/pci0000:00/0000:00:1c.4/0000:07:00.0$ ll 2 total 0 3 ... 4 lrwxrwxrwx 1 root root 0 Aug 20 15:27 driver -> ../../../../bus/pci/drivers/igb/ 5 ...
同样地, /sys/bus/pci/devices 目录下可以找到指向同样设备的一个链接:
1 /sys/bus/pci/devices$ ll 2 total 0 3 ... 4 ... 0 Aug 20 15:27 0000:07:00.0 -> ../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.0/ 5 ... 0 Aug 20 15:27 0000:07:00.1 -> ../../../devices/pci0000:00/0000:00:1c.4/0000:07:00.1/ 6 ...
对于早期的Linux内核(2.6版本以前)来说,通常在驱动代码中xxx_driver
注册过程中调用 probe() 函数来对设备进行初始化。
引入Linux设备驱动模型下,设备和驱动可以分开注册,依赖总线完成相互绑定。系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,系统每注册一个驱动的时候,会寻找与之匹配的设备。这个过程中,设备和驱动的匹配工作由总线完成。
下文中将会用关键的内核源码(基于Linux 5.2.14 Kernel)说明驱动和设备间匹配机制的实现,分析的过程中以platform总线为例。【如果只想了解大致过程,可提前转至最后一节:总结】
platform总线是一种虚拟的总线,与之相对应的是PCI、I2C、SPI等实体总线。引入虚拟platform总线是为了解决某些设备无法直接依附在现有实体总线上的问题,例如SoC系统中集成的独立外设控制器,挂接在SoC内存空间的外设等等。
2. platform总线的注册
platform总线作为Linux的基础总线,在内核启动阶段便完成了注册,注册的入口函数为 platform_bus_init() 。内核启动阶段调用该函数的路径为:
1 start_kernel() --> arch_call_rest_init()[last step in start_kernel] 2 --> rest_init() --> kernel_init() 3 --> kernel_init_freeable() --> do_basic_setup() 4 --> driver_init() --> platform_bus_init()
Linux内核中定义了 platform_bus_type 结构体来描述platform总线,同时也定义了设备 platform_bus ,用于管理所有挂载在platform总线下的设备,定义如下:
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_groups = platform_dev_groups, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .dma_configure = platform_dma_configure, 7 .pm = &platform_dev_pm_ops, 8 }; 9 10 struct device platform_bus = { 11 .init_name = "platform", 12 };
platform_bus_init()对 platform 总线的注册主要分为两步:
device_register(&platform_bus)
bus_register(&platform_bus_type)
1 int __init platform_bus_init(void) 2 { 3 int error; 4 5 /* Clear up early_platform_device_list, then only remain head_list */ 6 early_platform_cleanup(); 7 8 /* register platform_bus device (platform_bus is also regarded as a device) */ 9 error = device_register(&platform_bus); 10 if (error) { 11 put_device(&platform_bus); 12 return error; 13 } 14 /* Main process to register platform_bus */ 15 error = bus_register(&platform_bus_type); 16 if (error) 17 device_unregister(&platform_bus); 18 of_platform_register_reconfig_notifier(); 19 return error; 20 }
2.1 device_register(&platform_bus)
1 /***** drivers/base/core.c *****/ 2 int device_register(struct device *dev) 3 { 4 device_initialize(dev); // init device structure 5 return device_add(dev); // add device to device hierarchy 6 }
- device_initialize() :对 struct device 中基本成员进行初始化,包括 kobject 、 struct device_private 、 struct mutex 等。
- device_add(dev) :将platform总线也作为一个设备 platform_bus 注册到驱动模型中,重要的函数包括 device_create_file() 、 device_add_class_symlinks() 、 bus_add_device() 、 bus_probe_device() 等,下文中对设备注册的介绍一节,将对这个函数做更详细的介绍。 device_add(&platform_bus) 主要功能是完成 /sys/devices/platform 目录的建立。
2.2 bus_register(&platform_bus_type)
1 /***** drivers/base/bus.c *****/ 2 int bus_register(struct bus_type *bus) 3 { 4 struct subsys_private *priv; 5 struct lock_class_key *key = &bus->lock_key; 6 7 priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); 8 9 priv->bus = bus; 10 bus->p = priv; 11 12 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); 13 14 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); 15 if (retval) 16 goto out; 17 18 priv->subsys.kobj.kset = bus_kset; 19 priv->subsys.kobj.ktype = &bus_ktype; 20 priv->drivers_autoprobe = 1; 21 22 /* Register kset (subsys) */ 23 retval = kset_register(&priv->subsys); 24 25 retval = bus_create_file(bus, &bus_attr_uevent); 26 27 /* Setup "devices" and "drivers" subfolder under "platform" */ 28 priv->devices_kset = kset_create_and_add("devices", NULL, 29 &priv->subsys.kobj); 30 31 priv->drivers_kset = kset_create_and_add("drivers", NULL, 32 &priv->subsys.kobj); 33 34 INIT_LIST_HEAD(&priv->interfaces); 35 __mutex_init(&priv->mutex, "subsys mutex", key); 36 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 37 klist_init(&priv->klist_drivers, NULL, NULL); 38 39 /* bus_create_file(bus, &bus_attr_drivers_probe); BUS_ATTR_WO(drivers_probe) 40 * bus_create_file(bus, &bus_attr_drivers_autoprobe); BUS_ATTR_RW(drivers_autoprobe) 41 * Add two attribute files for current bus /sys/bus/platform 42 */ 43 retval = add_probe_files(bus); 44 45 retval = bus_add_groups(bus, bus->bus_groups); 46 47 return 0; 48 }
bus_register(&platform_bus_type) 将总线 platform 注册到 Linux 的总线系统中,主要完成了 subsystem 的注册,对 struct subsys_private 结构进行了初始化,具体包括:
- platform_bus_type->p->drivers_autoprobe = 1
- 对 struct kset 类型成员sysfs进行初始化,作为子系统中 kobject 对象的 parent 。 kset 本身也包含 kobject 对象,在sysfs中也表现为一个目录,即 /sys/bus/platform 。
- 建立 struct kset 类型的 drivers_kset 和 devices_kset ,作为总线下挂载的所有驱动和设备的集合,sysfs中表现为 /sys/bus/platform/drivers 和 /sys/bus/platform/devices 。
- 初始化链表 klist_drivers 和 klist_devices ,将总线下的驱动和设备分别链接在一起。
- 增加 probe 文件,对应 /sys/bus/platform 目录的文件 drivers_autoprobe 和 drivers_probe 。
注册完成后 platform_bus_type 结构重要的成员列举如下:
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_groups = platform_dev_groups, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .dma_configure = platform_dma_configure, 7 .pm = &platform_dev_pm_ops, 8 .p (struct subsys_private) = { 9 .bus = &platform_bus_type, 10 .subsys (struct kset) = { 11 .kobj = { 12 .name = “platform” 13 .kref->refcount->refs = 1, // kset_init() 14 INIT_LIST_HEAD(.entry), 15 .state_in_sysfs = 0, 16 .state_add_uevent_sent = 1, // kset_register() 17 .state_remove_uevent_sent = 0, 18 .state_initialized = 1, 19 .kset = bus_kset, // attached to /sys/bus/ 20 .ktype= bus_ktype, 21 22 .parent = bus_kset->kobj, 23 .sd (kernfs_node) = { // create_dir, kobject_add_internal 24 .parent = bus_kset->kobj->sd, 25 .dir.root = bus_kset->kobj->sd->dir.root, 26 .ns = NULL, 27 .priv = .kobj 28 } 29 } 30 INIT_LIST_HEAD(&k->list); 31 spin_lock_init(&k->list_lock); 32 } 33 /* key point for driver to autoprobe device, set in bus_register() */ 34 . drivers_autoprobe = 1 35 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); 36 klist_init(&priv->klist_drivers, NULL, NULL); 37 .devices_kset = kset_create_and_add("devices", NULL, &.p->subsys.kobj); 38 /* .drivers_kset = kset_create_and_add("drivers", NULL, &.p->subsys.kobj) */ 39 .drivers_kset = { 40 .kobj = { 41 .name = “drivers”, 42 .parent = &.subsys.kobj, 43 .ktype = &kset_ktype, 44 .kset = NULL, 45 46 .kref->refcount->refs = 1, // kset_init 47 INIT_LIST_HEAD(.entry), 48 .state_in_sysfs = 0, 49 .state_add_uevent_sent = 1, // kset_register 50 .state_remove_uevent_sent = 0, 51 .state_initialized = 1, 52 53 .sd = { // create_dir: /sys/bus/platform/drivers 54 /* kobject_add_internal */ 55 .parent = &.subsys.kobj.sd, 56 .dir.root = = &.subsys.kobj.sd->dir.root 57 .ns = NULL, 58 .priv = .kobj 59 } 60 } 61 INIT_LIST_HEAD(.list); 62 spin_lock_init(.list_lock); 63 .uevent_ops = NULL, 64 } 65 } 66 };
3. platform驱动的注册
Linux内核中对依赖于platform总线的驱动定义了 platform_driver 结构体,内部封装了前述的 struct device_driver 。
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 void (*shutdown)(struct platform_device *); 5 int (*suspend)(struct platform_device *, pm_message_t state); 6 int (*resume)(struct platform_device *); 7 struct device_driver driver; 8 const struct platform_device_id *id_table; 9 bool prevent_deferred_probe; 10 };
为了更好地说明 platform 驱动的注册过程,以驱动 globalfifo_driver 为实例 (完整代码), globalfifo_driver 结构成员定义如下:
1 static struct platform_driver globalfifo_driver = { 2 .driver = { 3 .name = "globalfifo_platform", 4 .owner = THIS_MODULE, 5 }, 6 .probe = globalfifo_probe, 7 .remove = globalfifo_remove, 8 };
globalfifo_driver 注册的入口函数为 platform_driver_register(&globalfifo_driver) ,具体实现为 __platform_driver_register(&globalfifo_driver, THIS_MODULE) 。
该函数会对 struct device_driver 的bus、probe、remove等回调函数进行初始化,紧接着调用 driver_register(&globalfifo_driver->driver) 。
1 /***** drivers/base/platform.c *****/ 2 /** 3 * __platform_driver_register - register a driver for platform-level devices 4 * @drv: platform driver structure 5 * @owner: owning module/driver 6 */ 7 int __platform_driver_register(struct platform_driver *drv, 8 struct module *owner) 9 { 10 drv->driver.owner = owner; 11 drv->driver.bus = &platform_bus_type; 12 drv->driver.probe = platform_drv_probe; 13 drv->driver.remove = platform_drv_remove; 14 drv->driver.shutdown = platform_drv_shutdown; 15 16 return driver_register(&drv->driver); 17 }
3.1 driver_register(&(globalfifo_driver.driver))
1 /***** drivers/base/driver.c *****/ 2 /** 3 * driver_register - register driver with bus 4 * @drv: driver to register 5 * 6 * We pass off most of the work to the bus_add_driver() call, 7 * since most of the things we have to do deal with the bus 8 * structures. 9 */ 10 int driver_register(struct device_driver *drv) 11 { 12 int ret; 13 struct device_driver *other; 14 15 if (!drv->bus->p) { 16 pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n", 17 drv->name, drv->bus->name); 18 return -EINVAL; 19 } 20 21 if ((drv->bus->probe && drv->probe) || 22 (drv->bus->remove && drv->remove) || 23 (drv->bus->shutdown && drv->shutdown)) 24 printk(KERN_WARNING "Driver '%s' needs updating - please use " 25 "bus_type methods\n", drv->name); 26 27 other = driver_find(drv->name, drv->bus); 28 29 ret = bus_add_driver(drv); 30 31 ret = driver_add_groups(drv, drv->groups); 32 33 kobject_uevent(&drv->p->kobj, KOBJ_ADD); 34 35 return ret; 36 }
driver_register(&(globalfifo_driver.driver)) 主要的工作包括:
- 确认驱动依附的总线 platform_bus 已经被注册并初始化(必要条件)。
- 对bus、probe、remove等回调函数初始化进行判断,保证总线和驱动上相应的函数只能存在一个。
- driver_find() 查找总线上是否已存在当前驱动的同名驱动。
- bus_add_driver(&(globalfifo_driver.driver)) ,将驱动注册到总线上,下文详述。
- 发起 KOBJ_ADD 类型uevent,指示驱动已经添加完成,TODO。
3.2 bus_add_driver(&(globalfifo_driver.driver))
1 /***** drivers/base/bus.c *****/ 2 /** 3 * bus_add_driver - Add a driver to the bus. 4 * @drv: driver. 5 */ 6 int bus_add_driver(struct device_driver *drv) 7 { 8 struct bus_type *bus; 9 struct driver_private *priv; 10 int error = 0; 11 12 bus = bus_get(drv->bus); 13 14 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 15 16 klist_init(&priv->klist_devices, NULL, NULL); 17 priv->driver = drv; 18 drv->p = priv; 19 priv->kobj.kset = bus->p->drivers_kset; 20 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, 21 "%s", drv->name); 22 23 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 24 25 /* Entrance to match device: try to bind driver to devices */ 26 if (drv->bus->p->drivers_autoprobe) { 27 error = driver_attach(drv); 28 } 29 module_add_driver(drv->owner, drv); 30 31 error = driver_create_file(drv, &driver_attr_uevent); 32 error = driver_add_groups(drv, bus->drv_groups); 33 34 if (!drv->suppress_bind_attrs) { 35 error = add_bind_files(drv); 36 } 37 return 0; 38 }
bus_add_driver(&(globalfifo_driver.driver)) 的主要工作包括:
- 为 struct device_driver 中结构 struct driver_private 动态分配空间,并完成后者 kobject 对象初始化。对应地,在 /sys/bus/platform/drivers 下建立目录 globalfifo_platform 。
- 初始化 klist_devices 链表,用来维护驱动相关联的设备。对应sysfs中在每个驱动目录下关联的设备。
- klist_add_tail() 将当前驱动加入到总线对应的 klist_drivers 链表中。
- 如果总线使能 drivers_autoprobe ,将调用 driver_attach() 尝试匹配设备。下文中将详述此过程。
- module_add_driver(drv->owner, drv) 通过 sysfs_create_link() ,在 globalfifo_platform 目录下新建module项指向 /sys/module/globalfifo_platform 。同时,也在 /sys/module/globalfifo_platform/ 目录下新建driver目录,建立 bus->name:drv->name链接到 /sys/bus/platform/drivers/globalfifo_platform 。
- uevent设置。
初始化后 globalfifo_driver 结构主要的成员列举如下:
1 static struct platform_driver globalfifo_driver = { 2 .driver = { 3 .name = "globalfifo_platform", 4 .owner = THIS_MODULE, 5 .bus = &platform_bus_type, 6 .probe = platform_drv_probe 7 .remove = platform_drv_remove, 8 .shutdown = platform_drv_shutdown, 9 .p (struct driver_private) = { 10 .driver = & globalfifo_driver.driver, 11 klist_init(&.klist_devices, NULL, NULL); 12 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); 13 14 .kobj = { 15 .kset = platform_bus_type->p->drivers_kset, 16 .ktype = driver_ktype, 17 18 .kref->refcount->refs = 1, // kset_init 19 INIT_LIST_HEAD(.entry), 20 .state_in_sysfs = 1, 21 .state_add_uevent_sent = 0, 22 .state_remove_uevent_sent = 0, 23 .state_initialized = 1, 24 .name = "globalfifo_platform", 25 .parent = platform_bus_type->p->drivers_kset->kobj, 26 } 27 } 28 }, 29 .probe = globalfifo_probe, 30 .remove = globalfifo_remove, 31 };
3.3 driver_attach(&(globalfifo_driver.driver))
1 /***** drivers/base/dd.c *****/ 2 /** 3 * driver_attach - try to bind driver to devices. 4 * @drv: driver. 5 */ 6 int driver_attach(struct device_driver *drv) 7 { 8 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 9 } 10 11 /***** drivers/base/bus.c *****/ 12 /** 13 * bus_for_each_dev - device iterator. 14 * @bus: bus type. 15 * @start: device to start iterating from. 16 * @data: data for the callback. 17 * @fn: function to be called for each device. 18 */ 19 int bus_for_each_dev(struct bus_type *bus, struct device *start, 20 void *data, int (*fn)(struct device *, void *)) 21 { 22 struct klist_iter i; 23 struct device *dev; 24 int error = 0; 25 26 if (!bus || !bus->p) 27 return -EINVAL; 28 29 klist_iter_init_node(&bus->p->klist_devices, &i, 30 (start ? &start->p->knode_bus : NULL)); 31 while (!error && (dev = next_device(&i))) 32 error = fn(dev, data); 33 klist_iter_exit(&i); 34 return error; 35 }
driver_attach() 函数找到驱动依附的总线信息,遍历总线上链表 klist_devices 得到当前总线上存在的设备,然后调用 __driver_attach(dev, drv) 函数,尝试将驱动和设备绑定。
__driver_attach(dev, drv) 函数包含两个主要的部分:
- driver_match_device(drv, dev) : 尝试将驱动和设备匹配,返回值指示是否能匹配。
- device_driver_attach(drv, dev) : 将驱动和设备绑定。
1 static int __driver_attach(struct device *dev, void *data) 2 { 3 struct device_driver *drv = data; 4 int ret; 5 6 /* 7 * Lock device and try to bind to it. We drop the error 8 * here and always return 0, because we need to keep trying 9 * to bind to devices and some drivers will return an error 10 * simply if it didn't support the device. 11 * 12 * driver_probe_device() will spit a warning if there 13 * is an error. 14 */ 15 ret = driver_match_device(drv, dev); 16 if (ret == 0) { 17 /* no match */ 18 return 0; 19 } else if (ret == -EPROBE_DEFER) { 20 dev_dbg(dev, "Device match requests probe deferral\n"); 21 driver_deferred_probe_add(dev); 22 } else if (ret < 0) { 23 dev_dbg(dev, "Bus failed to match device: %d", ret); 24 return ret; 25 } /* ret > 0 means positive match */ 26 27 ... ... 28 29 device_driver_attach(drv, dev); 30 return 0; 31 }
3.4 driver_match_device(drv, dev)
1 static inline int driver_match_device(struct device_driver *drv, 2 struct device *dev) 3 { 4 return drv->bus->match ? drv->bus->match(dev, drv) : 1; 5 }
driver_match_device(drv, dev) 回调 drv->bus->match() 函数,对于platform_bus为 platform_match() 。
platform_match() 函数会依次尝试如下几种方式:
- driver_override 有效时,尝试将驱动名字和 driver_override 匹配
- 基于设备树风格的匹配
- 基于 ACPI 风格的匹配
- 匹配 ID 表
- 匹配 platform_device 设备名和驱动的名字
1 /** 2 * platform_match - bind platform device to platform driver. 3 * @dev: device. 4 * @drv: driver. 5 */ 6 static int platform_match(struct device *dev, struct device_driver *drv) 7 { 8 struct platform_device *pdev = to_platform_device(dev); 9 struct platform_driver *pdrv = to_platform_driver(drv); 10 11 /* When driver_override is set, only bind to the matching driver */ 12 if (pdev->driver_override) 13 return !strcmp(pdev->driver_override, drv->name); 14 15 /* Attempt an OF style match first */ 16 if (of_driver_match_device(dev, drv)) 17 return 1; 18 19 /* Then try ACPI style match */ 20 if (acpi_driver_match_device(dev, drv)) 21 return 1; 22 23 /* Then try to match against the id table */ 24 if (pdrv->id_table) 25 return platform_match_id(pdrv->id_table, pdev) != NULL; 26 27 /* fall-back to driver name match */ 28 return (strcmp(pdev->name, drv->name) == 0); 29 }
3.5 device_driver_attach(drv, dev)
1 /** 2 * device_driver_attach - attach a specific driver to a specific device 3 * @drv: Driver to attach 4 * @dev: Device to attach it to 5 */ 6 int device_driver_attach(struct device_driver *drv, struct device *dev) 7 { 8 int ret = 0; 9 10 __device_driver_lock(dev, dev->parent); 11 12 /* 13 * If device has been removed or someone has already successfully 14 * bound a driver before us just skip the driver probe call. 15 */ 16 if (!dev->p->dead && !dev->driver) 17 ret = driver_probe_device(drv, dev); 18 19 __device_driver_unlock(dev, dev->parent); 20 21 return ret; 22 }
在驱动和设备匹配成功之后,便将驱动和设备进行绑定。调用 driver_probe_device(drv, dev) 完成此工作,进一步调用 really_probe(dev, drv) 。
1 /** 2 * driver_probe_device - attempt to bind device & driver together 3 * @drv: driver to bind a device to 4 * @dev: device to try to bind to the driver 5 */ 6 int driver_probe_device(struct device_driver *drv, struct device *dev) 7 { 8 int ret = 0; 9 10 if (!device_is_registered(dev)) 11 return -ENODEV; 12 13 ... ... 14 if (initcall_debug) 15 ret = really_probe_debug(dev, drv); 16 else 17 ret = really_probe(dev, drv); 18 ... ... 19 return ret; 20 }
3.6 really_probe(dev, drv)
1 /***** drivers/base/dd.c *****/ 2 static int really_probe(struct device *dev, struct device_driver *drv) 3 { 4 dev->driver = drv; 5 ... ... 6 driver_sysfs_add(dev); 7 ... ... 8 /* Routine to probe device */ 9 if (dev->bus->probe) { 10 ret = dev->bus->probe(dev); 11 if (ret) 12 goto probe_failed; 13 } else if (drv->probe) { 14 ret = drv->probe(dev); 15 if (ret) 16 goto probe_failed; 17 } 18 ... ... 19 driver_bound(dev); 20 }
really_probe(dev, drv) 主要完成的工作包括:
- 将设备 struct device 中 driver 指针指向 globalfifo_driver->driver 。
- driver_sysfs_add(dev) 完成sysfs中设备和驱动的链接,包括在驱动目录下建立到设备的链接,和在设备目录下建立到驱动的链接。
- 设备probe函数的调用:优先使用 platform_device->bus->probe 函数,其次使用 platform_driver->driver->probe 函数。对于 globalfifo_driver ,会回调 globalfifo_probe() ,完成设备的初始化。
- driver_bound(dev) 将设备添加到驱动维护的设备链表中,并发起 KOBJ_BIND 事件。
4. platform设备的注册
最后,对设备的注册过程进行简要梳理。
和驱动类似,Linux内核中对依赖于platform总线的设备也定义了特有的结构: platform_device ,内部封装了 struct device 结构。
1 struct platform_device { 2 const char *name; 3 int id; 4 bool id_auto; 5 struct device dev; 6 u32 num_resources; 7 struct resource *resource; 8 9 const struct platform_device_id *id_entry; 10 char *driver_override; /* Driver name to force a match */ 11 12 /* MFD cell pointer */ 13 struct mfd_cell *mfd_cell; 14 15 /* arch specific additions */ 16 struct pdev_archdata archdata; 17 };
与 globalfifo_driver 相对应,同样定义 globalfifo_device 结构体(完整代码),成员定义如下:
1 static struct platform_device globalfifo_device = { 2 .name = "globalfifo_platform", 3 .id = -1, 4 };
对设备 globalfifo_device 进行注册的入口函数为 platform_device_register(&globalfifo_device) 。
1 int platform_device_register(struct platform_device *pdev) 2 { 3 device_initialize(&pdev->dev); 4 arch_setup_pdev_archdata(pdev); 5 return platform_device_add(pdev); 6 }
其中 device_initialize(&pdev->dev) 在第一节platform_bus注册中也提到过,主要对 struct device 中基本成员进行初始化,包括 kobject 、 struct device_private 、 struct mutex 等。着重介绍 platform_device_add(pdev) 。
4.1 platform_device_add(&globalfifo_device)
1 int platform_device_add(struct platform_device *pdev) 2 { 3 int i, ret; 4 5 if (!pdev->dev.parent) 6 pdev->dev.parent = &platform_bus; 7 8 pdev->dev.bus = &platform_bus_type; 9 10 switch (pdev->id) { 11 default: 12 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 13 break; 14 case PLATFORM_DEVID_NONE: 15 dev_set_name(&pdev->dev, "%s", pdev->name); 16 break; 17 case PLATFORM_DEVID_AUTO: 18 /* 19 * Automatically allocated device ID. We mark it as such so 20 * that we remember it must be freed, and we append a suffix 21 * to avoid namespace collision with explicit IDs. 22 */ 23 ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL); 24 pdev->id = ret; 25 pdev->id_auto = true; 26 dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); 27 break; 28 } 29 for (i = 0; i < pdev->num_resources; i++) { 30 struct resource *p, *r = &pdev->resource[i]; 31 if (r->name == NULL) 32 r->name = dev_name(&pdev->dev); 33 34 p = r->parent; 35 if (!p) { 36 if (resource_type(r) == IORESOURCE_MEM) 37 p = &iomem_resource; 38 else if (resource_type(r) == IORESOURCE_IO) 39 p = &ioport_resource; 40 } 41 42 if (p) { 43 ret = insert_resource(p, r); 44 } 45 } 46 47 ret = device_add(&pdev->dev); 48 if (ret == 0) 49 return ret; 50 ... ... 51 }
platform_device_add(&globalfifo_device) 主要工作如下:
- 对 globalfifo_device.dev.parent 和 globalfifo_device->dev.bus 初始化,分别指向 platform_bus 和 platform_bus_type 。
- globalfifo_device.dev.kobj->name 初始化为 globalfifo_device.name (“globalfifo_platform”)。
- 调用 device_add(&globalfifo_device.dev) device_add(&globalfifo_device.dev) 添加设备。
4.2 device_add(&globalfifo_device.dev)
1 int device_add(struct device *dev) 2 { 3 struct device *parent; 4 struct kobject *kobj; 5 int error = -EINVAL; 6 7 /* This will incr the ref_count */ 8 dev = get_device(dev); 9 10 /* Init dev->p->device = dev */ 11 if (!dev->p) 12 error = device_private_init(dev); 13 14 /* if init_name exists, use it to initialize dev.kobj->name */ 15 if (dev->init_name) { 16 dev_set_name(dev, "%s", dev->init_name); 17 dev->init_name = NULL; 18 } 19 20 /* subsystems can specify simple device enumeration */ 21 if (!dev_name(dev) && dev->bus && dev->bus->dev_name) 22 dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); 23 24 /* Return ERROR if dev name is not specified */ 25 if (!dev_name(dev)) { 26 error = -EINVAL; 27 goto name_error; 28 } 29 30 ... ... 31 parent = get_device(dev->parent); 32 /* get_device_parent(dev, parent) --> platform_bus.kobj */ 33 kobj = get_device_parent(dev, parent); 34 if (kobj) 35 dev->kobj.parent = kobj; 36 37 ... ... 38 /* first, register with generic layer. */ 39 /* we require the name to be set before, and pass NULL */ 40 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); 41 42 /* notify platform of device entry */ 43 error = device_platform_notify(dev, KOBJ_ADD); 44 45 error = device_create_file(dev, &dev_attr_uevent); 46 47 error = device_add_class_symlinks(dev); 48 error = device_add_attrs(dev); 49 50 /* Main Entrance to add device into existing bus */ 51 error = bus_add_device(dev); 52 53 error = dpm_sysfs_add(dev); 54 device_pm_add(dev); 55 56 /* Create related node in devfs */ 57 if (MAJOR(dev->devt)) { 58 error = device_create_file(dev, &dev_attr_dev); 59 60 error = device_create_sys_dev_entry(dev); 61 62 devtmpfs_create_node(dev); 63 } 64 ... ... 65 66 kobject_uevent(&dev->kobj, KOBJ_ADD); 67 68 /* Try to find driver to bind this device */ 69 bus_probe_device(dev); 70 71 ... ... 72 73 }
主要工作如下:
- globalfifo_device.dev.kobj.parent 初始化为 &platform_bus.kobj 。
- kobject_add() 函数初始化 globalfifo_device.dev.kobj 对象,在sysfs中建立相关的目录,例如 /sys/devices/platform/globalfifo_platform 。
- bus_add_device(&globalfifo_device.dev) :将 globalfifo_device 注册到总线系统里,并建立sysfs的相关目录:总线系统中建立到设备的链接,同时也在设备目录下建立到总线的subsystem链接。
- bus_probe_device(dev) :尝试在总线上寻找可以绑定的驱动。下文详细介绍。
globalfifo_device 初步初始化后主要成员列举如下:
static struct platform_device globalfifo_device = { .name = "globalfifo_platform", .id = -1, .dev = { .parent = &platform_bus, .bus = &platform_bus_type, .p = { .device = & globalfifo_device.dev, INIT_LIST_HEAD(.klist_children->k_list), spin_lock_init(.klist_children->k_lock), .klist_children->get = klist_children_get, .klist_children->put = klist_children_put, INIT_LIST_HEAD(&.deferred_probe) }, .kobj = { .name = "globalfifo_platform", .kref->refcount->refs = 1, INIT_LIST_HEAD(.entry), .state_in_sysfs = 0, .state_add_uevent_sent = 0, .state_remove_uevent_sent = 0, .state_initialized = 1, .kset = devices_kset, .ktype = device_ktype, .name = "globalfifo_platform", .parent = & platform_bus.kobj, .sd = { //create_dir: /sys/devices/platform/globalfifo_platform .parent = platform_bus.kobj.sd, .dir.root = platform_bus.kobj.sd->dir.root, .ns = NULL, .priv = .kobj } }, INIT_LIST_HEAD(.dma_pools), Mutex_init(.mutex), spin_lock_init(.devres_lock), INIT_LIST_HEAD(&dev->devres_head), device_pm_init(.), .numa_node = -1, INIT_LIST_HEAD(&dev->msi_list), INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); dev->links.status = DL_DEV_NO_DRIVER; }, };
4.3 bus_probe_device(&globalfifo_device.dev)
1 /***** drivers/base/bus.c *****/ 2 /** 3 * bus_probe_device - probe drivers for a new device 4 * @dev: device to probe 5 * 6 * - Automatically probe for a driver if the bus allows it. 7 */ 8 void bus_probe_device(struct device *dev) 9 { 10 struct bus_type *bus = dev->bus; 11 12 if (!bus) 13 return; 14 15 if (bus->p->drivers_autoprobe) 16 device_initial_probe(dev); 17 ... ... 18 } 19 20 /***** drivers/base/dd.c *****/ 21 void device_initial_probe(struct device *dev) 22 { 23 __device_attach(dev, true); 24 } 25 26 static int __device_attach(struct device *dev, bool allow_async) 27 { 28 ... ... 29 ret = bus_for_each_drv(dev->bus, NULL, &data, 30 __device_attach_driver); 31 ... ... 32 } 33 34 static int __device_attach_driver(struct device_driver *drv, void *_data) 35 { 36 struct device_attach_data *data = _data; 37 struct device *dev = data->dev; 38 bool async_allowed; 39 int ret; 40 41 ret = driver_match_device(drv, dev); 42 if (ret == 0) { 43 /* no match */ 44 return 0; 45 } else if (ret == -EPROBE_DEFER) { 46 dev_dbg(dev, "Device match requests probe deferral\n"); 47 driver_deferred_probe_add(dev); 48 } else if (ret < 0) { 49 dev_dbg(dev, "Bus failed to match device: %d", ret); 50 return ret; 51 } /* ret > 0 means positive match */ 52 53 async_allowed = driver_allows_async_probing(drv); 54 55 if (async_allowed) 56 data->have_async = true; 57 58 if (data->check_async && async_allowed != data->want_async) 59 return 0; 60 61 return driver_probe_device(drv, dev); 62 }
bus_probe_device(&globalfifo_device.dev) 的执行函数路线分析如下所示,经过层层调用,最终又调用到 driver_match_device() 和 driver_probe_device() 函数,查找总线上能和当前设备匹配的驱动,并将驱动和设备绑定在了一起。
struct device *dev = &globalfifo_device.dev; struct device_attach_data *data = { .dev = dev, .check_async = allow_async, .want_async = false, }; struct device_driver *drv; --------------------------------------------------- bus_probe_device(dev) | V device_initial_probe(dev) | V __device_attach(dev, true) | V bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver) | V __device_attach_driver(drv, data) | V driver_match_device(drv, dev) / driver_probe_device(drv, dev)
5. 总结
综上述分析,可以看到驱动注册的过程中,会尝试寻找总线上可以与之匹配的设备;同样地,设备注册的过程中,也会尝试寻找总线上可以与之绑定的驱动。整个过程中,总线、设备、驱动的关键注册函数分别为:
- 总线注册: bus_register()
- 驱动注册: platform_driver_register()
--> driver_register() --> bus_add_driver()
- 设备注册: platform_device_add()
--> device_add() --> bus_add_device() / bus_probe_device()
从sysfs的角度,可以清楚地看到 platform_device 、 platform_driver 、 platform_bus 之间的联系:
/sys/bus/platform/drivers/globalfifo_platform$ ll total 0 bind globalfifo_platform -> ../../../../devices/platform/globalfifo_platform/ module -> ../../../../module/globalfifo_platform/ uevent unbind /sys/bus/platform/devices$ ll total 0 ... ... globalfifo_platform -> ../../../devices/platform/globalfifo_platform/ /sys/devices/platform/globalfifo_platform$ ll total 0 driver -> ../../../bus/platform/drivers/globalfifo_platform/ modalias power/ subsystem -> ../../../bus/platform/ uevent /sys/module/globalfifo_platform/drivers$ ll total 0 platform:globalfifo_platform -> ../../../bus/platform/drivers/globalfifo_platform/
参考资料
[1] Linux设备驱动开发详解(基于最新的Linux4.0内核),宋宝华编著,2016年
[2] Linux设备驱动程序,第三版
[3] 知识整理–linux设备驱动模型:https://blog.csdn.net/TongxinV/article/details/54853122
[4] Linux设备驱动模型:https://blog.csdn.net/qq_40732350/article/details/82992904