I2C是Phillips開發的2線的串行總線協議。通常應用在嵌入式系統中讓不同的組件通信,PC主板可以通過I2C來與不同的傳感器通信。這些傳感器通常報告風扇速度,處理器溫度和整個硬件系統的信息,這個協議也可以用在RAM chips上,向操作系統提供DIMM的信息。
在2.0時I2C的kernel源碼不在內核裡的,2.4內核包括了一點對I2C的支持,主要是視頻驅動。
在2.6內核裡,大量的I2C代碼加入到了內核裡。感謝很多內核開發者的努力,他們讓接口更容易被內核社區接受。有些驅動仍在外部的CVS樹裡,沒有放入kernel.org裡,但是他們被移植只是時間的問題。
I2C 的kernel源碼被分成幾個部分:the I2C core, I2C bus drivers,I2C algorithm drivers and I2C chip drivers。我們將忽略I2C core部分,而是關注如何編寫a bus and algorithm 驅動。
I2C Bus Drivers 總線驅動
I2C總線驅動用一個結構i2c_adapter來描述,該結構在文件include/linux/i2c.h裡定義。
以下的字段需要在總線驅動裡設置:
struct module *owner; -set to the value (THIS_MODULE) that allows the proper module reference counting.
unsigned int class; -the type of I2C class devices that this driver supports. Usually this is set to the value I2C_ADAP_CLASS_SMBUS.
struct i2c_algorithm *algo; -a pointer to the struct i2c_algorithm structure that describes the way data is transferred through this I2C bus controller. More information on this structure is provided below.
char name[I2C_NAME_SIZE]; -set to a descriptive name of the I2C bus driver. This value shows up in the sysfs filename associated with this I2C adapter.
以下的代碼來自一個I2C adapter驅動的例子tiny_i2c_adap.c,
可以在the Linux Journal FTP site [ftp.ssc.com/pub/lj/listings/issue116/7136.tgz [1]] 獲得。
來看看struct i2c_adapter 如何被設置:
static struct i2c_adapter tiny_adapter = {
.owner = THIS_MODULE,
.class = I2C_ADAP_CLASS_SMBUS,
.algo = &tiny_algorithm,
.name = "tiny adapter",
};
要注冊I2C adapter,驅動調用函數i2c_add_adapter,參數是struct i2c_adapter指針。
retval = i2c_add_adapter(&tiny_adapter);
要注銷一個I2C adapter, 驅動調用i2c_del_adapter,參數是struct i2c_adapter指針。像這樣:
i2c_del_adapter(&tiny_adapter);
I2C Algorithm Drivers
I2C algorithm是為了I2C總線驅動和I2C總線之間能夠對話,很多I2C總線驅動定義和使用它們自己的Algorithm。因為他們聯系緊密,實現了總線驅動如何和特定類型的硬件對話。對於一些I2C總線驅動來說,很多I2C algorithm已經寫好了。
其中的一些例子:
ITE adapters, 在目錄drivers/i2c/i2c-algo-ite.c;
IBM PPC 405 adapters 在目錄drivers/i2c/i2c-algo-ibm_ocp.c;
a generic I2C bit shift algorithm 在目錄drivers/i2c/i2c-algo-bit.c;
所有這些已經寫好的Algorithm有它們自己的函數,I2C總線驅動需要注冊來使用。
要想得到這方面更多的信息,可以查看內核源碼目錄drivers/i2c/i2c-algo-*.c。
我們將創建自己的I2C algorithm驅動,一個algorithm驅動被結構i2c_algorithm 定義,該結構被定義在
include/linux/i2c.h,以下是常用字段的描述:
char name[32];: the name of the algorithm.
unsigned int id;: description of the type of algorithm this structure defines. These different types are defined in the include/linux/i2c-id.h file and start with the characters I2C_ALGO_.
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], int num);: a function pointer to be set if this algorithm driver can do I2C direct-level accesses. If it is set, this function is called whenever an I2C chip driver wants to communicate with the chip device. If it is set to NULL, the smbus_xfer function is used instead.
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);: a function pointer to be set if this algorithm driver can do SMB bus accesses. Most PCI-based I2C bus drivers are able to do this, and they should set this function pointer. If it is set, this function is called whenever an I2C chip driver wants to communicate with the chip device. If it is set to NULL, the master_xfer function is used instead.
u32 (*functionality) (struct i2c_adapter *);: a function pointer called by the I2C core to determine what kind of reads and writes the I2C adapter driver can do.
在我們的I2C adapter驅動裡,i2c_adapter結構引用了tiny_algorithm的變量,結構定義如下:
static struct i2c_algorithm tiny_algorithm = {
.name = "tiny algorithm",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = tiny_access,
.functionality = tiny_func,
};
函數tiny_func很簡單,告訴I2C core這個algorithm 能支持什麼類型的I2C messages。
在這個驅動裡,我們要支持很多不同的I2C messages類型:
static u32 tiny_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
所有的I2C message類型都在include/linux/i2c.h定義,以I2C_FUNC_開頭。
當I2C client驅動要和I2C總線對話是,函數tiny_access會被調用。我們的例子函數很簡單,
它僅僅記錄I2C chip驅動的請求到syslog,同時返回成功給調用者。