1. 程式人生 > >linux驅動--i2c驅動學習

linux驅動--i2c驅動學習

預備知識

在閱讀本文最好先熟悉一種i2c裝置的驅動程式,並且瀏覽一下i2c-core.c以及晶片提供商的提供的i2c匯流排驅動(i2c-davinci.c)。標題黨請見諒!

其實i2c介面非常的簡單,即使用51單片的gpio來模擬i2c,編寫一個e2prom或者其他i2c介面的驅動程式,也不是什麼難事,幾百行程式碼就能搞定。

但是Linux的i2c驅動體系結構卻有相當的複雜度,不管是叫linux i2c驅動還是微控制器i2c驅動,其根本還是操作soc晶片內部的i2c模組(也叫i2c adapter)(讀寫i2c相關的暫存器)來產生start、stop還有ack訊號而已。

linux裝置驅動到底複雜在什麼地方?

假設soc晶片dm368有兩個i2c adapter(368內部真正只有一個i2c模組):i2c_adapter1,i2c_adapter1;然後外部有三個i2c介面的裝置i2c_device1,i2c_device2,i2c_device3。

現在要求在裸機下寫出他們的驅動函式。那麼肯定要寫出6個不同的驅動函式:

[cpp] view plaincopyprint?
  1. i2c_adapter1_ReadWrite_i2c_device1();  
  2. i2c_adapter1_ReadWrite_i2c_device2()  
  3. i2c_adapter1_ReadWrite_i2c_device3()  
  4. i2c_adapter2_ReadWrite_i2c_device1()  
  5. i2c_adapter2_ReadWrite_i2c_device2()  
  6. i2c_adapter2_ReadWrite_i2c_device3()  
i2c_adapter1_ReadWrite_i2c_device1();
i2c_adapter1_ReadWrite_i2c_device2()
i2c_adapter1_ReadWrite_i2c_device3()
i2c_adapter2_ReadWrite_i2c_device1()
i2c_adapter2_ReadWrite_i2c_device2()
i2c_adapter2_ReadWrite_i2c_device3()
設想一共有m個i2c adapter和n個外設i2c device,那麼將需要m*n個驅動。並且這m*n個驅動程式必要會有很大部分重複的程式碼,而且不利於驅動程式的移植。

如果採用adapter和device分離的思想來寫這樣的驅動會是怎樣呢?


圖1

這樣分離之後,只需要m+n個驅動,而且Adapter和Device的幾乎沒有耦合性,增加一個Adapter或者device並不會影響其餘的驅動。

這就是分離思想帶來的好處。除此之外,linux雖然是C寫的,但是大量使用了面向物件的程式設計方法(可以理解為分層的思想),

僅僅分離細想和分層思想的引入,就大大增加了linux裝置驅動的複雜度。

linux驅動中 i2c驅動架構


圖2

上圖完整的描述了linux i2c驅動架構,雖然I2C硬體體系結構比較簡單,但是i2c體系結構在linux中的實現卻相當複雜。那麼我們如何編寫特定i2c介面器件(比如,ov2715,需要i2c來配置暫存器)的驅動程式?就是說上述架構中的那些部分需要我們完成,而哪些是linux核心已經完善的或者是晶片提供商(TI davinci平臺已經做好的)已經提供的?

架構層次分類

第一層:提供i2c adapter的硬體驅動,探測、初始化i2c adapter(如申請i2c的io地址和中斷號),驅動soc控制的i2c adapter在硬體上產生訊號(start、stop、ack)以及處理i2c中斷。覆蓋圖中的硬體實現層

第二層:提供i2c adapter的algorithm,用具體介面卡的xxx_xferf()函式來填充i2c_algorithm的master_xfer函式指標,並把賦值後的i2c_algorithm再賦值給i2c_adapter的algo指標。覆蓋圖中的訪問抽象層、i2c核心層

第三層:實現i2c裝置驅動中的i2c_driver介面,用具體的i2c device裝置的attach_adapter()、detach_adapter()方法賦值給i2c_driver的成員函式指標。實現裝置device與匯流排(或者叫adapter)的掛接。覆蓋圖中的driver驅動層

第四層:實現i2c裝置所對應的具體device的驅動,i2c_driver只是實現裝置與匯流排的掛接,而掛接在總線上的裝置則是千差萬別的,eeprom和ov2715顯然不是同一類的device,所以要實現具體裝置device的write()、read()、ioctl()等方法,賦值給file_operations,然後註冊字元裝置(多數是字元裝置)。覆蓋圖中的driver驅動層

第一層和第二層又叫i2c匯流排驅動(bus),第三第四屬於i2c裝置驅動(device driver)。在linux驅動架構中,幾乎不需要驅動開發人員再新增bus,因為linux核心幾乎整合所有匯流排bus,如usb、pci、i2c等等。並且匯流排bus中的【與特定硬體相關的程式碼】已由晶片提供商編寫完成,例如TI davinci平臺i2c匯流排bus與硬體相關的程式碼在核心目錄/drivers/i2c/buses下的i2c-davinci.c原始檔中;而三星的s3c-2440平臺i2c匯流排bus為/drivers/i2c/buses/i2c-s3c2410.c

第三第四層又叫裝置驅動層與特定device相干的就需要驅動工程師來實現了。

明確了方向後,再來具體分析。

具體分析

i2c_adapter與i2c_client的關係與i2c硬體體系中設配器與裝置的關係一致,即i2c_client依附於i2c_adapter,由於一個介面卡上可以連線多個i2c裝置device,所以相應的,i2c_adapter也可以被多個i2c_client依附,在i2c_adapter中包含i2c_client的連結串列。同一類的i2c裝置device對應一個驅動driver。driver與device的關係是一對多的關係。

現在,我們就來看一下這幾個重要的結構體,分別是i2c_driver i2c_client i2c_adapter,也可以先忽略他們,待會回過頭來看會更容易理解

1、i2c_driver

[cpp] view plaincopyprint?
  1. struct i2c_driver {  
  2. int id;  
  3.     unsigned intclass;  
  4. int (*attach_adapter)(struct i2c_adapter *);  
  5. int (*detach_adapter)(struct i2c_adapter *);  
  6. int (*detach_client)(struct i2c_client *);  
  7. int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);  
  8. struct device_driver driver;  
  9. struct list_head list;  
  10. };  
struct i2c_driver {
	int id;
	unsigned int class;

	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	int (*detach_client)(struct i2c_client *);

	int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
	struct device_driver driver;
	struct list_head list;
};

2、i2c_client

[cpp] view plaincopyprint?
  1. struct i2c_client {  
  2.     unsigned int flags;     /* div., see below      */
  3.     unsigned short addr;        /* chip address - NOTE: 7bit    */
  4. /* addresses are stored in the  */
  5. /* _LOWER_ 7 bits       */
  6. struct i2c_adapter *adapter;    /* the adapter we sit on    */
  7. struct i2c_driver *driver;  /* and our access routines  */
  8. int usage_count;        /* How many accesses currently  */
  9. /* to the client        */
  10. struct device dev;      /* the device structure     */
  11. struct list_head list;  
  12. char name[I2C_NAME_SIZE];  
  13. struct completion released;  
  14. };  
struct i2c_client {
	unsigned int flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit 	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	int usage_count;		/* How many accesses currently  */
					/* to the client		*/
	struct device dev;		/* the device structure		*/
	struct list_head list;
	char name[I2C_NAME_SIZE];
	struct completion released;
};

3、i2c_adapter

[cpp] view plaincopyprint?
  1. struct i2c_adapter {  
  2. struct module *owner;  
  3.     unsigned int id;  
  4.     unsigned intclass;  
  5. struct i2c_algorithm *algo;/* the algorithm to access the bus   */
  6. void *algo_data;  
  7. /* --- administration stuff. */
  8. int (*client_register)(struct i2c_client *);  
  9. int (*client_unregister)(struct i2c_client *);  
  10. /* data fields that are valid for all devices   */
  11. struct mutex bus_lock;  
  12. struct mutex clist_lock;  
  13. int timeout;  
  14. int retries;  
  15. struct device dev;      /* the adapter device */
  16. struct class_device class_dev;  /* the class device */
  17. int nr;  
  18. struct list_head clients;  
  19. struct list_head list;  
  20. char name[I2C_NAME_SIZE];  
  21. struct completion dev_released;  
  22. struct completion class_dev_released;  
  23. };  
struct i2c_adapter {
	struct module *owner;
	unsigned int id;
	unsigned int class;
	struct i2c_algorithm *algo;/* the algorithm to access the bus	*/
	void *algo_data;

	/* --- administration stuff. */
	int (*client_register)(struct i2c_client *);
	int (*client_unregister)(struct i2c_client *);

	/* data fields that are valid for all devices	*/
	struct mutex bus_lock;
	struct mutex clist_lock;

	int timeout;
	int retries;
	struct device dev;		/* the adapter device */
	struct class_device class_dev;	/* the class device */

	int nr;
	struct list_head clients;
	struct list_head list;
	char name[I2C_NAME_SIZE];
	struct completion dev_released;
	struct completion class_dev_released;
};

4、i2c_algorithm

[cpp] view plaincopyprint?
  1. struct i2c_algorithm {  
  2. int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,   
  3. int num);  
  4. int (*slave_send)(struct i2c_adapter *,char*,int);  
  5. int (*slave_recv)(struct i2c_adapter *,char*,int);  
  6.     u32 (*functionality) (struct i2c_adapter *);  
  7. };  
struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, 
	                   int num);
	int (*slave_send)(struct i2c_adapter *,char*,int);
	int (*slave_recv)(struct i2c_adapter *,char*,int);
	u32 (*functionality) (struct i2c_adapter *);
};
【i2c_adapter與i2c_algorithm】

i2c_adapter對應與物理上的一個介面卡,而i2c_algorithm對應一套通訊方法,一個i2c介面卡需要i2c_algorithm中提供的(i2c_algorithm中的又是更下層與硬體相關的程式碼提供)通訊函式來控制介面卡上產生特定的訪問週期。缺少i2c_algorithm的i2c_adapter什麼也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指標。

i2c_algorithm中的關鍵函式master_xfer()用於產生i2c訪問週期需要的start stop ack訊號,以i2c_msg(即i2c訊息)為單位傳送和接收通訊資料。i2c_msg也非常關鍵,呼叫驅動中的傳送接收函式需要填充該結構體

[cpp] view plaincopyprint?
  1. /* 
  2.  * I2C Message - used for pure i2c transaction, also from /dev interface 
  3.  */
  4. struct i2c_msg {  
  5.     __u16 addr; /* slave address            */
  6.     __u16 flags;          
  7.     __u16 len;      /* msg length               */
  8.     __u8 *buf;      /* pointer to msg data          */
  9. };  
/*
 * I2C Message - used for pure i2c transaction, also from /dev interface
 */
struct i2c_msg {
	__u16 addr;	/* slave address			*/
 	__u16 flags;		
 	__u16 len;		/* msg length				*/
 	__u8 *buf;		/* pointer to msg data			*/
};
【i2c_driver和i2c_client】

i2c_driver對應一套驅動方法,其主要函式是attach_adapter()和detach_client(),i2c_client對應真實的i2c物理裝置device,每個i2c裝置都需要一個i2c_client來描述,i2c_driver與i2c_client的關係是一對多。一個i2c_driver上可以支援多個同等型別的i2c_client.

【i2c_adapter和i2c_client】

i2c_adapter和i2c_client的關係與i2c硬體體系中介面卡和裝置的關係一致,即i2c_client依附於i2c_adapter,由於一個介面卡上可以連線多個i2c裝置,所以i2c_adapter中包含依附於它的i2c_client的連結串列。

從圖1圖2中都可以看出,linux核心對i2c架構抽象了一個叫核心層core的中介軟體,它分離了裝置驅動device driver和硬體控制的實現細節(如操作i2c的暫存器),core層不但為上面的裝置驅動提供封裝後的核心註冊函式,而且還為小面的硬體時間提供註冊介面(也就是i2c匯流排註冊介面),可以說core層起到了承上啟下的作用。

我們先看一下i2c-core為外部提供的核心函式(選取部分),i2c-core對應的原始檔為i2c-core.c,位於核心目錄/driver/i2c/i2c-core.c

[cpp] view plaincopyprint?
  1. EXPORT_SYMBOL(i2c_add_adapter);  
  2. EXPORT_SYMBOL(i2c_del_adapter);  
  3. EXPORT_SYMBOL(i2c_del_driver);  
  4. EXPORT_SYMBOL(i2c_attach_client);  
  5. EXPORT_SYMBOL(i2c_detach_client);  
  6. EXPORT_SYMBOL(i2c_transfer);  
EXPORT_SYMBOL(i2c_add_adapter);
EXPORT_SYMBOL(i2c_del_adapter);
EXPORT_SYMBOL(i2c_del_driver);
EXPORT_SYMBOL(i2c_attach_client);
EXPORT_SYMBOL(i2c_detach_client);

EXPORT_SYMBOL(i2c_transfer);
如果看過i2c裝置驅動程式的人一定對上面幾個函式比較熟悉。

i2c_transfer()函式,i2c_transfer()函式本身並不具備驅動介面卡物理硬體完成訊息互動的能力,它只是尋找到i2c_adapter對應的i2c_algorithm,並使用i2c_algorithm的master_xfer()函式真正的驅動硬體流程,程式碼清單如下,不重要的已刪除。

[cpp] view plaincopyprint?
  1. int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)  
  2. {  
  3. int ret;  
  4. if (adap->algo->master_xfer) {//如果master_xfer函式存在,則呼叫,否則返回錯誤
  5.         ret = adap->algo->master_xfer(adap,msgs,num);//這個函式在硬體相關的程式碼中給algorithm賦值
  6. return ret;  
  7.     } else {  
  8. return -ENOSYS;  
  9.     }  
  10. }  
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
	int ret;
	if (adap->algo->master_xfer) {//如果master_xfer函式存在,則呼叫,否則返回錯誤
		ret = adap->algo->master_xfer(adap,msgs,num);//這個函式在硬體相關的程式碼中給algorithm賦值
		return ret;
	} else {
		return -ENOSYS;
	}
}
當一個具體的client被偵測到並被關聯的時候,裝置和sysfs檔案將被註冊。相反的,在client被取消關聯的時候,sysfs檔案和裝置也被登出,驅動開發人員需開發i2c裝置驅動時,需要呼叫下列函式。程式清單如下
[cpp] view plaincopyprint?
  1. int i2c_attach_client(struct i2c_client *client)  
  2. {  
  3.     ...  
  4.     device_register(&client->dev);  
  5.     device_create_file(&client->dev, &dev_attr_client_name);  
  6.     ...  
  7. return 0;  
  8. }  
int i2c_attach_client(struct i2c_client *client)
{
	...
	device_register(&client->dev);
	device_create_file(&client->dev, &dev_attr_client_name);
	...
	return 0;
}
[cpp] view plaincopyprint?
  1. int i2c_detach_client(struct i2c_client *client)  
  2. {  
  3.     ...  
  4.     device_remove_file(&client->dev, &dev_attr_client_name);  
  5.     device_unregister(&client->dev);  
  6.     ...  
  7. return res;  
  8. }  
int i2c_detach_client(struct i2c_client *client)
{
	...
	device_remove_file(&client->dev, &dev_attr_client_name);
	device_unregister(&client->dev);
	...
	return res;
}
i2c_add_adapter()函式和i2c_del_adapter()在i2c-davinci.c中有呼叫,稍後分析
[cpp] view plaincopyprint?
  1. /* ----- 
  2.  * i2c_add_adapter is called from within the algorithm layer, 
  3.  * when a new hw adapter registers. A new device is register to be 
  4.  * available for clients. 
  5.  */
  6. int i2c_add_adapter(struct i2c_adapter *adap)  
  7. {  
  8.     ...  
  9.     device_register(&adap->dev);  
  10.     device_create_file(&adap->dev, &dev_attr_name);  
  11.     ...  
  12. /* inform drivers of new adapters */
  13.     list_for_each(item,&drivers) {  
  14.         driver = list_entry(item, struct i2c_driver, list);  
  15. if (driver->attach_adapter)  
  16. /* We ignore the return code; if it fails, too bad */
  17.             driver->attach_adapter(adap);  
  18.     }  
  19.     ...  
  20. }  
/* -----
 * i2c_add_adapter is called from within the algorithm layer,
 * when a new hw adapter registers. A new device is register to be
 * available for clients.
 */
int i2c_add_adapter(struct i2c_adapter *adap)
{
	...
	device_register(&adap->dev);
	device_create_file(&adap->dev, &dev_attr_name);
	...
	/* inform drivers of new adapters */
	list_for_each(item,&drivers) {
		driver = list_entry(item, struct i2c_driver, list);
		if (driver->attach_adapter)
			/* We ignore the return code; if it fails, too bad */
			driver->attach_adapter(adap);
	}
	...
}
[cpp] view plaincopyprint?
  1. int i2c_del_adapter(struct i2c_adapter *adap)  
  2. {  
  3.     ...  
  4.     list_for_each(item,&drivers) {  
  5.         driver = list_entry(item, struct i2c_driver, list);  
  6. if (driver->detach_adapter)  
  7. if ((res = driver->detach_adapter(adap))) {  
  8.             }  
  9.     }  
  10.     ...  
  11.     list_for_each_safe(item, _n, &adap->clients) {  
  12.         client = list_entry(item, struct i2c_client, list);  
  13. if ((res=client->driver->detach_client(client))) {  
  14.         }  
  15.     }  
  16.     ...  
  17.     device_remove_file(&adap->dev, &dev_attr_name);  
  18.     device_unregister(&adap->dev);  
  19. }  
int i2c_del_adapter(struct i2c_adapter *adap)
{
	...
	list_for_each(item,&drivers) {
		driver = list_entry(item, struct i2c_driver, list);
		if (driver->detach_adapter)
			if ((res = driver->detach_adapter(adap))) {
			}
	}
	...
	list_for_each_safe(item, _n, &adap->clients) {
		client = list_entry(item, struct i2c_client, list);

		if ((res=client->driver->detach_client(client))) {

		}
	}
	...
	device_remove_file(&adap->dev, &dev_attr_name);
	device_unregister(&adap->dev);

}
i2c-davinci.c是實現與硬體相關功能的程式碼集合,這部分是與平臺相關的,也叫做i2c匯流排驅動,這部分程式碼是這樣新增到系統中的
[cpp] view plaincopyprint?
  1. staticstruct platform_driver davinci_i2c_driver = {  
  2.     .probe      = davinci_i2c_probe,  
  3.     .remove     = davinci_i2c_remove,  
  4.     .driver     = {  
  5.         .name   = "i2c_davinci",  
  6.         .owner  = THIS_MODULE,  
  7.     },  
  8. };  
  9. /* I2C may be needed to bring up other drivers */
  10. staticint __init davinci_i2c_init_driver(void)  
  11. {  
  12. return platform_driver_register(&davinci_i2c_driver);  
  13. }  
  14. subsys_initcall(davinci_i2c_init_driver);  
  15. staticvoid __exit davinci_i2c_exit_driver(void)  
  16. {  
  17.     platform_driver_unregister(&davinci_i2c_driver);  
  18. }  
  19. module_exit(davinci_i2c_exit_driver);  
static struct platform_driver davinci_i2c_driver = {
	.probe		= davinci_i2c_probe,
	.remove		= davinci_i2c_remove,
	.driver		= {
		.name	= "i2c_davinci",
		.owner	= THIS_MODULE,
	},
};

/* I2C may be needed to bring up other drivers */
static int __init davinci_i2c_init_driver(void)
{
	return platform_driver_register(&davinci_i2c_driver);
}
subsys_initcall(davinci_i2c_init_driver);

static void __exit davinci_i2c_exit_driver(void)
{
	platform_driver_unregister(&davinci_i2c_driver);
}
module_exit(davinci_i2c_exit_driver);
並且,i2c介面卡控制硬體傳送接收資料的函式在這裡賦值給i2c-algorithm,i2c_davinci_xfer稍加修改就可以在裸機中控制i2c介面卡
[cpp] view plaincopyprint?
  1. staticstruct i2c_algorithm i2c_davinci_algo = {  
  2.     .master_xfer    = i2c_davinci_xfer,  
  3.     .functionality  = i2c_davinci_func,  
  4. };  
static struct i2c_algorithm i2c_davinci_algo = {
	.master_xfer	= i2c_davinci_xfer,
	.functionality	= i2c_davinci_func,
};
然後在davinci_i2c_probe函式中,將i2c_davinci_algo新增到新增到algorithm系統中 [cpp] view plaincopyprint?
  1. adap->algo = &i2c_davinci_algo;  
adap->algo = &i2c_davinci_algo;

梳理圖

有時候程式碼比任何文字描述都來得直接,但是過多的程式碼展示反而讓人覺得枯燥。這個時候,需要一幅圖來梳理一下上面的內容,請看圖3。


圖3

好了,上面這些程式碼的展示是告訴我們,linux核心和晶片提供商為我們的的驅動程式提供了 i2c驅動的框架,以及框架底層與硬體相關的程式碼的實現。剩下的就是針對掛載在i2c兩線上的i2c裝置了device,如at24c02,例如ov2715,而編寫的具體裝置驅動了,這裡的裝置就是硬體介面外掛載的裝置,而非硬體介面本身(soc硬體介面本身的驅動可以理解為匯流排驅動)。

在理解了i2c驅動架構後,我們接下來再作兩方面的分析工作:一是具體的i2c裝置ov2715驅動原始碼分析,二是davinci平臺的i2c匯流排驅動原始碼。

ov2715裝置i2c驅動原始碼分析

ov2715為200萬的CMOS Sensor,晶片的暫存器控制通過i2c介面完成,i2c裝置地址為0x6c,暫存器地址為16位兩個位元組,暫存器值為8位一個位元組,可以理解為一般的字元裝置。
該驅動程式並非只能用於ov2715,因此原始碼中存在支援多個裝置地址的機制。
該字元裝置的用到的結構體有兩個,如下

[cpp] view plaincopyprint?
  1. typedefstruct {  
  2. int devAddr;  
  3. struct i2c_client client;   //!< Data structure containing general access routines.
  4. struct i2c_driver driver;   //!< Data structure containing information specific to each client.
  5. char name[20];  
  6. int nameSize;  
  7. int users;  
  8. } I2C_Obj;  
typedef struct {

  int devAddr;

  struct i2c_client client;   //!< Data structure containing general access routines.
  struct i2c_driver driver;   //!< Data structure containing information specific to each client.
  
  char name[20];
  int nameSize;
  int users;
  
} I2C_Obj;
[cpp] view plaincopyprint?
  1. #define I2C_DEV_MAX_ADDR  (0xFF)
  2. #define I2C_TRANSFER_BUF_SIZE_MAX   (256)
  3. typedefstruct {  
  4. struct cdev cdev;             /* Char device structure    */
  5. int     major;  
  6. struct semaphore semLock;  
  7.   I2C_Obj *pObj[I2C_DEV_MAX_ADDR];  
  8.   uint8_t reg[I2C_TRANSFER_BUF_SIZE_MAX];  
  9.   uint16_t reg16[I2C_TRANSFER_BUF_SIZE_MAX];  
  10.   uint8_t buffer[I2C_TRANSFER_BUF_SIZE_MAX*4];  
  11. } I2C_Dev;  
#define I2C_DEV_MAX_ADDR  (0xFF)
#define I2C_TRANSFER_BUF_SIZE_MAX   (256)
typedef struct {

  struct cdev cdev;             /* Char device structure    */
  int     major;
  struct semaphore semLock;
    
  I2C_Obj *pObj[I2C_DEV_MAX_ADDR];

  uint8_t reg[I2C_TRANSFER_BUF_SIZE_MAX];
  uint16_t reg16[I2C_TRANSFER_BUF_SIZE_MAX];
  uint8_t buffer[I2C_TRANSFER_BUF_SIZE_MAX*4];
  
} I2C_Dev;

一個I2C_Obj描述一個裝置,devAddr儲存該裝置的地址,I2C_Obj內嵌到結構體I2C_Dev,I2C_Dev管理該驅動所支援的所有裝置,儘管支援多個裝置,但i2c介面卡只有一個,因此需要一個訊號量semLock來保護該共享資源,同時只能向一個裝置讀寫資料。成員變數cdev是我們所熟知的,每個字元裝置驅動中幾乎總會有一個結構體包含它,major用於儲存該驅動的主裝置編號,reg陣列為暫存器地址為8位的暫存器地址緩衝區,reg16為暫存器地址為16的暫存器地址緩衝區。同時可以讀寫多個暫存器地址的值。buffer為讀寫的暫存器值 使用I2C_Dev構建一個全域性變數gI2C_dev,在驅動的多個地方均需要它。
下面先從字元裝置的基本框架入手,然後深入該驅動的細節部分。
首先是該字元裝置的初始化和退出函式 [cpp] view plaincopyprint?
  1. int I2C_devInit(void)  
  2. {  
  3. int     result, i;  
  4.   dev_t   dev = 0;  
  5.   result = alloc_chrdev_region(&dev, 0, 1, I2C_DRV_NAME);//分配字元裝置空間
  6. for(i=0; i<I2C_DEV_MAX_ADDR; i++)  
  7.   {  
  8.     gI2C_dev.pObj[i]=NULL;  
  9.   }  
  10.   gI2C_dev.major = MAJOR(dev);//儲存裝置主編號
  11.   sema_init(&gI2C_dev.semLock, 1);//訊號量初始化
  12.   cdev_init(&gI2C_dev.cdev, &gI2C_devFileOps);//使用gI2C_devFileOps初始化該字元裝置,gI2C_devFileOps見下文
  13.   gI2C_dev.cdev.owner = THIS_MODULE;//常規賦值
  14.  gI2C_dev.cdev.ops = &gI2C_devFileOps;//常規賦值 result = cdev_add(&gI2C_dev.cdev, dev, 1);//新增裝置到字元裝置中 return result;}void I2C_devExit(void){ dev_t devno = MKDEV(gI2C_dev.major, 0); cdev_del(&gI2C_dev.cdev);//從字元裝置中刪除該裝置 unregister_chrdev_region(devno, 1);//回收空間}
  15. gI2c_devFileOps全域性變數,驅動初始化會用到該結構體變數  
  16. struct file_operations gI2C_devFileOps = {  
  17.   .owner = THIS_MODULE,  
  18.   .open = I2C_devOpen,  
  19.   .release = I2C_devRelease,  
  20.   .ioctl = I2C_devIoctl,  
  21. };  
int I2C_devInit(void)
{
  int     result, i;
  dev_t   dev = 0;

  result = alloc_chrdev_region(&dev, 0, 1, I2C_DRV_NAME);//分配字元裝置空間
  
  for(i=0; i<I2C_DEV_MAX_ADDR; i++)
  {
    gI2C_dev.pObj[i]=NULL;
  }

  gI2C_dev.major = MAJOR(dev);//儲存裝置主編號
  sema_init(&gI2C_dev.semLock, 1);//訊號量初始化
  cdev_init(&gI2C_dev.cdev, &gI2C_devFileOps);//使用gI2C_devFileOps初始化該字元裝置,gI2C_devFileOps見下文
  gI2C_dev.cdev.owner = THIS_MODULE;//常規賦值
 gI2C_dev.cdev.ops = &gI2C_devFileOps;//常規賦值 result = cdev_add(&gI2C_dev.cdev, dev, 1);//新增裝置到字元裝置中 return result;}void I2C_devExit(void){ dev_t devno = MKDEV(gI2C_dev.major, 0); cdev_del(&gI2C_dev.cdev);//從字元裝置中刪除該裝置 unregister_chrdev_region(devno, 1);//回收空間}
gI2c_devFileOps全域性變數,驅動初始化會用到該結構體變數
struct file_operations gI2C_devFileOps = {
  .owner = THIS_MODULE,
  .open = I2C_devOpen,
  .release = I2C_devRelease,
  .ioctl = I2C_devIoctl,
};
該驅動只實現了三個函式,open,release和ioctl,對於i2c裝置來說,這已經足夠了。
在I2C_devOpen和I2C_devOpen中並沒有做實際的工作,重要的工作均在I2C_devIoctl這個ioctl中完成。I2C_devIoctl程式碼展示(將影響結構條理的程式碼去掉,稍後在做詳細分析) [cpp] view plaincopyprint?
  1. int I2C_devIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
  2. {  
  3.   I2C_Obj *pObj;  
  4. int status=0;  
  5.   I2C_TransferPrm transferPrm;  
  6.   pObj = (I2C_Obj *)filp->private_data;  
  7. if(!I2C_IOCTL_CMD_IS_VALID(cmd))  
  8. return -1;  
  9.   cmd = I2C_IOCTL_CMD_GET(cmd);//cmd命令轉換,防止混淆,具體原因參見上一篇文章:ioctl中的cmd
  10.   down_interruptible(&gI2C_dev.semLock);      //訊號量down
  11. switch(cmd)  
  12.   {  
  13. case I2C_CMD_SET_DEV_ADDR://命令1,設定裝置地址
  14.       filp->private_data = I2C_create(arg);  
  15. case I2C_CMD_WRITE:  //命令2,寫暫存器值
  16.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm));  
  17.       ...  
  18. break;  
  19. case I2C_CMD_READ:  //命令3,讀暫存器值
  20.       status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm));  
  21.       ...  
  22. break;  
  23. default:  
  24.       status = -1;  
  25. break;      
  26.   }  
  27.   up(&gI2C_dev.semLock);      //訊號量up
  28. return status;  
  29. }  
int I2C_devIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
  I2C_Obj *pObj;
  int status=0;
  I2C_TransferPrm transferPrm;
  
  pObj = (I2C_Obj *)filp->private_data;

  if(!I2C_IOCTL_CMD_IS_VALID(cmd))
    return -1;
  cmd = I2C_IOCTL_CMD_GET(cmd);//cmd命令轉換,防止混淆,具體原因參見上一篇文章:ioctl中的cmd

  down_interruptible(&gI2C_dev.semLock);      //訊號量down
  
  switch(cmd)
  {
    case I2C_CMD_SET_DEV_ADDR://命令1,設定裝置地址
      filp->private_data = I2C_create(arg);

    case I2C_CMD_WRITE:  //命令2,寫暫存器值
      
      status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm));
      ...
            
      break;
    case I2C_CMD_READ:  //命令3,讀暫存器值
    
      status = copy_from_user(&transferPrm, (void *)arg, sizeof(transferPrm));
      ...
      
      break;
    default:
      status = -1;
      break;    
  }

  up(&gI2C_dev.semLock);      //訊號量up

  return status;
}
以上三個命令中最重要最複雜的是第一個I2C_CMD_SET_DEV_ADDR,設定裝置地址,之所以重要和複雜,因為在I2C_create()函式中,將通過i2c-core提供的函式把該驅動程式和底層的i2c_adapter聯絡起來。下面是I2C_create()函式原始碼 [cpp] view plaincopyprint?
  1. void *I2C_create(int devAddr) {  
  2. int ret;  
  3. struct i2c_driver *driver;  
  4. struct i2c_client *client = client;  
  5.     I2C_Obj *pObj;  
  6.     devAddr >>= 1;  
  7. if(devAddr>I2C_DEV_MAX_ADDR)  //變數合法性判斷
  8. return NULL;  
  9. if(gI2C_dev.pObj[devAddr]!=NULL) {  //變數合法性判斷,如果該地址的裝置已經建立,則調過,防止上層錯誤呼叫
  10. // already allocated, increment user count, and return the allocated handle
  11.       gI2C_dev.pObj[devAddr]->users++;  
  12. return gI2C_dev.pObj[devAddr];  
  13.     }  
  14.     pObj = (void*)kmalloc( sizeof(I2C_Obj), GFP_KERNEL); //為pObj分配空間
  15.     gI2C_dev.pObj[devAddr] = pObj;  //將分配的空間地址儲存在全域性變數裡
  16.     memset(pObj, 0, sizeof(I2C_Obj));  
  17.     pObj->client.adapter = NULL;  
  18.     pObj->users++;    //使用者基數,初始化為0,當前設為1
  19.     pObj->devAddr = devAddr;  //儲存裝置地址
  20.     gI2C_curAddr = pObj->devAddr;  //gI2C_curAddr為全域性的整型變數,用於儲存當前的裝置地址
  21.     driver = &pObj->driver;  //將成員變數driver單獨抽取出來,因為線面要使用driver來初始化驅動
  22.     pObj->nameSize=0;//i2c裝置名稱,注意,這裡不是在/dev下面的裝置節點名
  23.     pObj->name[pObj->nameSize++] = 'I';  
  24.     pObj->name[pObj->nameSize++] = '2';  
  25.     pObj->name[pObj->nameSize++] = 'C';  
  26.     pObj->name[pObj->nameSize++] = '_';     
  27.     pObj->name[pObj->nameSize++] = 'A' + ((pObj->devAddr >> 0) & 0xF);  
  28.     pObj->name[pObj->nameSize++] = 'B' + ((pObj->devAddr >> 4) & 0xF);  
  29.     pObj->name[pObj->nameSize++] = 0;  
  30.     driver->driver.name = pObj->name; //儲存剛才設定的name
  31.     driver->id = I2C_DRIVERID_MISC;  
  32.     driver->attach_adapter = I2C_attachAdapter;   //這個很重要,將驅動連線到i2c介面卡上,在後面分析
  33.     driver->detach_client = I2C_detachClient;    //這個很重,在後面分析
  34. if((ret = i2c_add_driver(driver)))  //使用i2c-core(i2c_register_driver函式)的介面,註冊該驅動,i2c_add_driver實質呼叫了driver_register()
  35.     {  
  36.         printk( KERN_ERR "I2C: ERROR: Driver registration failed (address=%x), module not inserted.\n", pObj->devAddr);  
  37.     }  
  38. if(ret<0) {  
  39.       gI2C_dev.pObj[pObj->devAddr] = NULL;  
  40.       kfree(pObj);      
  41. return NULL;  
  42.     }  
  43. return pObj;  
  44. }  
void *I2C_create(int devAddr) {

    int ret;
    struct i2c_driver *driver;
    struct i2c_client *client = client;
    I2C_Obj *pObj;

    devAddr >>= 1;
    
    if(devAddr>I2C_DEV_MAX_ADDR)  //變數合法性判斷
      return NULL;
   
    if(gI2C_dev.pObj[devAddr]!=NULL) {	//變數合法性判斷,如果該地址的裝置已經建立,則調過,防止上層錯誤呼叫
      // already allocated, increment user count, and return the allocated handle
      gI2C_dev.pObj[devAddr]->users++;
      return gI2C_dev.pObj[devAddr];
    }
    
    pObj = (void*)kmalloc( sizeof(I2C_Obj), GFP_KERNEL); //為pObj分配空間
    gI2C_dev.pObj[devAddr] = pObj;  //將分配的空間地址儲存在全域性變數裡
    memset(pObj, 0, sizeof(I2C_Obj));
  
    pObj->client.adapter = NULL;
    pObj->users++;    //使用者基數,初始化為0,當前設為1
    pObj->devAddr = devAddr;  //儲存裝置地址
    
    gI2C_curAddr = pObj->devAddr;  //gI2C_curAddr為全域性的整型變數,用於儲存當前的裝置地址
    driver = &pObj->driver;  //將成員變數driver單獨抽取出來,因為線面要使用driver來初始化驅動

    pObj->nameSize=0;//i2c裝置名稱,注意,這裡不是在/dev下面的裝置節點名
    pObj->name[pObj->nameSize++] = 'I';
    pObj->name[pObj->nameSize++] = '2';
    pObj->name[pObj->nameSize++] = 'C';
    pObj->name[pObj->nameSize++] = '_';   
    pObj->name[pObj->nameSize++] = 'A' + ((pObj->devAddr >> 0) & 0xF);
    pObj->name[pObj->nameSize++] = 'B' + ((pObj->devAddr >> 4) & 0xF);
    pObj->name[pObj->nameSize++] = 0;

    driver->driver.name = pObj->name; //儲存剛才設定的name
    driver->id = I2C_DRIVERID_MISC;
    driver->attach_adapter = I2C_attachAdapter;   //這個很重要,將驅動連線到i2c介面卡上,在後面分析
    driver->detach_client = I2C_detachClient;	//這個很重,在後面分析

    if((ret = i2c_add_driver(driver)))	//使用i2c-core(i2c_register_driver函式)的介面,註冊該驅動,i2c_add_driver實質呼叫了driver_register()
    {
        printk( KERN_ERR "I2C: ERROR: Driver registration failed (address=%x), module not inserted.\n", pObj->devAddr);
    }

    if(ret<0) {

      gI2C_dev.pObj[pObj->devAddr] = NULL;
      kfree(pObj);    
      return NULL;
    }
    return pObj;
}

其他兩個命令是I2C_CMD_WRITE和I2C_CMD_READ,這個比較簡單,只需