最近在看Linux 2.6.21核心的I2C驅動,也在網上查了一下資料,有錯也有對,有些心得,記錄下來吧。裡面認識或許多有不當之處,還懇請指正。
1. I2C 協議
1.1 I2C匯流排工作原理
I2C匯流排是由資料線SDA和時鐘SCL構成的串列匯流排,各種被控制器件均並聯在這條匯流排上,每個器件都有一個唯一的地址識別,可以作為匯流排上的一個發送器件或接收器件(具體由器件的功能決定)
1.2 I2C匯流排的幾種訊號狀態
1. 空閑狀態:SDA和SCL都為高電平。
2. 開始條件(S):SCL為高電平時,SDA由高電平向低電平跳變,開始傳送資料。
3. 結束條件(P):SCL為低電平時,SDA由低電平向高電平跳變,結束傳送資料。
4. 資料有效:在SCL的高電平期間, SDA保持穩定,資料有效。SDA的改變只能發生在SCL的底電平期間。
5. ACK訊號: 資料轉送的過程中,接收器件每接收一個位元組資料要產生一個ACK訊號,向發送器件發出特定的低電平脈衝,表示已經收到資料。
1.3 I2C匯流排基本操作
I2C匯流排必須由主器件(通常為微控制器)控制,主器件產生串列時鐘(SCL),同時控制匯流排的傳輸方向,併產生開始和停止條件。
資料轉送中,首先主器件產生開始條件,隨後是器件的控制位元組(前七位是從器件的地址,最後一位為讀寫位 )。接下來是讀寫操作的資料,以及 ACK響應訊號。資料轉送結束時,主器件產生停止條件
2. Linux I2C 結構分析
2.1 層次分析
1. I2C Core
I2C Core用於維護Linux的I2C核心部分,其中維護了兩個靜態List,分別記錄系統中的I2C driver結構和I2C adapter結構。
static LIST_HEAD(adapters);
static LIST_HEAD(drivers);
I2C core提供介面函數,允許一個I2C adatper,I2C driver和I2C client初始化時在I2C core中進行註冊,以及退出時進行登出。具體可以參見i2c_core.c代碼。
同時還提供了I2C匯流排讀寫訪問的一般介面(具體的實現在與I2C控制器相關的I2C adapter中實現),主要應用在I2C裝置驅動中。
常用的主要是
i2c_master_send()
i2c_master_recv()
i2c_transfer()
2. I2C bus driver
匯流排驅動的職責,是為系統中每個I2C匯流排增加相應的讀寫方法。但是匯流排驅動本身並不會進行任何的通訊,它只是存在在那裡,等待裝置驅動調用其函數。
在系統開機時,首先裝載的是I2C匯流排驅動。一個匯流排驅動用於支援一條特定的I2C匯流排的讀寫。一個匯流排驅動通常需要兩個模組,一個struct i2c_adapter和一個struct i2c_algorithm來描述:
在 buses目錄下的i2c-pxa.c中實現了PXA的I2C匯流排適配器,I2C adapter 構造一個對I2C core層介面的資料結構,並通過介面函數向I2C core註冊一個控制器。I2C adapter主要實現對I2C匯流排訪問的演算法,iic_xfer() 函數就是I2C adapter底層對I2C匯流排讀寫方法的實現。同時I2C adpter 中還實現了對I2C控制器中斷的處理函數。
1) i2c-pxa.c定義了i2c_algorithm,並且實現了master的發送函數i2c_pxa_xfer(),以及裝置查詢匯流排的模式的函數i2c_pxa_functionality()
static const struct i2c_algorithm i2c_pxa_algorithm = {
.master_xfer = i2c_pxa_xfer,
.functionality = i2c_pxa_functionality,
};
2) i2c-pxa.c中,實現了i2c_adapter,主要是在定義pxa-i2c時進行初始化,並且i2c_pxa_probe()中進行填充parent指標,並且調用
ret = i2c_add_adapter(&i2c->adap);
進行添加。
static struct pxa_i2c i2c_pxa = {
.lock = SPIN_LOCK_UNLOCKED,
.adap = {
.owner = THIS_MODULE,
.algo = &i2c_pxa_algorithm,
.name = "pxa2xx-i2c.0",
.retries = 5,
},
};
總的來說,在i2c-pxa中,使用platform驅動模型,完成了i2c的匯流排兩種模組struct i2c_adapter和struct i2c_algorithm
3. I2C device driver
I2C只有匯流排驅動是不夠的,必須有裝置才能工作。這就是I2C device driver的必要性。I2C的device是有兩個模組來描述的,struct i2c_driver和struct i2c_client。
在介紹chips目錄下的device driver前有必要介紹一下i2c-dev.c檔案。
i2c-dev.c中提供了一個通用的I2C裝置的驅動程式,實現了字元類型裝置的提供者,對裝置的具體訪問是通過I2C adapter來實現的。構造一個對I2C core層介面的資料結構,通過介面函數向 I2C Core註冊一個I2C裝置驅動。同時構造一個對使用者層介面的資料結構,並通過介面函數向核心註冊為一個主裝置號為89的字元類型裝置。
static struct i2c_driver i2cdev_driver = {
.driver = {
.name = "dev_driver",
},
.id = I2C_DRIVERID_I2CDEV,
.attach_adapter = i2cdev_attach_adapter,
.detach_adapter = i2cdev_detach_adapter,
.detach_client = i2cdev_detach_client,
};
struct i2c_dev {
struct list_head list;
struct i2c_adapter *adap;
struct device *dev;
};
該檔案提供了使用者層對I2C裝置的訪問,包括open,read,write,ioctl,release等常規檔案操作,我們可以通過open函數開啟 I2C的裝置檔案,通過ioctl函數設定要訪問從裝置的地址,然後就可以通過 read和write函數完成對I2C裝置的讀寫操作。
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
註:通過I2C driver提供的通用方法可以訪問任何一個I2C的裝置,但是其中實現的read,write及ioctl等功能完全是基於一般裝置的實現,所有的操作資料都是基於位元組流,沒有明確的格式和意義。為了更方便和有效地使用I2C裝置,我們可以為一個具體的I2C裝置開發特定的I2C裝置驅動程式,在驅動中完成對特定的資料格式的解釋以及實現一些專用的功能。
在chips目錄下包含著各種device 的driver,完成各種從裝置的註冊。作為一般的I2C裝置,使用i2c-dev.c裡的操作足夠完成操作了。
當然如果不能完成,則需要獨立完成該驅動,這就是chips目錄下的代碼。因為i2c-dev.c已經實現了I2C裝置的檔案操作介面,所以只要實現struct i2c_driver就可以了。對於某些特殊的操作,可以使用command介面進行控制。 當然,對於i2介面的fm晶片,則將struct i2c_driver放在i2c的chips目錄下,而將另外fm操作相關的代碼放在了/media/radio目錄下了。在這個目錄下需要完成讀寫介面的完成,這個大部分使用V4L2架構。
繼續分析中……