第一百三十九章:总线
合道期涉及内核源码:
一
林小源沿着设备山脉的一条大路继续前行,路面上刻着各种总线的标志——PCI、USB、I2C、platform。每种标志的颜色不同,PCI 是深蓝色,USB 是白色,I2C 是绿色,platform 是橙色。
"总线到底是什么?"林小源问守山老者。
老者还没来得及回答,一个身穿深蓝色铠甲的中年人从 PCI 路口走了出来。他的铠甲上刻着一张巨大的匹配表——左边是设备列表,右边是驱动列表,中间是无数根连接线。
"我是总线。"中年人说,声音沉稳有力,"设备和驱动之间的纽带。没有我,设备永远找不到自己的驱动,驱动也永远找不到自己能控制的设备。"
"你怎么匹配的?"林小源问。
"每种总线有自己的匹配规则。"中年人指向 PCI 路口,"PCI 总线用 Vendor ID 和 Device ID 匹配——设备的身份证号跟驱动的 id_table 对上了,就算匹配成功。"他又指向 USB 路口,"USB 总线用 Vendor/Product ID。"指向 I2C 路口,"I2C 用设备地址。"最后指向 platform 路口,"platform 总线用设备树的 compatible 字符串。"
/*
* 总线类型:
*
* PCI 总线:
* 连接 CPU 和外设
* 通过 Vendor/Device ID 匹配
*
* USB 总线:
* 连接 USB 设备
* 通过 Vendor/Product ID 匹配
*
* I2C 总线:
* 低速设备
* 通过设备地址匹配
*
* platform 总线:
* 集成在 SoC 中的设备
* 通过设备树匹配
*
* 总线的职责:
* - 管理设备和驱动的匹配
* - 提供设备枚举
* - 处理电源管理
*/
printf("=== 总线 — 连接设备和驱动 ===\n\n");
printf("总线类型:\n\n");
printf("PCI 总线:\n");
printf(" 连接 CPU 和外设\n");
printf(" 通过 Vendor/Device ID 匹配\n\n");
printf("USB 总线:\n");
printf(" 连接 USB 设备\n");
printf(" 通过 Vendor/Product ID 匹配\n\n");
printf("I2C 总线:\n");
printf(" 低速设备\n");
printf(" 通过设备地址匹配\n\n");
printf("platform 总线:\n");
printf(" 集成在 SoC 中的设备\n");
printf(" 通过设备树匹配\n\n");
printf("--- 总线的职责 ---\n");
printf("1. 匹配:\n");
printf(" 设备注册时找驱动\n");
printf(" 驱动注册时找设备\n\n");
printf("2. 枚举:\n");
printf(" 扫描总线上的设备\n\n");
printf("3. 电源管理:\n");
printf(" 管理设备电源状态\n\n");
printf("--- 匹配过程 ---\n");
printf("设备注册:\n");
printf(" device_register(dev)\n");
printf(" → bus_for_each_drv()\n");
printf(" → match(dev, drv)\n");
printf(" → probe(dev)\n\n");
printf("驱动注册:\n");
printf(" driver_register(drv)\n");
printf(" → bus_for_each_dev()\n");
printf(" → match(dev, drv)\n");
printf(" → probe(dev)\n");#include <stdio.h>
/*
* 总线类型:
*
* PCI 总线:
* 连接 CPU 和外设
* 通过 Vendor/Device ID 匹配
*
* USB 总线:
* 连接 USB 设备
* 通过 Vendor/Product ID 匹配
*
* I2C 总线:
* 低速设备
* 通过设备地址匹配
*
* platform 总线:
* 集成在 SoC 中的设备
* 通过设备树匹配
*
* 总线的职责:
* - 管理设备和驱动的匹配
* - 提供设备枚举
* - 处理电源管理
*/
int main() {
printf("=== 总线 — 连接设备和驱动 ===\n\n");
printf("总线类型:\n\n");
printf("PCI 总线:\n");
printf(" 连接 CPU 和外设\n");
printf(" 通过 Vendor/Device ID 匹配\n\n");
printf("USB 总线:\n");
printf(" 连接 USB 设备\n");
printf(" 通过 Vendor/Product ID 匹配\n\n");
printf("I2C 总线:\n");
printf(" 低速设备\n");
printf(" 通过设备地址匹配\n\n");
printf("platform 总线:\n");
printf(" 集成在 SoC 中的设备\n");
printf(" 通过设备树匹配\n\n");
printf("--- 总线的职责 ---\n");
printf("1. 匹配:\n");
printf(" 设备注册时找驱动\n");
printf(" 驱动注册时找设备\n\n");
printf("2. 枚举:\n");
printf(" 扫描总线上的设备\n\n");
printf("3. 电源管理:\n");
printf(" 管理设备电源状态\n\n");
printf("--- 匹配过程 ---\n");
printf("设备注册:\n");
printf(" device_register(dev)\n");
printf(" → bus_for_each_drv()\n");
printf(" → match(dev, drv)\n");
printf(" → probe(dev)\n\n");
printf("驱动注册:\n");
printf(" driver_register(drv)\n");
printf(" → bus_for_each_dev()\n");
printf(" → match(dev, drv)\n");
printf(" → probe(dev)\n");
return 0;
}二
"你说有四种总线——PCI、USB、I2C、platform。"林小源掰着手指,"它们有什么区别?为什么需要这么多总线?"
中年人笑了:"因为不同的设备有不同的连接方式。"
他指着 PCI 路口:"PCI 是高速总线,连接 CPU 和外设——网卡、显卡、RAID 控制器。这些设备性能要求高,需要高速的总线连接。"
指向 USB 路口:"USB 是通用接口,什么设备都能插——键盘、鼠标、U盘、摄像头。它的优势是通用性和热插拔。"
指向 I2C 路口:"I2C 是低速总线,只需要两根线——SDA 和 SCL。适合传感器、EEPROM、RTC 这些低速设备。引脚少,布线简单。"
最后指向 platform 路口:"platform 总线最特殊——它连接的设备不是插上去的,而是直接集成在 SoC 芯片里的。UART 控制器、I2C 控制器、GPIO 控制器——这些设备跟 CPU 在同一块芯片上,不需要外部总线。"
"所以 platform 总线其实不是一条物理总线?"
"对。"中年人点头,"platform 总线是逻辑上的总线。它的设备通过设备树描述,而不是通过物理扫描发现。这也是为什么 platform 设备需要用 compatible 字符串来匹配驱动。"
三
"每种总线的 函数不一样,"林小源说,"那总线本身是怎么注册的?"
"用 。"中年人说,"每种总线类型定义一个 结构体,里面有 、、 等回调函数。然后调用 注册到内核。"
他伸手在空中画了一个结构体:
struct bus_type {
const char *name;
int (*match)(struct device *dev, struct device_driver *drv);
int (*probe)(struct device *dev);
void (*remove)(struct device *dev);
// ...
};"match 是核心。"中年人说,"当设备或驱动注册时,总线调用 match 来判断它们是否匹配。PCI 的 match 比较 Vendor/Device ID,platform 的 match 比较 compatible 字符串。match 返回 1 表示匹配成功,返回 0 表示不匹配。"
"匹配成功后呢?"
"调用 probe。"中年人说,"probe 是驱动的初始化入口——映射寄存器、分配资源、注册中断。匹配只是'认识',probe 才是'开始工作'。"
林小源看着那四种总线的路口,心中渐渐清晰:总线是设备模型的中枢神经——它定义了匹配规则,管理着设备和驱动的配对,是整个设备模型能够自动运转的关键。
道藏笔记
内核启示
总线连接设备和驱动。
总线类型:
- PCI — CPU 和外设
- USB — USB 设备
- I2C — 低速设备
- platform — SoC 集成设备
总线的职责:
- 匹配 — 设备和驱动的匹配
- 枚举 — 扫描设备
- 电源管理 — 管理电源状态
总线是"纽带"——连接设备和驱动。
总线之试
驱动世界要登记一条新的总线时,本章提到的注册函数是什么?