Linux的I2C驅動架構
1. Linux的I2C驅動架構Linux中I2C匯流排的驅動分為兩個部分,匯流排驅動(BUS)和裝置驅動(DEVICE)。其中匯流排驅動的職責,是為系統中每個I2C匯流排增加相應的讀寫方法。但是匯流排驅動本身並不會進行任何的通訊,它只是存在在那裡,等待裝置驅動調用其函數。
裝置驅動則是與掛在I2C匯流排上的具體的裝置通訊的驅動。通過I2C匯流排驅動提供的函數,裝置驅動可以忽略不同匯流排控制器的差異,不考慮其實現細節地與硬體裝置通訊。
1.1 匯流排驅動在系統開機時,首先裝載的是I2C匯流排驅動。一個匯流排驅動用於支援一條特定的I2C匯流排的讀寫。一個匯流排驅動通常需要兩個模組,一個struct i2c_adapter和一個struct i2c_algorithm來描述:
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,
};
這個範例掛接了一個叫做“pb1550 adapter”的驅動。但這個模組並未提供讀寫函數,具體的讀寫方法由第二個模組,struct i2c_algorithm提供。
static struct i2c_algorithm au1550_algo = {
.name = "Au1550 algorithm",
.id = I2C_ALGO_AU1550,
.master_xfer = au1550_xfer,
.functionality = au1550_func,
};
i2c_adap->algo = &au1550_algo;
這個範例給上述匯流排驅動增加了讀寫“演算法”。通常情況下每個I2C匯流排驅動都定義一個自己的讀寫演算法,但鑒於有些匯流排使用相同的演算法,因而可以共用同一套讀寫函數。本例中的驅動定義了自己的讀寫演算法模組,起名叫“Au1550 algorithm”。
全部填妥後,通過調用:
i2c_add_adapter(i2c_adap);
將這兩個模組註冊到作業系統裡,匯流排驅動就算裝上了。對於AMD au1550,這部分已經由AMD提供了。
1.2 裝置驅動如前所述,匯流排驅動只是提供了對一條匯流排的讀寫機制,本身並不會去做通訊。通訊是由I2C裝置驅動來做的,裝置驅動透過I2C匯流排同具體的裝置進行通訊。一個裝置驅動有兩個模組來描述,struct i2c_driver和struct i2c_client。
當系統開機、I2C匯流排驅動裝入完成後,就可以裝入裝置驅動了。首先裝入如下結構:
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);
這個i2c_driver一旦裝入完成,其中的attach_adapter函數就會被調用。在其中可以遍曆系統中的每個i2c匯流排驅動,探測想要訪問的裝置:
static int tuner_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, tuner_attach);
}
注意探測可能會找到多個裝置,因而不僅一個I2C匯流排可以掛多個不同類型的裝置,一個裝置驅動也可以同時為掛在多個不同I2C匯流排上的裝置服務。
每當裝置驅動探測到了一個它能支援的裝置,它就建立一個struct i2c_client來標識這個裝置:
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &driver;
/* Tell the I2C layer a new client has arrived */
err = i2c_attach_client(new_client);
if (err)
goto error;
可見,一個i2c_client代表著位於adapter匯流排上,地址為address,使用driver來驅動的一個裝置。它將匯流排驅動與裝置驅動,以及裝置地址綁定在了一起。一個i2c_client就代表著一個I2C裝置。
當得到I2C裝置後,就可以直接對此裝置進行讀寫:
/*
* The master routines are the ones normally used to transmit data to devices
* on a bus (or read from them). Apart from two basic transfer functions to
* transmit one message at a time, a more complex version can be used to
* transmit an arbitrary number of messages without interruption.
*/
extern int i2c_master_send(struct i2c_client *,const char* ,int);
extern int i2c_master_recv(struct i2c_client *,char* ,int);
與通常意義上的讀寫函數一樣,這兩個函數對i2c_client指標指定的裝置,讀寫int個char。傳回值為讀寫的位元組數。對於我們現有的SLIC的驅動,只要將最後要往匯流排上進行讀寫的資料引出傳輸到這兩個函數中,移植工作就算完成了,我們將得到一個Linux版的I2C裝置驅動。