Linux驅動子系統之I2C(3)

來源:互聯網
上載者:User
3  i2c-dev

3.1 概述

之前在介紹I2C子系統時,提到過使用i2c-dev.c檔案在應用程式中實現我們的I2C從裝置驅動。不過,它實現的是一個虛擬,臨時的i2c_client,隨著裝置檔案的開啟而產生,並隨著裝置檔案的關閉而撤銷。I2c-dev.c針對每個I2C適配器產生一個主裝置號為89的裝置檔案,實現了i2c_driver的成員函數以及檔案操作介面,所以i2c-dev.c的主題是”i2c_driver成員函數+字元裝置驅動”。

 

3.2 i2c-dev.c源碼分析

初始化模組

static int __init i2c_dev_init(void){         res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);          i2c_dev_class= class_create(THIS_MODULE, "i2c-dev");          /*Keep track of adapters which will be added or removed later */         res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);          /*綁定已經存在的適配器 */         i2c_for_each_dev(NULL,i2cdev_attach_adapter);}

I2c-dev初始化函數主要做了註冊名為”i2c”的字元裝置檔案和”i2c-dev”的類

 

i2cdev_read和i2cdev_write

I2c-dev.c中實現的i2cdev_read和i2cdev_write函數不具有太強的通用性,只適合下面這種單開始訊號情況:

而不適合多開始訊號的情況:

所以我們經常會使用i2cdev_ioctl函數的I2C_RDWR,在分析i2cdev_ioctl函數之前,我們需要瞭解一個結構體:

/* This is the structure as used in theI2C_RDWR ioctl call */struct i2c_rdwr_ioctl_data {         structi2c_msg __user *msgs;         /* pointersto i2c_msgs */         __u32nmsgs;                    /* number ofi2c_msgs */};

Msgs     表示單個開始訊號傳遞的資料;

Nmsgs     表示有多少個msgs,比如,單開始訊號時,nmsgs等於1;多開始訊號時,nmsgs等於2

 

struct i2c_msg {         __u16addr;     /* slave address                         */         __u16flags;  /* 預設為寫入 */#define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */#define I2C_M_RD           0x0001     /* read data,from slave to master */#define I2C_M_NOSTART                  0x4000     /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR     0x2000     /*if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK          0x1000     /*if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NO_RD_ACK           0x0800     /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_RECV_LEN               0x0400     /* length will be first received byte */         __u16len;                  /* msg length                              */         __u8*buf;                 /* pointer to msgdata                       */};

 

使用i2cdev_ioctl函數的I2C_RDWR指令會調用到i2cdev_ioctl_rdrw函數:

static noinline inti2cdev_ioctl_rdrw(struct i2c_client *client,                   unsignedlong arg){         structi2c_rdwr_ioctl_data rdwr_arg;         structi2c_msg *rdwr_pa;         u8__user **data_ptrs;         inti, res;          if(copy_from_user(&rdwr_arg,                               (struct i2c_rdwr_ioctl_data __user *)arg,                               sizeof(rdwr_arg)))                   return-EFAULT;          if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)                   return-EINVAL;          rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);          if(copy_from_user(rdwr_pa, rdwr_arg.msgs,                               rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {                   kfree(rdwr_pa);                   return-EFAULT;         }          res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);         while(i-- > 0) {                   if(res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {                            if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf,                                                rdwr_pa[i].len))                                     res= -EFAULT;                   }                   kfree(rdwr_pa[i].buf);         }}

咋一看,還挺複雜,其實主要做了一件事情:把使用者空間傳遞過來的i2c_rdwr_ioctl_data資料進行錯誤檢查,然後調用i2c_transfer函數與適配器進行通訊,如果是接收資料,代碼會將訪問到的資料傳回i2c_rdwr_ioctl_data的buf中。I2c_transfer最終會調用到I2C適配器具體實現的master_xfer函數來與硬體進行通訊。

 

3.3 eeprom執行個體

預備知識

使用的mini2440開發板,eeprom的地址為0x50,實驗完成一個資料的讀寫,先看下讀寫時序

AT24C08任意地址位元組寫的時序:

AT24C08任意地址位元組寫的時序:

下面的代碼可以按照上面的兩個圖來閱讀:

#include <stdio.h>#include <linux/types.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <sys/ioctl.h>#include <errno.h>#include <assert.h>#include <string.h>#include <linux/i2c.h>#include <linux/i2c-dev.h> int main(){         intfd, ret;         unsignedchar rdwr_addr = 0x42;   /* e2prom 讀寫地址 */         unsignedchar device_addr = 0x50; /* e2prom 裝置地址 */         unsignedchar data = 0x12;  /* 向e2prom寫的資料 */         structi2c_rdwr_ioctl_data e2prom_data;          fd= open("/dev/i2c/0", O_RDWR);         if(fd < 0) {                   perror("openerror");                   exit(1);         }          e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \                                               sizeof(structi2c_msg));         if(e2prom_data.msgs == NULL) {                   perror("mallocerror");                   exit(1);         }          ioctl(fd,I2C_TIMEOUT, 1); /* 設定逾時 */         ioctl(fd,I2C_RETRIES, 2); /* 設定重試次數 */                  /*向e2prom的rdwr_addr地址寫入資料data*/         e2prom_data.nmsgs= 1;         e2prom_data.msgs[0].len= 2;         e2prom_data.msgs[0].addr= device_addr;         e2prom_data.msgs[0].flags= 0;     /* write */                  e2prom_data.msgs[0].buf= (unsigned char *)malloc(2);         e2prom_data.msgs[0].buf[0]= rdwr_addr;    /* write address */         e2prom_data.msgs[0].buf[1]= data;      /* write data */          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);         if(ret < 0) {                   perror("writedata error");                   exit(1);         }         printf("writedata: %d to address: %#x\n", data, rdwr_addr);         data= 0;  /* be zero*/           /*從e2prom的rdwr_addr地址讀取資料存入buf*/         e2prom_data.nmsgs= 2;         e2prom_data.msgs[0].len= 1;         e2prom_data.msgs[0].addr= device_addr;//      e2prom_data.msgs[0].flags= 0;     /* write */         e2prom_data.msgs[0].buf= &rdwr_addr;          e2prom_data.msgs[1].len= 1;         e2prom_data.msgs[1].addr= device_addr;         e2prom_data.msgs[1].flags= 1;     /* read */         e2prom_data.msgs[1].buf= &data;          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);         if(ret < 0) {                   perror("readerror");                   exit(1);         }         printf("read  data: %d from address: %#x\n", data,rdwr_addr);                 free(e2prom_data.msgs);         close(fd);          return0;}

在mini2440開發板上已經實驗成功。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.