Linux I2C 子系統將驅動分為總線驅動與裝置驅動兩層,實現硬體抽象與複用。本文以 AU1550 處理器為例,詳細說明 i2c_adapter/i2c_algorithm 總線層實作、i2c_driver/i2c_client 裝置層實作、掃描匹配機制、i2c_master_send/recv 通訊介面,並提供完整程式碼範本。
static struct i2c_adapter pb1550_board_adapter = {
.name = "pb1550 adapter",
.id = I2C_HW_AU1550_PSC,
.algo = NULL, // 稍後指定
.algo_data = &pb1550_i2c_info,
.inc_use = pb1550_inc_use,
.dec_use = pb1550_dec_use,
.client_register = pb1550_reg,
.client_unregister = pb1550_unreg,
.client_count = 0,
};
static struct i2c_algorithm au1550_algo = {
.name = "Au1550 algorithm",
.id = I2C_ALGO_AU1550,
.master_xfer = au1550_xfer, // 核心收發函數
.functionality = au1550_func, // 功能位圖
};
pb1550_board_adapter.algo = &au1550_algo;
i2c_add_adapter(&pb1550_board_adapter);
static struct i2c_driver driver = {
.name = "i2c TV tuner driver",
.id = I2C_DRIVERID_TUNER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tuner_probe, // 掃描入口
.detach_client = tuner_detach,
.command = tuner_command,
};
i2c_add_driver(&driver);
static int tuner_probe(struct i2c_adapter *adap) {
return i2c_probe(adap, &addr_data, tuner_attach);
}
i2c_probe() 遍歷位址清單,對每個位址呼叫 tuner_attach()。
struct i2c_client *new_client = kzalloc(sizeof(*new_client), GFP_KERNEL);
new_client->addr = address; // 裝置位址
new_client->adapter = adapter; // 所屬總線
new_client->driver = &driver; // 綁定驅動
err = i2c_attach_client(new_client);
i2c_client 將「適配器 + 位址 + 驅動」三方綁定,代表一個具體裝置實例。
extern int i2c_master_send(struct i2c_client *, const char *, int);
extern int i2c_master_recv(struct i2c_client *, char *, int);
client 指定裝置、緩衝區、長度i2c_transfer() 支援多訊息原子傳輸i2c_algorithm)/dev/i2c-N 直接存取(i2c-dev.c)將既有裸機 I2C 驅動移植到 Linux,核心工作是:
master_xfer(對應原 I2C_Start/Write/Read/Stop)i2c_master_send/recv