在 Linux 内核驱动开发中,设备模型(Device Model) 是一切驱动的骨架与基石。它解决了多硬件、多平台、多总线的统一管理问题,让驱动与硬件解耦、让系统能自动匹配与枚举设备。
本文目的是理清总线、设备、驱动三者的关系、内核数据结构、匹配流程,以及它如何支撑起整个 Linux 驱动世界。
一、为什么需要 Linux 设备模型?
早期 Linux 驱动非常“硬编码”,即代码实现于可参数的内容并没有分离,均是在驱动代码中写死实现,比如:
由于这种实现方式,在适配不同的硬件时成本太高,需要修改驱动代码,因此提出了设备模型的概念与实现。设备模型的核心价值为:
- 统一抽象:不管是 PCI、USB、I2C、SPI、平台总线,都用同一套框架管理;
- 支持热插拔自动匹配:设备插入 → 内核找到对应驱动 → 自动绑定运行;
- 拓扑呈现
- 生命周期统一
- 驱动复用
可以这样总结,即:设备模型 = 总线(通道)+ 设备(硬件)+ 驱动(软件)+ 匹配机制。
二、设备模型的三大核心结构体
Linux 设备模型围绕3个基础结构体构建,所有总线、驱动、设备都基于它们展开。
1. struct bus_type (总线)
代表一种总线类型(PCI、USB、I2C、platform、SPI 等)。
struct bus_type { const char *name; int (*match)(struct device *dev, struct device_driver *drv); int (*probe)(struct device *dev); struct bus_attribute *bus_attrs; struct device_driver *drv; struct device *dev; // ...};
结构体struct bus_type的核心作用:
- 提供 match() 匹配函数:决定设备和驱动能否绑定;
2. struct device (设备)
代表一个真实硬件设备,是所有设备的“基类”。
struct device { struct bus_type *bus; // 属于哪条总线 struct device_driver *driver;// 绑定的驱动 const char *init_name; void *platform_data; struct device_node *of_node; // 设备树节点 // ...};
结构体struct device 只描述“硬件本身”:
3. struct device_driver (驱动)
代表驱动程序,负责操作硬件、实现功能。
struct device_driver { const char *name; struct bus_type *bus; int (*probe) (struct device *dev); int (*remove)(struct device *dev); const struct of_device_id *of_match_table; // ...};
结构体struct device_driver的核心回调:
probe()remove()of_match_table
三、经典且常用的总线:平台总线(Platform Bus)
其中嵌入式 Linux 90% 的驱动都跑在平台总线上,为什么要用 platform 总线?
该总线专门管理:
- 常见的片上控制器(UART、I2C、SPI、GPIO、时钟等);
平台驱动标准结构
// 1. 匹配表(与设备树 compatible 对应)static const struct of_device_id xxx_dt_ids[] = { { .compatible = "vendor,xxx-ip" }, { /* Sentinel */ }};MODULE_DEVICE_TABLE(of, xxx_dt_ids);// 2. 平台驱动结构体static struct platform_driver xxx_driver = { .probe = xxx_probe, .remove = xxx_remove, .driver = { .name = "xxx-driver", .of_match_table = xxx_dt_ids, },};// 3. 注册/卸载module_platform_driver(xxx_driver);
probe 注册函数什么时候被调用?
满足以下任一条件:
- 内核启动时创建了 platform_device;
probe 函数内部的常见操作:
- 获取控制器基址、驱动需要的空间资源与使能时钟等(地址、中断、时钟);
四、设备模型完整工作流程
1. 总线注册
Linux内核启动—>注册各类总线(platform/i2c/usb/pci);
2. 设备生成
硬件信息来自:
- 板级代码,内核个别驱动C代码写死生成对应的
struct device;
3. 驱动注册
驱动加载(或内核内置)—> 注册 struct device_driver,并指定所属总线;
4. 自动匹配 & 绑定
总线的 .match() 函数开始工作:
如果匹配成功,则调用驱动的 probe() 函数进行初始化,然后硬件开始工作。
五、设备模型呈现给用户的方式:sysfs
设备模型把硬件拓扑变成文件系统,挂载在 /sys目录。
/sys /bus 所有总线 /platform /pci /i2c /usb /dev 设备号 /class 设备分类(input/tty/block/net) /devices 全局设备树拓扑
开发者可以直接看到:
七、总结
设备只提供数据,驱动只提供逻辑,总线只提供匹配与管理,设备模型使三者完全解耦。
- Linux 设备模型 = 总线 + 设备 + 驱动;
- 三大结构体:
bus_type、device、device_driver ; - 流程:1)注册总线;2)生成设备;3)注册驱动;4)匹配;5) probe;