5.23 Bus总线子系统
5.23 Bus总线子系统
📚 本节导读
学习时长: 约 30 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: C 语言基础、链接器脚本、5.1 设备驱动框架
🎯 学习目标
- 理解 OneOS 声明式总线模型的驱动-设备匹配机制
- 掌握 OS_DRIVER_INFO / OS_DEVICE_INFO 宏的声明式注册方法
- 理解 driver_match_devices() 和 device_match_drivers() 的匹配流程
- 掌握 OS_DRIVER_DEFINE 宏的自动初始化机制
- 理解链接器段 driver_table 和 device_table 的作用
一、概述
Bus 总线子系统是 OneOS 实现声明式驱动与设备匹配的核心机制。它借鉴了 Linux 内核的设备-总线-驱动模型,通过链接器段(linker section) 在编译期收集驱动和设备信息,在运行时自动完成匹配。驱动源码位于 drivers/bus/ 目录下,核心头文件为 us.h,实现文件为 us.c。
Bus 总线子系统的特点:
- 声明式注册:通过 OS_DRIVER_INFO / OS_DEVICE_INFO 宏将驱动和设备信息放入特定链接器段
- 编译期收集:利用链接器脚本将分散在各源文件中的驱动/设备信息聚合到 driver_table / device_table 段
- 自动匹配:通过名称匹配 driver->name == device->driver,自动调用 probe 函数
- 自动初始化:OS_DRIVER_DEFINE 宏结合 OS_INIT_CALL 实现驱动的自动初始化
- 双向匹配:支持 driver_match_devices()(驱动匹配设备)和 device_match_drivers()(设备匹配驱动)
二、核心数据结构
2.1 驱动信息 os_driver_info_t
c typedef struct os_driver_info { char *name; /* 驱动名称 */ int (*probe)(const struct os_driver_info *drv, const struct os_device_info *dev); } os_driver_info_t;
| 字段 | 说明 |
|---|---|
| ame | 驱动名称,用于与 os_device_info_t.driver 匹配 |
| probe | 探测函数,当驱动与设备匹配成功时调用 |
2.2 设备信息 os_device_info_t
c typedef struct os_device_info { char *name; /* 设备名称 */ char *driver; /* 所需驱动名称 */ const void *info; /* 设备私有信息(如引脚配置、地址等) */ } os_device_info_t;
| 字段 | 说明 |
|---|---|
| ame | 设备名称 |
| driver | 所需驱动名称,用于匹配 os_driver_info_t.name |
| info | 设备私有信息,传递给 probe 函数 |
2.3 匹配关系图
┌──────────────────┐ ┌──────────────────┐ │ os_driver_info │ name │ os_device_info │ │ ┌────────────┐ │ match │ ┌────────────┐ │ │ │ name │──┼────────→│ │ driver │ │ │ │ probe() │ │ │ │ name │ │ │ └────────────┘ │ │ │ info │ │ └──────────────────┘ └──────────────────┘
三、声明式注册宏
3.1 驱动信息注册
c #define OS_DRIVER_INFO static OS_USED OS_SECTION("driver_table") const os_driver_info_t
使用示例:
c /* 在驱动源文件中声明驱动信息 */ OS_DRIVER_INFO my_uart_driver = { .name = "uart_driver", .probe = uart_probe, };
OS_DRIVER_INFO 宏将结构体放入 driver_table 链接器段,链接器会自动收集所有此类声明。
3.2 设备信息注册
c #define OS_DEVICE_INFO static OS_USED OS_SECTION("device_table") const os_device_info_t
使用示例:
c /* 在 BSP 源文件中声明设备信息 */ OS_DEVICE_INFO uart0_device = { .name = "uart0", .driver = "uart_driver", .info = &uart0_config, };
OS_DEVICE_INFO 宏将结构体放入 device_table 链接器段。
3.3 驱动自动初始化
c #define OS_DRIVER_DEFINE(_driver_, sequence, subsequence) \ static os_err_t __driver_##_driver_##_init(void) \ { \ return driver_match_devices(&_driver_); \ } \ OS_INIT_CALL(__driver_##_driver_##_init, sequence, subsequence)
使用示例:
c /* 定义驱动并指定初始化顺序 */ OS_DRIVER_DEFINE(my_uart_driver, OS_INIT_LEVEL_DEVICE, OS_INIT_SUBLEVEL_MIDDLE);
这个宏做了两件事:
- 生成一个 __driver_xxx_init 函数,内部调用 driver_match_devices()
- 通过 OS_INIT_CALL 注册到指定初始化级别,系统启动时自动执行
四、关键 API
4.1 驱动匹配设备
c int driver_match_devices(const os_driver_info_t *driver);
- driver: 驱动信息指针
- 返回: 匹配成功的设备数量
遍历 device_table 段中的所有设备,找到 device->driver == driver->name 的设备,调用 driver->probe(driver, device)。
4.2 设备匹配驱动
c int device_match_drivers(const os_device_info_t *device);
- device: 设备信息指针
- 返回: 匹配成功的驱动数量
遍历 driver_table 段中的所有驱动,找到匹配的驱动并调用 probe。
五、使用示例
5.1 完整的驱动-设备匹配示例
`c
/* ===== 驱动层:drivers/serial/my_uart.c ===== */
#include <bus.h>
#include <device.h>
/* 串口私有数据 */
struct my_uart {
uint32_t base_addr;
int irq;
int baudrate;
};
/* probe 函数:驱动与设备匹配时调用 */
static int uart_probe(const struct os_driver_info *drv, const struct os_device_info *dev)
{
struct my_uart *uart;
const struct my_uart_config *cfg = (const struct my_uart_config *)dev->info;
os_kprintf("probe: driver=%s, device=%s, addr=0x%x\r\n",
drv->name, dev->name, cfg->base_addr);
/* 分配设备结构体 */
uart = os_calloc(1, sizeof(struct my_uart));
uart->base_addr = cfg->base_addr;
uart->irq = cfg->irq;
uart->baudrate = cfg->baudrate;
/* 注册到设备框架 */
os_device_register(&uart->parent, dev->name);
return OS_SUCCESS;
}
/* 声明驱动信息 */
OS_DRIVER_INFO my_uart_driver = {
.name = "my_uart",
.probe = uart_probe,
};
/* 定义驱动自动初始化 */
OS_DRIVER_DEFINE(my_uart_driver, OS_INIT_LEVEL_DEVICE, OS_INIT_SUBLEVEL_MIDDLE);
/* ===== BSP 层:boards/my_board/board.c ===== */
#include <bus.h>
struct my_uart_config {
uint32_t base_addr;
int irq;
int baudrate;
};
/* 声明设备信息 */
static const struct my_uart_config uart0_cfg = {
.base_addr = 0x40001000,
.irq = 21,
.baudrate = 115200,
};
OS_DEVICE_INFO uart0_device = {
.name = "uart0",
.driver = "my_uart",
.info = &uart0_cfg,
};
static const struct my_uart_config uart1_cfg = {
.base_addr = 0x40002000,
.irq = 22,
.baudrate = 9600,
};
OS_DEVICE_INFO uart1_device = {
.name = "uart1",
.driver = "my_uart",
.info = &uart1_cfg,
};
`
5.2 手动触发匹配
c /* 在某些场景下,设备可能晚于驱动注册 */ void late_device_init(void) { /* 手动触发设备匹配驱动 */ device_match_drivers(&uart0_device); }
六、内部实现机制
6.1 链接器段机制
编译时 链接时 ┌─────────────┐ ┌─────────────────────┐ │ file1.c │ │ driver_table 段 │ │ OS_DRIVER_ │──────┐ │ ┌───────────────┐ │ │ INFO(drv1) │ │ │ │ drv1: "uart" │ │ └─────────────┘ │ │ │ drv2: "spi" │ │ ├── 收集 ──→ │ │ drv3: "i2c" │ │ ┌─────────────┐ │ │ │ ... │ │ │ file2.c │ │ │ └───────────────┘ │ │ OS_DRIVER_ │──────┘ └─────────────────────┘ │ INFO(drv2) │ └─────────────┘
链接器脚本中定义 driver_table 和 device_table 段,将分散在各编译单元中的信息聚合到连续内存区域。框架通过遍历这些段实现 O(1) 的驱动-设备遍历。
6.2 初始化流程
系统启动 │ ├── OS_INIT_LEVEL_DEVICE 阶段 │ │ │ ├── __driver_uart_init() → driver_match_devices(&uart_driver) │ │ ├── 遍历 device_table,找到 driver == "uart_driver" 的设备 │ │ └── 调用 uart_probe(drv, dev) 初始化每个匹配的设备 │ │ │ ├── __driver_spi_init() → driver_match_devices(&spi_driver) │ │ │ └── __driver_i2c_init() → driver_match_devices(&i2c_driver) │ └── 应用层初始化
📝 本节小结
本节介绍了 OneOS 中 Bus 总线子系统的声明式驱动-设备匹配机制,包括:
- 声明式注册:OS_DRIVER_INFO / OS_DEVICE_INFO 宏将驱动和设备信息放入链接器段
- 编译期收集:链接器将分散的驱动/设备信息聚合到 driver_table / device_table 段
- 自动匹配:通过名称匹配(driver->name == device->driver)自动调用 probe 函数
- 自动初始化:OS_DRIVER_DEFINE 宏结合 OS_INIT_CALL 实现驱动的自动初始化
- 双向匹配:支持驱动匹配设备和设备匹配驱动两种模式
Bus 总线子系统是 OneOS 驱动框架的重要基础设施,它实现了驱动与设备的解耦,使 BSP 开发者只需声明设备信息,框架自动完成驱动匹配和初始化。