Linux 設備模型將裝置與驅動透過總線綁定。本文以 S3C2410 RTC 驅動為例,追蹤 platform_driver_register() 至 driver_register() 的完整呼叫鏈,揭示總線、驅動、裝置三者互動機制。
static struct platform_driver s3c2410_rtc_driver = {
.probe = s3c_rtc_probe,
.remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};
.name 將成為 /sys/bus/platform/drivers/s3c2410-rtc 目錄名,也是匹配裝置的關鍵鍵值。
int platform_driver_register(struct platform_driver *drv) {
drv->driver.bus = &platform_bus_type; // 關鍵:綁定總線類型
if (drv->probe) drv->driver.probe = platform_drv_probe;
if (drv->remove) drv->driver.remove = platform_drv_remove;
if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend) drv->driver.suspend = platform_drv_suspend;
if (drv->resume) drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver); // 轉入核心驅動註冊
}
關鍵點:將 platform_driver 專用回調包裝為通用 device_driver 回調,並指定 bus = &platform_bus_type。總線類型決定後續匹配規則(match() 回調)。
int driver_register(struct device_driver *drv) {
// 1. 相容性檢查:避免同時定義 bus->probe 與 drv->probe
if ((drv->bus->probe && drv->probe) || ...)
printk(KERN_WARNING "Driver '%s' needs updating...", drv->name);
// 2. 重複註冊檢查
struct device_driver *other = driver_find(drv->name, drv->bus);
if (other) { put_driver(other); return -EEXIST; }
// 3. 加入總線 kset,建立 sysfs 目錄與連結
int ret = bus_add_driver(drv);
if (ret) return ret;
// 4. 處理 attribute groups(本例無)
ret = driver_add_groups(drv, drv->groups);
if (ret) bus_remove_driver(drv);
return ret;
}
struct device_driver *driver_find(const char *name, struct bus_type *bus) {
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
if (k) {
struct driver_private *priv = to_driver(k);
return priv->driver;
}
return NULL;
}
總線內部維護 drivers_kset(kobject 集合),透過 kobject 名稱索引驅動。kset_find_obj() 遍歷鏈表比對名稱。
platform_bus_type:定義 match()、uevent() 等總線行為。device_driver:通用驅動物件,掛載於 bus->p->drivers_kset。platform_driver:Platform 專用封裝,含 probe/remove/shutdown/suspend/resume。driver_private:核心內部封裝,持有 struct device_driver *driver 並掛載 kobject。driver.name 必須在同總線下唯一,否則註冊失敗返回 -EEXIST。struct platform_device * 再呼叫驅動原始回調,驅動開發者無需感知總線細節。/sys/bus/platform/drivers/<name>,可透過 bind/unbind 手動控制綁定。理解此流程,有助於除錯驅動未被 Probe、總線匹配失敗、sysfs 節點異常等問題。