這次來研究核心內建I2C驅動的代碼,在深入代碼之前,首先簡單瞭解一下I2C核心資料結構的相互關係。由此來展開,也許能夠對驅動代碼有更好地理解。軟體資料結構的設計、資料結構之間的關係就至少應該描述硬體物理串連的這種組織關係。Linux的i2c 架構中各個部分的關係如所示:
核心中 i2c 相關代碼可以分為三個層次:
1. i2c 架構:i2c.h 和i2c-core.c 為i2c 架構的主體,提供了核心資料結構的定義、i2c 適配器驅動和裝置驅動的註冊、登出管理,i2c 通訊方法上層的、與具體適配器無關的代碼、檢測裝置地址的上層代碼等;i2c-dev.c 用於建立i2c 適配器的/dev/i2c/%d 裝置節點,提供i2c 裝置存取方法等。
2. i2c 匯流排適配器驅動:定義描述具體i2c 匯流排適配器的i2c_adapter 資料結構、實現在具體i2c 適配器上的i2c 匯流排通訊方法,並由i2c_algorithm 資料結構進行描述。
3. i2c 裝置驅動:定義描述具體裝置的i2c_client 和可能的私人資料結構、藉助i2c 架構的i2c_probe 函數實現註冊裝置的attach_adapter 方法、提供裝置可能使用的位址範圍、以及裝置地址檢測成功後建立i2c_client 資料結構的回呼函數。
I2C主要資料結構如下:
1. 一個 i2c 裝置的驅動程式由i2c_driver 資料結構描述,定義於include/linux/i2c.h:
struct i2c_driver {<br />char name[32]; //最大32位元組的字串<br />int id; //id 可選0xf000 到0xffff 中的任一數值<br />unsigned int flags; //一般直接設定為I2C_DF_NOTIFY<br />int (*attach_adapter)(struct i2c_adapter *);<br />int (*detach_client)(struct i2c_client *);<br />int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);<br />void (*inc_use)(struct i2c_client *client);<br />void (*dec_use)(struct i2c_client *client);<br />};<br />
attach_adapter 回呼函數在安裝i2c 裝置驅動程式模組時、或者在安裝i2c 適配器驅動
程式模組時被調用,用於檢測、認領裝置並為裝置分配i2c_client 資料結構。
detach_client 方法在卸載適配器或裝置驅動程式模組時被調用,用於從匯流排上登出裝置、並釋放i2c_client 及相應的私人資料結構。
inc_use 和dec_use 所指向的函數用於改變i2c 裝置驅動程式模組的引用計數。注意不要直接調用i2c_driver資料結構中的這兩個方法,而要通過如下函數調用路徑:
i2c_use_client > i2c_inc_use_client > inc_use
i2c_release_client > i2c_dec_use_client > dec_use
2. 一個 i2c 裝置由i2c_client 資料結構進行描述:
struct i2c_client {<br />char name[32];<br />int id;<br />unsigned int flags; /* div., see below */<br />unsigned int addr; /* chip address - NOTE: 7bit addresses are stored in the */<br />/* _LOWER_ 7 bits of this char */<br />struct i2c_adapter *adapter; /* the adapter we sit on */<br />struct i2c_driver *driver; /* and our access routines */<br />void *data; /* for the clients */<br />int usage_count; /* How many accesses currently to the client */<br />};<br />
在安裝適配器或者裝置的驅動程式時通過裝置驅動程式 i2c_driver 中的attach_adapter 函數檢測裝置地址。如果檢測成功則調用裝置驅動程式提供的回呼函數建立描述裝置的i2c_client 資料結構,並將其中的driver指標指向裝置驅動程式的i2c_driver 資料結構。這樣將來就可以使用i2c_driver 中的登出裝置和控制引用計數的方法了。
3. 一個 i2c 適配器由i2c_adapter 資料結構描述:
struct i2c_adapter {<br />char name[32];<br />unsigned int id; /* == is algo->id | hwdep.struct->id, for registered values see below */<br />struct i2c_algorithm *algo; /* the algorithm to access the bus */<br />void *algo_data;<br />void (*inc_use)(struct i2c_adapter *);<br />void (*dec_use)(struct i2c_adapter *);<br />int (*client_register)(struct i2c_client *);<br />int (*client_unregister)(struct i2c_client *);<br />void *data; /* private data for the adapter */<br />struct semaphore lock;<br />unsigned int flags; /* flags specifying div. data */<br />struct i2c_client *clients[I2C_CLIENT_MAX];<br />int client_count;<br />int timeout;<br />int retries;<br />#ifdef CONFIG_PROC_FS<br />/* No need to set this when you initialize the adapter */<br />int inode;<br />#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)<br />struct proc_dir_entry *proc_entry;<br />#endif<br />#endif /* def CONFIG_PROC_FS */<br />};
在 i2c_adapter 資料結構中設計了clients 指標數組,指向該匯流排上每個裝置的i2c_client 資料結構。由於一條i2c 匯流排上最多隻有I2C_CLENT_MAX 個裝置,所以可以使用靜態數組(題外話,如果相關資料結構的個數是未知的,鏈表顯然是更好的選擇)。lock 訊號量用於實現對i2c 匯流排的互斥訪問:在訪問i2c 匯流排上的任一裝置期間當前進程必須首先獲得該訊號量,並且在阻塞等待i2c 操作完成期間不釋放。
4.具體 i2c 適配器的通訊方法由i2c_algorithm 資料結構進行描述:
struct i2c_algorithm {<br />char name[32]; /* textual description */<br />unsigned int id;<br />int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);<br />int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,<br />unsigned short flags, char read_write,<br />u8 command, int size, union i2c_smbus_data * data);<br />int (*slave_send)(struct i2c_adapter *,char*,int);<br />int (*slave_recv)(struct i2c_adapter *,char*,int);<br />int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);<br />u32 (*functionality) (struct i2c_adapter *);<br />};
master_xfer/smbus_xfer 指標指向i2c 適配器驅動程式模組實現的i2c 通訊協定或者smbus 通訊協定。由下文分析可見在使用者進程通過i2c-dev 提供的/dev/i2c/%d 裝置節點訪問i2c 裝置時,最終是通過調用master_xfer 或者smbus_xfer 指向的方法完成的。
slave_send/recv 函數用於實現當i2c 適配器扮演slave 角色時的傳輸方法。
由於內容統一的關係,在一篇文章裡又記錄資料結構,又貼出驅動代碼,這樣個人感覺比較亂。所以我還是選擇把I2C驅動這一塊內容分成三部分,本文討論資料結構為第一部分,在接下來的I2C驅動文章《S3C2440驅動簡析——I2C驅動(2)》裡,將會深入i2c-dev.c驅動代碼。第三部分是深入i2c-core.c,以瞭解實際底層操作的過程。
欲知後事如何,請聽下回分解~
本系列博文連結:
I2C驅動(1)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6449939.aspx
I2C驅動(2)http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6451168.aspx
I2C驅動(3)http://blog.csdn.net/jarvis_xian/archive/2011/05/28/6452431.aspx
I2C驅動(4)http://blog.csdn.net/jarvis_xian/archive/2011/05/30/6455697.aspx