ARM64 Linux 6.10 内核驱动(13):bus.c / driver.c 总线两张表,驱动怎么挂上去

§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"。

做完三件清单相关的事:

  1. /sys/bus/<name> 出现(挂在 buses_init 建的 bus_kset / /sys/bus 下)
  2. 建 devices / drivers 两个 kset,以及两张空 klist
  3. 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 调用)

  1. 总线默认属性(bus->dev_groups)
  2. /sys/bus/<bus>/devices/<名> → 设备 kobj;设备下 subsystem → /sys/bus/<bus>
  3. 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 的逆过程,并且解绑驱动:

  1. 拆符号链接和属性
  2. klist_del 离开 klist_devices
  3. device_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_ADD uevent
  • deferred_probe_extend_timeout:模块刚加载,给还在 defer 的设备多一点重试时间

5.2 bus_add_driver

  1. 分配 driver_private,kobj 放到 drivers_kset(/sys/bus/<bus>/drivers/<名>)
  2. 初始化 第三张表 priv->klist_devices(此时空)
  3. klist_add_tail(..., &sp->klist_drivers)
  4. 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

  1. 先 klist_remove 离开 klist_drivers(新 device_add 再也 match 不到这个驱动)
  2. 再 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:强制指定绑哪个驱动

相关推荐
东城居士6 分钟前
Linux驱动阻塞与非阻塞访问的理解
linux·嵌入式系统
无敌贵点大王9 分钟前
RTThread学习记录13——关于要使用一个外设,RTThread与cubemx到底要怎么配合?
c语言·stm32·学习·rtthread
..Dauntless..14 分钟前
【Linux】进程地址空间初步理解
linux·运维·服务器
Julien200422 分钟前
Docker 网络(一)
linux·运维·服务器·ssh·学习方法
phltxy24 分钟前
C 语言动态内存管理:从申请空间到安全释放
c语言·开发语言·ui
杨云龙UP1 小时前
一次数据库查询缓慢故障复盘:大表数据增长、SQL全表扫描导致系统响应异常
linux·运维·服务器·数据库·sql·mysql
上位机妹子1 小时前
C 语言 数组删除指定元素(快慢指针法)
c语言·数据结构·算法
心中有你02141 小时前
【路径规划】A*寻路算法最通俗易懂讲解(C语言完整实现+详细注释)
c语言·开发语言·算法
PellyKoo2 小时前
【linux运维】ubuntu+samba 用户组独占目录权限配置踩坑记
linux·运维·ubuntu
AR-26710-2 小时前
Linux Day15——系统管理复习
linux·运维