2. 基础知识

在 Linux 设备模型中,设备注册与依赖管理并不是孤立操作,而是围绕一组核心抽象展开的:设备(device)、驱动(driver)、总线(bus)、类(class)与子系统(subsystem)。理解它们之间的职责边界与匹配关系,是后续掌握 probe 顺序、依赖解析、热插拔与电源管理的基础。

核心记忆:总线负责“匹配”,驱动负责“驱动”,设备负责“被驱动”,类负责“用户可见视图”,子系统负责“同类设备的统一管理”。

一、五个核心对象及其职责

Linux 设备模型用 struct device、struct device_driver、struct bus_type、struct class 和 struct subsys_private 等结构描述这些对象。它们不是并列关系,而是有明确的层次与引用关系。

  • 设备(device):描述一个具体的硬件实例或逻辑功能单元,例如一颗 I2C 温度传感器、一个 GPIO 按键、一块 PCIe 网卡。它携带资源信息:寄存器基址、中断号、时钟、GPIO、DMA 通道等。
  • 驱动(driver):描述“如何操作某一类设备”的代码逻辑,包含 probe、remove、suspend、resume 等回调。驱动通过 id_table 或 of_device_id 声明自己能匹配哪些设备。
  • 总线(bus):设备与驱动之间的“红娘”。它维护设备链表和驱动链表,负责在设备或驱动注册时触发匹配,并在匹配成功后调用驱动的 probe。常见总线有 platform、I2C、SPI、PCI、USB 等。
  • 类(class):面向用户空间的抽象,用于把功能相似的设备归为一类,并在 /sys/class/ 下生成统一视图,例如 /sys/class/net/、/sys/class/tty/、/sys/class/leds/。
  • 子系统(subsystem):对某一类设备和驱动的统一管理框架,通常包含总线、类、设备模型和电源管理策略,例如 input 子系统、TTY 子系统、网络子系统、LED 子系统。

二、设备与驱动如何匹配

匹配过程由总线完成。以 platform 总线为例,设备树中的节点会被转换成 platform_device,驱动则注册为 platform_driver。总线依次比较驱动中的 of_device_id.compatible、acpi_match_table、id_table 与设备名称,匹配成功后调用 probe。

static const struct of_device_id demo_of_match[] = {
    { .compatible = "vendor,demo-sensor" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, demo_of_match);

static struct platform_driver demo_driver = {
    .probe  = demo_probe,
    .remove = demo_remove,
    .driver = {
        .name           = "demo-sensor",
        .of_match_table = demo_of_match,
    },
};
module_platform_driver(demo_driver);

当设备树中存在 compatible = "vendor,demo-sensor" 的节点时,总线会将该设备与上述驱动绑定,随后进入 demo_probe。若匹配失败,设备会停留在“未绑定”状态,在 /sys/bus/platform/devices/ 下可见,但不会产生功能。

三、典型应用场景与依赖关系

实际系统中,设备之间往往存在依赖。例如一颗摄像头模组依赖 I2C 总线、MIPI CSI 控制器、时钟控制器、GPIO 控制器和电源 regulator。只有这些依赖先就绪,摄像头驱动才能成功 probe。Linux 通过 device_link、fwnode 引用、EPROBE_DEFER 机制表达并解决这些依赖。

对象 典型路径 作用 常见误区
设备 device /sys/devices/... 描述硬件实例与资源 把设备当成驱动
驱动 driver /sys/bus/*/drivers/ 提供操作回调 认为驱动一定先于设备注册
总线 bus /sys/bus/ 匹配设备与驱动 忽略总线匹配优先级
类 class /sys/class/ 用户空间统一视图 把 class 当作总线
子系统 subsystem /sys/class/*、/sys/bus/* 统一管理同类设备 混淆子系统与总线

四、案例:一个 GPIO 按键的注册链路

以 GPIO 按键为例:设备树中定义 gpio-keys 节点,platform 总线将其转换为 platform_device;gpio_keys_driver 通过 of_match_table 匹配后 probe;probe 中申请 GPIO、注册中断、创建 input 设备;input 子系统再通过 /sys/class/input/ 和 /dev/input/eventX 向用户空间暴露事件。整个链路中,设备、驱动、总线、类、子系统各司其职。

Linux 设备模型:设备、驱动、总线、类、子系统关系 总线 bus_type platform / i2c / spi / pci / usb 设备 device 资源:reg / irq / clk / gpio /sys/devices/... 驱动 device_driver probe / remove / suspend /sys/bus/*/drivers/ 类 class /sys/class/net、tty、leds 子系统 subsystem input / tty / net / led 注册 注册 匹配成功 → probe 归类 纳入管理 总线负责匹配,驱动负责操作,设备提供资源,类提供用户视图,子系统统一管理

五、常见误区与复盘

误区一:认为“设备注册后驱动就会自动运行”。实际上必须经过总线匹配并成功 probe,驱动才会真正接管设备。
误区二:把 class 当成 bus。class 只负责用户空间视图,不参与设备与驱动的匹配。
误区三:忽略 probe 顺序。若依赖的时钟、GPIO、regulator 尚未就绪,probe 会返回 -EPROBE_DEFER,设备会被延迟重试,而不是直接失败。

复盘时建议按以下顺序排查:先看设备是否出现在 /sys/bus/<bus>/devices/,再看驱动是否出现在 /sys/bus/<bus>/drivers/,然后检查 compatible 或 id_table 是否匹配,最后查看 dmesg 中是否有 probe 失败或 EPROBE_DEFER 日志。掌握这五个基本对象及其关系,后续学习设备注册、依赖管理与 probe 顺序时就能建立清晰的系统观。