1. Linux的
I2C驅(qū)動架構(gòu)
Linux中I2C總線的驅(qū)動分為兩個部分,總線驅(qū)動(BUS)和設(shè)備驅(qū)動(DEVICE)。其中總線驅(qū)動的職責(zé),是為系統(tǒng)中每個I2C總線增加相應(yīng)的讀寫方法。但是總線驅(qū)動本身并不會進(jìn)行任何的通訊,它只是存在在那里,等待設(shè)備驅(qū)動調(diào)用其函數(shù)。
設(shè)備驅(qū)動則是與掛在I2C總線上的具體的設(shè)備通訊的驅(qū)動。通過I2C總線驅(qū)動提供的函數(shù),設(shè)備驅(qū)動可以忽略不同總線控制器的差異,不考慮其實(shí)現(xiàn)細(xì)節(jié)地與硬件設(shè)備通訊。
1.1 總線
驅(qū)動
在系統(tǒng)開機(jī)時(shí),首先裝載的是I2C總線驅(qū)動。一個總線驅(qū)動用于支持一條特定的I2C總線的讀寫。一個總線驅(qū)動通常需要兩個模塊,一個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”的驅(qū)動。但這個模塊并未提供讀寫函數(shù),具體的讀寫方法由第二個模塊,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;
這個樣例給上述總線驅(qū)動增加了讀寫“算法”。通常情況下每個I2C總線驅(qū)動都定義一個自己的讀寫算法,但鑒于有些總線使用相同的算法,因而可以共用同一套讀寫函數(shù)。本例中的驅(qū)動定義了自己的讀寫算法模塊,起名叫“Au1550 algorithm”。
全部填妥后,通過調(diào)用:
i2c_add_adapter(i2c_adap);
將這兩個模塊注冊到操作系統(tǒng)里,總線驅(qū)動就算裝上了。對于AMD au1550,這部分已經(jīng)由AMD提供了。
1.2 設(shè)備
驅(qū)動
如前所述,總線驅(qū)動只是提供了對一條總線的讀寫機(jī)制,本身并不會去做通信。通信是由I2C設(shè)備驅(qū)動來做的,設(shè)備驅(qū)動透過I2C總線同具體的設(shè)備進(jìn)行通訊。一個設(shè)備驅(qū)動有兩個模塊來描述,struct i2c_driver和struct i2c_client。
當(dāng)系統(tǒng)開機(jī)、I2C總線驅(qū)動裝入完成后,就可以裝入設(shè)備驅(qū)動了。首先裝入如下結(jié)構(gòu):
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函數(shù)就會被調(diào)用。在其中可以遍歷系統(tǒng)中的每個i2c總線驅(qū)動,探測想要訪問的設(shè)備:
static int tuner_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, tuner_attach);
}
注意探測可能會找到多個設(shè)備,因而不僅一個I2C總線可以掛多個不同類型的設(shè)備,一個設(shè)備驅(qū)動也可以同時(shí)為掛在多個不同I2C總線上的設(shè)備服務(wù)。
每當(dāng)設(shè)備驅(qū)動探測到了一個它能支持的設(shè)備,它就創(chuàng)建一個struct i2c_client來標(biāo)識這個設(shè)備:
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來驅(qū)動的一個設(shè)備。它將總線驅(qū)動與設(shè)備驅(qū)動,以及設(shè)備地址綁定在了一起。一個i2c_client就代表著一個I2C設(shè)備。
當(dāng)?shù)玫?SPAN lang=EN-US>I2C設(shè)備后,就可以直接對此設(shè)備進(jìn)行讀寫:
/*
* 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);
與通常意義上的讀寫函數(shù)一樣,這兩個函數(shù)對i2c_client指針指定的設(shè)備,讀寫int個char。返回值為讀寫的字節(jié)數(shù)。對于我們現(xiàn)有的SLIC的驅(qū)動,只要將最后要往總線上進(jìn)行讀寫的數(shù)據(jù)引出傳輸?shù)竭@兩個函數(shù)中,移植工作就算完成了,我們將得到一個Linux版的I2C設(shè)備驅(qū)動。