Linux I2C 子系統核心在 drivers/i2c/i2c-core.c 中實現總線類型註冊、設備/驅動匹配、probe 掛載等關鍵邏輯。本文深度解析 struct bus_type i2c_bus_type 註冊、i2c_device_match 匹配規則、i2c_device_probe 掛載流程、虛擬驅動 dummy_driver 作用,揭示 I2C 設備如何被內核發現並綁定驅動。
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match, // 核心匹配函數
.probe = i2c_device_probe, // 設備掛載入口
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
static int __init i2c_init(void) {
int retval;
retval = bus_register(&i2c_bus_type); // 1. 向驅動模型核心註冊總線類型
if (retval) return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; }
#endif
retval = i2c_add_driver(&dummy_driver); // 2. 註冊虛擬驅動 (關鍵!)
if (retval) goto class_err;
return 0;
}
postcore_initcall(i2c_init); // 早期初始化,確保其他子系統可註冊 I2C 驅動
static int i2c_device_match(struct device *dev, struct device_driver *drv) {
struct i2c_client *client = i2c_verify_client(dev); // 確認是 I2C 設備
struct i2c_driver *driver;
if (!client) return 0; // 非 I2C 設備直接不匹配
driver = to_i2c_driver(drv); // 轉換為 I2C 驅動結構體
// 關鍵:若驅動定義了 id_table,則按名稱匹配
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0; // 無 id_table 則不自動匹配 (需人工綁定)
}
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client) {
while (id->name[0]) { // 遍歷 id_table 直到空名稱結束
if (strcmp(client->name, id->name) == 0) // 純字符串比對
return id;
id++;
}
return NULL;
}
client->name 來源:設備樹 compatible 最後一段、板級註冊 i2c_board_info.name、i2c_new_device 指定名稱。driver->id_table 定義於驅動代碼中,列出支援的設備名稱字符串。i2c_device_id 指針,包含 driver_data 供驅動識別具體變體。
static int i2c_device_probe(struct device *dev) {
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
int status = 0;
// 獲取匹配到的 id_table 條目 (含 driver_data)
const struct i2c_device_id *id = i2c_match_id(driver->id_table, client);
// 調用驅動的 probe 函數,傳遞 client 和 id
if (driver->probe)
status = driver->probe(client, id);
return status;
}
此時 client->adapter 已指向所屬 i2c_adapter,驅動可通過 i2c_transfer() / i2c_smbus_* 與硬體通訊。
static struct i2c_driver dummy_driver = {
.driver.name = "dummy",
};
i2c_add_driver(&dummy_driver); // 在 i2c_init() 中調用
作用:確保 I2C 總線類型註冊後至少有一個驅動附掛,避免總線處於「無驅動」狀態導致某些核心邏輯 (如設備自動探測、sysfs 目錄創建) 異常。實際上不處理任何真實設備。
板級代碼/設備樹解析最終調用:
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) {
// 分配 i2c_client、設置 name/addr/flags/platform_data
// device_register(&client->dev) → 觸發 bus_match → i2c_device_match → probe
}
of_i2c_register_devices(adap) 遍歷 adapter 節點下的子節點。i2c_board_info:name = compatible 最後一段、addr = reg、platform_data = NULL (驅動自行 of_property_read)。i2c_new_device() 註冊設備,觸發匹配流程。I2C 總線註冊機制遵循 Linux 統一設備模型:bus_register 註冊總線類型 → i2c_add_driver 註冊驅動 (含 id_table) → i2c_new_device 註冊設備 (含 name) → 核心調用 bus_match → i2c_device_match 字符串比對 id_table → 匹配成功 → i2c_device_probe 調用驅動 probe。虛擬 dummy_driver 保證總線就緒。現代開發核心在於正確編寫設備樹 compatible、驅動 of_match_table 與 id_table,實現無板級代碼的自動綁定。