设备在/sys里看得见却不probe、compatible对了仍不绑驱动:根因常在bus/device/driver 三件套的匹配与绑定,而不是probe函数体本身。本文沿driver_register→bus_add_driver→driver_attach→bus->match→probe走通一次绑定,便于对照 sysfs 与 DT 排障。
源码锚点
| 路径 | 作用 |
|---|---|
include/linux/device/driver.h | struct device_driver、driver_register |
include/linux/device/bus.h | struct bus_type(match/probe/remove) |
include/linux/device.h | struct device、device_register |
drivers/base/driver.c | driver_register/driver_attach |
drivers/base/bus.c | bus_add_driver、匹配与遍历 |
drivers/base/dd.c | device_attach/__device_attach/really_probe |
include/linux/mod_devicetable.h | of_device_id等匹配表 |
核心结构(头文件可见字段):
/* include/linux/device/bus.h */structbus_type{constchar*name;int(*match)(structdevice*dev,structdevice_driver*drv);int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);/* … */};/* include/linux/device/driver.h */structdevice_driver{constchar*name;structbus_type*bus;structmodule*owner;conststructof_device_id*of_match_table;int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);/* … */};注册骨架:
/* 典型写法:把 driver 挂到对应 bus */staticstructdevice_drivermy_drv={.name="my-drv",.bus=&platform_bus_type,/* 或 pci_bus_type 等 */.owner=THIS_MODULE,.of_match_table=my_of_match,.probe=my_probe,.remove=my_remove,};driver_register(&my_drv);/* 内部 bus_add_driver → driver_attach */调用链
驱动加载 / 内置 init → driver_register(drv) /* drivers/base/driver.c */ → bus_add_driver(drv) /* drivers/base/bus.c */ → driver_attach(drv) → 遍历 bus 上已有 device → driver_match_device() /* 调 bus->match */ → device_driver_attach / really_probe → drv->probe(dev) 或 bus->probe 设备侧后注册(热插拔 / 晚枚举) → device_register(dev) → bus_probe_device / device_initial_probe → 对已注册 driver 再跑 match → probeDevice Tree 场景(platform 常见):
of_platform_populate / 创建 platform_device → device 挂上 platform_bus → bus->match:of_driver_match_device(看 compatible) → 命中后进入 probe重点知识
1. 匹配键在 bus,不在「文件名像不像」bus->match决定能否绑:platform 看of_match_table/name,PCI 看 ID 表,USB 看 vid/pid。只改模块名、不改 match 表,永远不会probe。
2.driver_register与device_register谁先谁后都要能绑
模型支持双向:先有设备后有驱动,或反过来。driver_attach/device_attach保证后注册的一方仍会遍历匹配。排障时两边都要查/sys/bus/<bus>/devices与drivers。
3.probe失败会留下「半绑定」观感really_probe里probe返回非 0 会解绑并可能打 defer(-EPROBE_DEFER)。依赖时钟/regulator/GPIO 未就绪时应用 defer,而不是假装成功;反复 defer 要查依赖图与 DT 资源。
4. 观测与配置
ls/sys/bus/platform/devices/ls/sys/bus/platform/drivers/# 看是否已 bindls-l/sys/bus/platform/devices/<dev>/driver# DT compatiblecat/proc/device-tree/.../compatible# 若启用 proc DT# 强制 bind/unbind(调试)echo<device>|sudotee/sys/bus/platform/drivers/<drv>/binddmesg|grep-iE'probe|defer'5. 设计意图与踩坑
- 意图:用统一 kobject/sysfs 拓扑表达硬件,把「发现」与「驱动逻辑」解耦。
- 坑:
compatible字符串与of_match_table不一致(多一个厂商前缀就 miss)。 - 坑:
owner/module引用错误,probe后模块可被卸载。 - 坑:在
probe里睡眠过久占着绑定路径,拖慢整条 bus 枚举。 - 坑:忽略
-EPROBE_DEFER,当成永久失败反复改 DT。
Checklist
- 能画出
driver_register→match→really_probe→drv->probe的链 /sys/bus/<bus>/drivers/<name>存在,且目标 device 的driver软链正确- DT
compatible(或 PCI/USB ID)与 match 表逐字符一致 probe失败路径可重入;资源获取失败优先-EPROBE_DEFER- 模块
owner与remove成对;卸载无 UAF dmesg能对应到本次 bind/probe 日志,而非只看业务 printk