Linux 裝置驅動模型以「總線-裝置-驅動」三元架構為核心。驅動註冊後,其 probe() 何時被調用?關鍵在於總線的 match() 函數。本文從 driver_register() 入手,逐層追蹤原始碼路徑,揭示 probe() 被調用的完整呼叫鏈與判斷邏輯。
driver_register()int driver_register(struct device_driver *drv) {
klist_init(&drv->klist_devices, ...);
init_completion(&drv->unloaded);
return bus_add_driver(drv); // 關鍵轉向總線層
}
bus_add_driver()driver_attach(drv) → 遍歷總線上所有裝置嘗試匹配。driver_attach() → __driver_attach()void driver_attach(struct device_driver *drv) {
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
static int __driver_attach(struct device *dev, void *data) {
struct device_driver *drv = data;
if (!dev->driver) // 裝置尚未綁定驅動
driver_probe_device(drv, dev);
return 0;
}
driver_probe_device()
int driver_probe_device(struct device_driver *drv, struct device *dev) {
// 1. 總線 match 函數判斷是否匹配
if (drv->bus->match && !drv->bus->match(dev, drv))
return -ENODEV; // 不匹配,直接返回
// 2. 匹配成功,真正調用 probe
if (drv->probe) {
int ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
return ret; // probe 失敗
}
dev->driver = drv; // 綁定成功
}
return 0;
}
match() 決定生死drv->bus->match(dev, drv) 返回 1,probe() 才會被調用。match():
static int pci_bus_match(struct device *dev, struct device_driver *drv) {
struct pci_dev *pdev = to_pci_dev(dev);
struct pci_driver *pdrv = to_pci_driver(drv);
return pci_match_device(pdrv, pdev) ? 1 : 0; // 比對 id_table
}
PCI 驅動在 struct pci_driver 中定義 id_table,核心自動比對廠商/裝置 ID。
static int platform_match(struct device *dev, struct device_driver *drv) {
return strncmp(dev_name(dev), drv->name, strlen(drv->name)) == 0; // 名稱比對
}
Platform 裝置與驅動透過名稱字串比對綁定(常見於 SoC 內建外設)。
really_probe()(新版核心)新版核心(如 5.x)在 driver_probe_device() 內部委派給 really_probe():
static int really_probe(struct device *dev, struct device_driver *drv) {
// 1. 總線層 probe(少用)
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) goto probe_failed;
}
// 2. 驅動層 probe(主流)
else if (drv->probe) {
ret = drv->probe(dev);
if (ret) goto probe_failed;
}
// 成功綁定
dev->driver = drv;
return 0;
probe_failed:
dev->driver = NULL;
return ret;
}
driver_register()
└─ bus_add_driver()
└─ driver_attach()
└─ bus_for_each_dev() → __driver_attach()
└─ driver_probe_device()
├─ bus->match(dev, drv) ? → NO → 結束
└─ YES → drv->probe(dev) → 成功 → dev->driver = drv
bus->match 邏輯(id_table、名稱、of_match_table 等)。.name 與 DTS/板級註冊的裝置名稱完全一致。id_table 需包含目標裝置的 Vendor/Device ID,且 PCI_DEVICE() 巨集正確展開。of_match_table 配合 compatible 屬性,由 of_driver_match_device() 在 match 階段比對。