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/ 目录下,核心头文件为 bus.h,实现文件为 bus.c。参考 6.1 的链接器段机制理解为什么"声明式"能自动聚合。
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()(设备匹配驱动)
1.1 使用场景
一句话核心:Bus 子系统是"驱动与设备之间的自动匹配桥梁"——驱动写一次、用 OS_DRIVER_INFO 声明;BSP 用 OS_DEVICE_INFO 声明"板上有哪些设备、各自需要哪个驱动";系统启动时框架按名字自动把驱动 probe 到对应设备上,BSP 开发者无需手写每个设备的初始化调用。
与"手动逐个调用设备初始化"对比:
| 维度 | 用 Bus 声明式匹配 | 手动逐个初始化 |
|---|---|---|
| 加新设备 | 加一条 OS_DEVICE_INFO | 手写初始化代码 |
| 驱动复用 | 同一驱动自动匹配多个设备 | 重复实现 |
| 换板子 | 只改设备表 | 改一堆代码 |
| 可维护性 | 高(声明式) | 低 |
典型应用场景:
| 场景 | 谁在用 | 实际工作情况 |
|---|---|---|
| 板级外设初始化 | 所有 BSP | 板上每个 uart/spi/i2c 设备用 OS_DEVICE_INFO 声明,启动时自动 probe |
| 串口多实例 | 多串口板 | 一个 uart_driver 声明一次,多个 OS_DEVICE_INFO 各配不同基址/中断,自动匹配多次 |
| 外设晚注册 | 动态设备 | device_match_drivers 手动触发设备匹配驱动(见 5.2) |
| 驱动库复用 | 跨板移植 | 驱动只写一次,板子不同只改 OS_DEVICE_INFO |
选型边界表:
| 场景 | 该用什么 | 为什么 |
|---|---|---|
| 极简单单外设 | 直接初始化 | 引入 Bus 声明式有点重 |
| 多设备同驱动 | Bus 声明式 | 自动匹配显著省代码 |
| 动态/晚注册设备 | device_match_drivers | 手动触发匹配 |
结合源码的使用注意点:
- 匹配是精确字符串比较:
driver->name与device->driver用strcmp精确匹配,拼写必须完全一致; probe返回无实际意义:driver_match_devices/device_match_drivers都return 0(见 4.1),不要依赖返回值判断匹配结果;- 匹配方向差异:
driver_match_devices会匹配所有该驱动的设备,device_match_drivers匹配到第一个驱动就break(一个设备只配一个驱动)。
二、核心数据结构
2.1 驱动信息 os_driver_info_t
来源:drivers/bus/bus.h
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;字段详解(源码无注释,下表补充说明,不属于源码):
| 字段 | 类型 | 说明 |
|---|---|---|
name | char * | 驱动名称,用于与 os_device_info_t.driver 精确匹配 |
probe | 函数指针 | 探测函数:驱动与设备匹配成功时调用,返回 int(见 5.1 示例) |
2.2 设备信息 os_device_info_t
来源:drivers/bus/bus.h
typedef struct os_device_info
{
char *name;
char *driver;
const void *info;
} os_device_info_t;字段详解(源码无注释,下表补充说明,不属于源码):
| 字段 | 类型 | 说明 |
|---|---|---|
name | char * | 设备名称(如 "uart0"),注册到设备框架时的名字 |
driver | char * | 所需驱动名称,与 os_driver_info_t.name 精确匹配 |
info | const void * | 设备私有信息(如基址/中断/引脚配置),probe 里强转使用 |
2.3 匹配关系图
┌──────────────────┐ ┌──────────────────┐
│ os_driver_info │ name │ os_device_info │
│ ┌────────────┐ │ match │ ┌────────────┐ │
│ │ name │──┼────────→│ │ driver │ │
│ │ probe() │ │ │ │ name │ │
│ └────────────┘ │ │ │ info │ │
└──────────────────┘ └──────────────────┘三、声明式注册宏
3.1 驱动信息注册
#define OS_DRIVER_INFO static OS_USED OS_SECTION("driver_table") const os_driver_info_t使用示例:
/* 在驱动源文件中声明驱动信息 */
OS_DRIVER_INFO my_uart_driver = {
.name = "uart_driver",
.probe = uart_probe,
};OS_DRIVER_INFO 宏将结构体放入 driver_table 链接器段,链接器会自动收集所有此类声明。
3.2 设备信息注册
#define OS_DEVICE_INFO static OS_USED OS_SECTION("device_table") const os_device_info_t使用示例:
/* 在 BSP 源文件中声明设备信息 */
OS_DEVICE_INFO uart0_device = {
.name = "uart0",
.driver = "uart_driver",
.info = &uart0_config,
};OS_DEVICE_INFO 宏将结构体放入 device_table 链接器段。
3.3 驱动自动初始化
#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)使用示例:
/* 定义驱动并指定初始化顺序 */
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 驱动匹配设备 driver_match_devices
函数签名:
int driver_match_devices(const os_driver_info_t *driver);| 参数 | 说明 |
|---|---|
driver | 驱动信息指针 |
| 返回值 | 说明 |
|---|---|
0 | 总是返回 0(源码 return 0),不表示匹配数量,不要依赖 |
注意:源码
driver_match_devices固定return 0(见下方),文档早期版本说"返回匹配成功的设备数量"是错误的。实际匹配结果体现在probe是否被调用,不体现在返回值上。
完整源码(drivers/bus/bus.c,与项目逐字一致):
int driver_match_devices(const os_driver_info_t *driver)
{
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM MDK Compiler */
extern const int device_table$$Base;
extern const int device_table$$Limit;
const os_device_info_t *device_table = (os_device_info_t *)&device_table$$Base;
int device_num = (os_device_info_t *)&device_table$$Limit - device_table;
#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
const os_device_info_t *device_table = (os_device_info_t *)__section_begin("device_table");
int device_num = (os_device_info_t *)__section_end("device_table") - device_table;
#elif defined(__GNUC__) /* for GCC Compiler */
extern const int __device_table_start;
extern const int __device_table_end;
const os_device_info_t *device_table = (os_device_info_t *)&__device_table_start;
int device_num = (os_device_info_t *)&__device_table_end - device_table;
#endif
int dev_index;
for (dev_index = 0; dev_index < device_num; dev_index++)
{
if (!strcmp(driver->name, device_table[dev_index].driver))
{
driver->probe(driver, &device_table[dev_index]);
}
}
return 0;
}分步详解:
第 1 步:取 device_table 段起始和数量(无条件执行,按编译器分支)
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM MDK Compiler */
extern const int device_table$$Base;
extern const int device_table$$Limit;
const os_device_info_t *device_table = (os_device_info_t *)&device_table$$Base;
int device_num = (os_device_info_t *)&device_table$$Limit - device_table;
#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
const os_device_info_t *device_table = (os_device_info_t *)__section_begin("device_table");
int device_num = (os_device_info_t *)__section_end("device_table") - device_table;
#elif defined(__GNUC__) /* for GCC Compiler */
extern const int __device_table_start;
extern const int __device_table_end;
const os_device_info_t *device_table = (os_device_info_t *)&__device_table_start;
int device_num = (os_device_info_t *)&__device_table_end - device_table;
#endif详细解释:关键 —— 链接器段取址:三种编译器取 device_table 段首尾地址的方式不同:
- MDK(ARMCC/AC5):
device_table$$Base/$$Limit符号; - IAR:
__section_begin("device_table")/__section_end; - GCC:
__device_table_start/__device_table_end。
设备数量device_num= (段尾地址 - 段首地址)/sizeof(os_device_info_t)(指针相减自动按结构体大小算个数)。
第 2 步:遍历设备表,按名字匹配(条件 strcmp == 0)
int dev_index;
for (dev_index = 0; dev_index < device_num; dev_index++)
{
if (!strcmp(driver->name, device_table[dev_index].driver))
{
driver->probe(driver, &device_table[dev_index]);
}
}
return 0;
}详细解释:遍历所有设备,若 driver->name == device_table[dev_index].driver(strcmp 为 0)则调用 driver->probe(driver, &device)。注意:这里是驱动匹配设备,一个驱动可匹配多个设备(不 break,逐个 probe)。最后 return 0。
数值演算 —— 匹配过程(驱动 "my_uart",设备表 3 项):
| 设备 | device->driver | strcmp("my_uart", device.driver) | 动作 |
|---|---|---|---|
uart0 | "my_uart" | 0(相等) | probe 调用 |
uart1 | "my_uart" | 0(相等) | probe 调用 |
spi0 | "my_spi" | 非 0(不等) | 跳过 |
driver_match_devices() 条件走向总结:
driver_match_devices(driver) 条件走向总结:
├── 按编译器取 device_table 段首/数量(MDK/IAR/GCC 三分支)
├── for 遍历 device_table 每个设备
│ ├── strcmp(driver->name, device.driver) == 0(匹配)
│ │ └── driver->probe(driver, &device) 调用(可匹配多个设备)
│ └── 不匹配 → 跳过
└── return 04.2 设备匹配驱动 device_match_drivers
函数签名:
int device_match_drivers(const os_device_info_t *device);| 参数 | 说明 |
|---|---|
device | 设备信息指针 |
| 返回值 | 说明 |
|---|---|
0 | 总是返回 0(源码 return 0) |
完整源码(drivers/bus/bus.c,与项目逐字一致):
int device_match_drivers(const os_device_info_t *device)
{
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM MDK Compiler */
extern const int driver_table$$Base;
extern const int driver_table$$Limit;
const os_driver_info_t *driver_table = (os_driver_info_t *)&driver_table$$Base;
int driver_num = (os_driver_info_t *)&driver_table$$Limit - driver_table;
#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
const os_driver_info_t *driver_table = (os_driver_info_t *)__section_begin("driver_table");
int driver_num = (os_driver_info_t *)__section_end("driver_table") - driver_table;
#elif defined(__GNUC__) /* for GCC Compiler */
extern const int __driver_table_start;
extern const int __driver_table_end;
const os_driver_info_t *driver_table = (os_driver_info_t *)&__driver_table_start;
int driver_num = (os_driver_info_t *)&__driver_table_end - driver_table;
#endif
int drv_index;
for (drv_index = 0; drv_index < driver_num; drv_index++)
{
if (!strcmp(device->driver, driver_table[drv_index].name))
{
driver_table[drv_index].probe(&driver_table[drv_index], device);
/* a device must only match one driver */
break;
}
}
return 0;
}分步详解:
第 1 步:取 driver_table 段起始和数量(无条件执行,按编译器分支)
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM MDK Compiler */
extern const int driver_table$$Base;
extern const int driver_table$$Limit;
const os_driver_info_t *driver_table = (os_driver_info_t *)&driver_table$$Base;
int driver_num = (os_driver_info_t *)&driver_table$$Limit - driver_table;
#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */
const os_driver_info_t *driver_table = (os_driver_info_t *)__section_begin("driver_table");
int driver_num = (os_driver_info_t *)__section_end("driver_table") - driver_table;
#elif defined(__GNUC__) /* for GCC Compiler */
extern const int __driver_table_start;
extern const int __driver_table_end;
const os_driver_info_t *driver_table = (os_driver_info_t *)&__driver_table_start;
int driver_num = (os_driver_info_t *)&__driver_table_end - driver_table;
#endif详细解释:与 4.1 对称,取 driver_table 段(不是 device_table)的首地址和驱动数量。
第 2 步:遍历驱动表,匹配后 probe 并 break(条件 匹配成功)
int drv_index;
for (drv_index = 0; drv_index < driver_num; drv_index++)
{
if (!strcmp(device->driver, driver_table[drv_index].name))
{
driver_table[drv_index].probe(&driver_table[drv_index], device);
/* a device must only match one driver */
break;
}
}
return 0;
}详细解释:遍历所有驱动,若 device->driver == driver_table[drv_index].name 则调用 probe 并 break——一个设备只匹配一个驱动(源码注释明确"a device must only match one driver")。这与 4.1 的"驱动匹配多个设备"形成对比。
device_match_drivers() 条件走向总结:
device_match_drivers(device) 条件走向总结:
├── 按编译器取 driver_table 段首/数量(MDK/IAR/GCC 三分支)
├── for 遍历 driver_table 每个驱动
│ ├── strcmp(device.driver, driver.name) == 0(匹配)
│ │ ├── driver.probe(&driver, device) 调用
│ │ └── break(一个设备只配一个驱动)
│ └── 不匹配 → 跳过继续
└── return 0五、使用示例
5.1 完整的驱动-设备匹配示例
/* ===== 驱动层:drivers/serial/my_uart.c ===== */
#include <bus.h>
#include <device.h>
/* 设备私有配置:由 BSP 层提供并作为 dev->info 传入 */
struct my_uart_config {
uint32_t base_addr;
int irq;
int baudrate;
};
/* 串口设备结构体:首成员继承 os_device_t,probe 后注册为设备 */
struct my_uart {
os_device_t parent; /* 首成员:设备框架基类 */
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));
if (uart == OS_NULL)
{
os_kprintf("my_uart alloc failed\r\n");
return -1;
}
uart->base_addr = cfg->base_addr;
uart->irq = cfg->irq;
uart->baudrate = cfg->baudrate;
/* 注册到设备框架(&uart->parent 即设备基址,因 parent 是首成员) */
if (os_device_register(&uart->parent, dev->name) != OS_SUCCESS)
{
os_kprintf("my_uart register %s failed\r\n", dev->name);
os_free(uart);
return -1;
}
return 0;
}
/* 声明驱动信息 */
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>
/* BSP 提供设备私有配置(结构与驱动层 my_uart_config 一致) */
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,
};说明:
struct my_uart_config定义在驱动层(my_uart.c),BSP 层直接引用它即可(通过包含驱动头文件)。struct my_uart的首成员必须是os_device_t parent,这样&uart->parent就是os_device_t*,可os_device_register。BSP 声明OS_DEVICE_INFO时.info指向各自的config,probe里(const struct my_uart_config *)dev->info强转回来。
5.2 手动触发匹配
/* 在某些场景下,设备可能晚于驱动注册 */
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)
│
└── 应用层初始化七、使用 Bus 接入新外设(BSP 适配指南)
7.1 先认识 Bus 的"适配":其实是不用移植的
drivers/bus/ 的 Bus 子系统几乎不依赖任何硬件(只是链接器段收集 + 字符串匹配),任何平台都不用改它。所谓"适配"其实是用 Bus 的声明式机制去接入一个新外设,分两步:① 驱动层写一个驱动(OS_DRIVER_INFO + OS_DRIVER_DEFINE);② BSP 层声明设备(OS_DEVICE_INFO)。框架自动完成匹配。
适配全景图:
┌──────────────────────────────────────────────────────────┐
│ BSP 层(board.c / peripherals.c) │
│ OS_DEVICE_INFO uart0_device = {name, "uart_driver", info} │
│ OS_DEVICE_INFO spi0_device = {name, "spi_driver", info} │
└──────────────────────────────┬───────────────────────────┘
│ 编译期放入 device_table 段
┌──────────────────────────────▼───────────────────────────┐
│ Bus 框架层(drivers/bus/bus.c,通用不改) │
│ driver_match_devices → strcmp 匹配 → 调 probe │
└───────────────┬──────────────────────────┬────────────────┘
│ OS_DRIVER_DEFINE 自动触发 │ 匹配后调 probe
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ 驱动层(drivers/serial/my_uart.c 等)★需实现★ │
│ OS_DRIVER_INFO my_uart_driver = {name, uart_probe} │
│ uart_probe: 解析 dev->info → 分配/注册设备 │
└──────────────────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 实际硬件(串口/SPI/I2C 控制器) │
└──────────────────────────────────────────────────────────┘适配的关键洞察:Bus 框架不用动,你只需要:驱动层实现 probe + 声明 OS_DRIVER_INFO/OS_DRIVER_DEFINE;BSP 层声明 OS_DEVICE_INFO。匹配和初始化由框架自动完成。
7.2 接入一个新外设的标准流程
第 1 步:实现驱动并声明驱动信息——写一个驱动文件(如 my_spi.c),实现 probe 函数(解析 dev->info 的配置,分配/初始化设备并注册),用 OS_DRIVER_INFO 声明(name + probe),用 OS_DRIVER_DEFINE 指定初始化级别。
第 2 步:BSP 层声明设备——在 board.c/peripherals.c 定义设备配置结构体和实例,用 OS_DEVICE_INFO 声明:name(设备名)、driver(驱动名,须与第 1 步 OS_DRIVER_INFO.name 一致)、info(指向配置)。
第 3 步:编译确认段包含——确认链接脚本包含 driver_table/device_table 段(MDK/IAR/GCC 三种脚本都要有),且 OS_SECTION 宏被正确支持。
第 4 步:测试——系统启动后看 probe 日志是否打印(driver_match_devices 在初始化级别自动执行);用 os_device_find 验证设备已注册。
7.3 接口契约:接入外设需要提供什么
| 层 | 内容 | 职责 | 例子 |
|---|---|---|---|
| 驱动 | OS_DRIVER_INFO | 声明驱动名 + probe | my_uart_driver |
| 驱动 | probe 函数 | 解析 dev->info、分配/初始化/注册设备 | uart_probe |
| 驱动 | OS_DRIVER_DEFINE | 注册自动初始化(匹配时机) | OS_INIT_LEVEL_DEVICE |
| BSP | 配置结构体 + 实例 | 描述设备硬件参数 | uart0_cfg |
| BSP | OS_DEVICE_INFO | 声明设备名/驱动名/配置指针 | uart0_device |
7.4 适配检查清单(快速自查)
| 检查项 | 说明 |
|---|---|
链接脚本含 driver_table/device_table 段? | 无段则 OS_SECTION 不生效,信息收不进表 |
驱动名与设备 driver 名一致? | strcmp 精确匹配,拼写必须完全一致 |
probe 用 dev->info 强转的类型对? | 强转类型必须与 BSP 声明的配置结构体一致 |
设备结构体首成员是 os_device_t? | 否则 os_device_register(&xxx->parent) 非法 |
OS_DRIVER_DEFINE 初始化级别合适? | 设备需在应用前可用,级别要早于应用初始化 |
📝 本节小结
核心要点回顾
本节介绍了 OneOS 中 Bus 总线子系统的声明式驱动-设备匹配机制:
- 声明式注册:
OS_DRIVER_INFO/OS_DEVICE_INFO宏将驱动和设备信息放入链接器段 - 编译期收集:链接器将分散的驱动/设备信息聚合到
driver_table/device_table段(MDK/IAR/GCC 取段方式不同) - 自动匹配:通过名称匹配(
driver->name == device->driver,strcmp精确比较)自动调用 probe 函数 - 自动初始化:
OS_DRIVER_DEFINE宏结合OS_INIT_CALL实现驱动的自动初始化 - 双向匹配:支持驱动匹配设备(
driver_match_devices,一个驱动可匹配多设备)和设备匹配驱动(device_match_drivers,一个设备只配一个驱动并break) - 返回值固定为 0:两个匹配函数都
return 0,不表示匹配数量,不要依赖返回值 - 硬件适配:接入新外设 = 驱动层写 probe + 声明驱动,BSP 层声明设备,Bus 框架不用改
Bus 总线子系统是 OneOS 驱动框架的重要基础设施,它实现了驱动与设备的解耦,使 BSP 开发者只需声明设备信息,框架自动完成驱动匹配和初始化。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
忘记把设备放入 device_table 段 | 设备信息必须用 OS_DEVICE_INFO 宏声明,普通 static 变量不会被链接器收集 |
驱动名与设备 driver 名不匹配 | 匹配是精确字符串比较(driver->name == device->driver),拼写必须完全一致 |
漏写 OS_DRIVER_DEFINE 自动初始化 | 只用 OS_DRIVER_INFO 声明驱动后,若不调用匹配函数,驱动不会被激活;必须用 OS_DRIVER_DEFINE 注册自动初始化 |
info 指针悬空 | probe 会把 dev->info 强转成私有配置结构体指针,声明时必须指向有效的配置对象 |
混淆 OS_DRIVER_INFO 与 OS_DEVICE_INFO | 前者放驱动(含 probe),后者放设备(含 driver 需求名),两者放入的链接器段不同 |
| 误以为匹配函数返回匹配数量 | 两个匹配函数都固定 return 0,结果体现在 probe 是否被调用,不在返回值 |
设备结构体缺 parent 首成员 | probe 里要 os_device_register(&dev->parent, name),结构体首成员必须是 os_device_t |
| 链接脚本缺段 | MDK/IAR/GCC 链接脚本都要有 driver_table/device_table 段,否则 OS_SECTION 失效 |
下一步
掌握了 Bus 总线的声明式驱动-设备匹配机制后,接下来学习 USB 设备驱动:
- 5.24 USB设备:了解 OneOS 如何基于 TinyUSB 协议栈实现 USB 设备(CDC/HID 等)驱动。