歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 手把手教你寫Linux I2C設備驅動

手把手教你寫Linux I2C設備驅動

日期:2017/3/1 10:40:17   编辑:Linux編程
Linux I2C驅動是嵌入式Linux驅動開發人員經常需要編寫的一種驅動,因為凡是系統中使用到的I2C設備,幾乎都需要編寫相應的I2C驅動去配置和控制它,例如 RTC實時時鐘芯片、音視頻采集芯片、音視頻輸出芯片、EEROM芯片、AD/DA轉換芯片等等。

Linux I2C驅動涉及的知識點還是挺多的,主要分為Linux I2C的總線驅動(I2C BUS Driver)和設備驅動(I2C Clients Driver),本文主要關注如何快速地完成一個具體的I2C設備驅動(I2C Clients Driver)。關於Linux I2C驅動的整體架構、核心原理等可以在網上搜索其他相關文章學習。

本文主要參考了Linux內核源碼目錄下的 ./Documentation/i2c/writing-clients 文檔。以手頭的一款視頻采集芯片TVP5158為驅動目標,編寫Linux I2C設備驅動。

1. i2c_driver結構體對象

每一個I2C設備驅動,必須首先創造一個i2c_driver結構體對象,該結構體包含了I2C設備探測和注銷的一些基本方法和信息,示例如下:

  1. static struct i2c_driver tvp5158_i2c_driver = {
  2. .driver = {
  3. .name = "tvp5158_i2c_driver",
  4. },
  5. .attach_adapter = &tvp5158_attach_adapter,
  6. .detach_client = &tvp5158_detach_client,
  7. .command = NULL,
  8. };

其中,name字段標識本驅動的名稱(不要超過31個字符),attach_adapter和detach_client字段為函數指針,這兩個函數在I2C設備注冊的時候會自動調用,需要自己實現這兩個函數,後面將詳細講述。

2. i2c_client 結構體對象

上面定義的i2c_driver對象,抽象為一個i2c的驅動模型,提供對i2C設備的探測和注銷方法,而i2c_client結構體則是代表著一個具體的i2c設備,該結構體有一個data指針,可以指向任何私有的設備數據,在復雜點的驅動中可能會用到。示例如下:

  1. struct tvp5158_obj{
  2. struct i2c_client client;
  3. int users; // how many users using the driver
  4. };
  5. struct tvp5158_obj* g_tvp5158_obj;

其中,users為示例,用戶可以自己在tvp5158_obj這個結構體裡面添加感興趣的字段,但是i2c_client字段不可少。具體用法後面再詳細講。

3. 設備注冊及探測功能

這一步很關鍵,按照標准的要求來寫,則Linux系統會自動調用相關的代碼去探測你的I2C設備,並且添加到系統的I2C設備列表中以供後面訪問。

我們知道,每一個I2C設備芯片,都通過硬件連接設定好了該設備的I2C設備地址。因此,I2C設備的探測一般是靠設備地址來完成的。那麼,首先要在驅動代碼中聲明你要探測的I2C設備地址列表,以及一個宏。示例如下:

  1. static unsigned short normal_i2c[] = {
  2. 0xbc >> 1,
  3. 0xbe >> 1,
  4. I2C_CLIENT_END
  5. };
  6. I2C_CLIENT_INSMOD;

normal_i2c 數組包含了你需要探測的I2C設備地址列表,並且必須以I2C_CLIENT_END作為結尾,注意,上述代碼中的0xbc和0xbe是我在硬件上為我的tvp5158分配的地址,硬件上我支持通過跳線將該地址設置為 0xbc 或者 0xbe,所以把這兩個地址均寫入到探測列表中,讓系統進行探測。如果你的I2C設備的地址是固定的,那麼,這裡可以只寫你自己的I2C設備地址,注意必須向右移位1。

宏 I2C_CLIENT_INSMOD 的作用網上有許多文章進行了詳細的講解,這裡我就不詳細描述了,記得加上就行,我們重點關注實現。

下一步就應該編寫第1步中的兩個回調函數,一個用於注冊設備,一個用於注銷設備。探測函數示例如下:

  1. static int tvp5158_attach_adapter(struct i2c_adapter *adapter)
  2. {
  3. return i2c_probe(adapter, &addr_data, &tvp5158_detect_client);
  4. }

這個回調函數系統會自動調用,我們只需要按照上述代碼形式寫好就行,這裡調用了系統的I2C設備探測函數,i2c_probe(),第三個參數為具體的設備探測回調函數,系統會在探測設備的時候調用這個函數,需要自己實現。示例如下:

  1. static int tvp5158_detect_client(struct i2c_adapter *adapter,int address,int kind)
  2. {
  3. struct tvp5158_obj *pObj;
  4. int err = 0;
  5. printk(KERN_INFO "I2C: tvp5158_detect_client at address %x ...\n", address);
  6. if( g_tvp5158_obj != NULL ) {
  7. //already allocated,inc user count, and return the allocated handle
  8. g_tvp5158_obj->users++;
  9. return 0;
  10. }
  11. /* alloc obj */
  12. pObj = kmalloc(sizeof(struct tvp5158_obj), GFP_KERNEL);
  13. if (pObj==0){
  14. return -ENOMEM;
  15. }
  16. memset(pObj, 0, sizeof(struct tvp5158_obj));
  17. pObj->client.addr = address;
  18. pObj->client.adapter = adapter;
  19. pObj->client.driver = &tvp5158_i2c_driver;
  20. pObj->client.flags = I2C_CLIENT_ALLOW_USE;
  21. pObj->users++;
  22. /* attach i2c client to sys i2c clients list */
  23. if((err = i2c_attach_client(&pObj->client))){
  24. printk( KERN_ERR "I2C: ERROR: i2c_attach_client fail! address=%x\n",address);
  25. return err;
  26. }
  27. // store the pObj
  28. g_tvp5158_obj = pObj;
  29. printk( KERN_ERR "I2C: i2c_attach_client ok! address=%x\n",address);
  30. return 0;
  31. }

到此為止,探測並且注冊設備的代碼已經完成,以後對該 I2C 設備的訪問均可以通過 g_tvp5158_obj 這個全局的指針進行了。

Copyright © Linux教程網 All Rights Reserved