§1.2 bus.c / driver.c:总线两张表,驱动怎么挂上去
平台:QEMU virt + ARM64。这两份是驱动模型的模板胶水,不是 UART 业务。
1. 这是模板,不是业务
device_register、bus_register、driver_register 写法同一套路:
门禁(对象在不在、重名没有)
→ 入表(klist)
→ 自动配对(probe / attach)
→ sysfs / uevent
出错则回滚
和业务代码的「校验 → 入库 → 发事件」很像。清单读它们是为了认骨架。
业务填在槽里:
| 模板 | virt 上的业务 |
|---|---|
bus_type.match |
platform_match(compatible) |
bus_type.probe |
platform_probe → platform_driver.probe |
driver_register |
你的 .ko / module_platform_driver |
driver_register 里那条 pr_warn:总线已经有 .probe 时不要再填 device_driver.probe(call_driver_probe 优先 bus->probe,你的会被忽略)。platform 正确写法是 platform_driver.probe,由 platform_probe 转发。
2. 两张表在哪(匹配用 klist,不是 sysfs)
每条总线的内核私有数据是 struct subsys_private(drivers/base/base.h)。对外的 struct bus_type 是 const 描述(名字、函数指针);能挂节点的链表在 priv 上。bus_to_subsys(bus) 把二者连起来。
struct subsys_private {
struct kset *devices_kset; // /sys/bus/<名>/devices
struct kset *drivers_kset; // /sys/bus/<名>/drivers
struct klist klist_devices; // ★ 设备表
struct klist klist_drivers; // ★ 驱动表
unsigned int drivers_autoprobe:1;
...
};
bus_register 里 klist_init 这两张。
| 链表 | 谁挂 | 谁扫 | 含义 |
|---|---|---|---|
klist_devices |
bus_add_device |
bus_for_each_dev |
这条总线上有哪些设备 |
klist_drivers |
bus_add_driver |
bus_for_each_drv |
这条总线上有哪些驱动 |
第三张表 在每个驱动上:drv->p->klist_devices。bus_add_driver 里初始化为空,driver_bound(probe 成功)才往里挂 。driver_for_each_device / driver_detach 扫的是它,不是总线设备表。
sysfs 是给人看的镜像,内核 match 不读 /sys:
/sys/bus/platform/devices/ ← 符号链接
/sys/bus/platform/drivers/
bus_add_device 既 klist_add 又 sysfs_create_link,不要把目录当成匹配表。
3. bus_register:先有容器
virt 上 platform_bus_register() 启动早期调用,bus->name 是 "platform"。
做完三件清单相关的事:
/sys/bus/<name>出现(挂在buses_init建的bus_kset//sys/bus下)- 建
devices/drivers两个 kset,以及两张空 klist drivers_autoprobe = 1:以后device_add会自动bus_probe_device,driver_register会自动driver_attach
.match / .probe 不在这里填 ,已经写在 platform_bus_type 上。本函数不往表里塞设备或驱动。
4. 设备入表 / 出表
4.1 bus_add_device(device_add 调用)
- 总线默认属性(
bus->dev_groups) /sys/bus/<bus>/devices/<名>→ 设备 kobj;设备下subsystem→/sys/bus/<bus>klist_add_tail(&dev->p->knode_bus, &sp->klist_devices)
没有 dev->bus 直接返回 0(不少纯 class 设备)。
这里还不 probe。
4.2 bus_probe_device(仍由 device_add 调用,在入表之后)
不是再登记一张表。 设备已经在 klist_devices 里,这里是:去驱动表里找对象。
if (sp->drivers_autoprobe)
device_initial_probe(dev); // 进 dd.c,扫 klist_drivers
后面的 subsys_interface->add_dev 是观察者,不是 platform_driver.probe。
-EPROBE_DEFER 重试:deferred_probe_work_func 再调本函数,把链重走一遍。
关掉 autoprobe:设备在表里、sysfs 也有,但不自动 probe。
4.3 bus_remove_device(device_del 调用)
bus_add_device 的逆过程,并且解绑驱动:
- 拆符号链接和属性
klist_del离开klist_devicesdevice_release_driver→dd.c的device_remove
两次 subsys_put:一次抵本函数的 bus_to_subsys,一次抵 bus_add_device 时拿的引用。
5. 驱动入表 / 出表
5.1 driver_register(driver.c,对外入口)
platform_driver_register 先设 drv->bus = &platform_bus_type,再进这里。本函数只做门禁,活在 bus_add_driver:
- 总线必须已经
bus_register - 同名驱动不能重复(
driver_find) bus_add_driver→ 驱动groups→KOBJ_ADDueventdeferred_probe_extend_timeout:模块刚加载,给还在 defer 的设备多一点重试时间
5.2 bus_add_driver
- 分配
driver_private,kobj 放到drivers_kset(/sys/bus/<bus>/drivers/<名>) - 初始化 第三张表
priv->klist_devices(此时空) klist_add_tail(..., &sp->klist_drivers)drivers_autoprobe则driver_attach(drv)(dd.c:扫klist_devices,对已有设备 match/probe)
insmod 你的 .ko 时,virt 上 DT 设备往往已经 device_add 过,走的就是第 4 步。
sysfs bind/unbind 文件清单可跳过。
5.3 driver_unregister → bus_remove_driver
- 先
klist_remove离开klist_drivers(新device_add再也 match 不到这个驱动) - 再
driver_detach:扫drv->p->klist_devices,对每台已绑定设备device_release_driver
不要和 bus_remove_device 搞反:那边拆一台设备;这边拆一个驱动(可能绑着多台设备)。
6. 两把刷子:bus_for_each_dev / bus_for_each_drv
不是「抽象设备 vs 驱动表」。同一条总线上两张平级的表,各扫一张。回调返回非 0 停扫。
| 时机 | 用谁 | 含义 |
|---|---|---|
设备后到(device_add → bus_probe_device → __device_attach) |
bus_for_each_drv |
这一台设备,对每个已有 driver match |
驱动后到(insmod → driver_attach) |
bus_for_each_dev |
这一个 driver,对每个已有 device match |
这就是清单「bus / device / driver 三段如何匹配」的骨架。字符串怎么比,仍是 drv->bus->match → virt 上 platform_match。
7. 整条链(virt)
buses_init /sys/bus
→ bus_register(platform_bus_type) 两张空表,autoprobe=1
设备侧(DT → platform_device):
device_add
→ bus_add_device 入 klist_devices
→ bus_probe_device 扫 klist_drivers → dd.c probe
驱动侧(你的 platform_driver / 内核里已编进的驱动):
driver_register
→ bus_add_driver 入 klist_drivers
→ driver_attach 扫 klist_devices → dd.c probe
谁先谁后都能碰上。写错 compatible:入表仍成功,卡在 platform_match,不 probe。
8. driver.c 其余接口
清单不用精读:
driver_for_each_device/driver_find_device:扫第三张表driver_create_file/driver_add_groups:驱动 kobj 的 sysfs 工具driver_set_override:强制指定绑哪个驱动