Linux驱动子系统之I2C(3)

来源:互联网 发布:js发送get请求 编辑:程序博客网 时间:2024/05/15 08:58

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源码分析

初始化模块


[cpp] view plaincopyprint?
  1. static int __init i2c_dev_init(void
  2.          res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); 
  3.   
  4.          i2c_dev_class= class_create(THIS_MODULE, "i2c-dev"); 
  5.   
  6.          /*Keep track of adapters which will be added or removed later */ 
  7.          res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); 
  8.   
  9.          /*绑定已经存在的适配器 */ 
  10.          i2c_for_each_dev(NULL,i2cdev_attach_adapter); 

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函数之前,我们需要了解一个结构体:


[cpp] view plaincopyprint?
  1. /* This is the structure as used in theI2C_RDWR ioctl call */ 
  2. struct i2c_rdwr_ioctl_data { 
  3.          structi2c_msg __user *msgs;         /* pointersto i2c_msgs */ 
  4.          __u32nmsgs;                    /* number ofi2c_msgs */ 
  5. }; 

/* 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


[cpp] view plaincopyprint?
  1. struct i2c_msg { 
  2.          __u16addr;     /* slave address                         */ 
  3.          __u16flags;  /* 默认为写入 */ 
  4. #define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */ 
  5. #define I2C_M_RD           0x0001     /* read data,from slave to master */ 
  6. #define I2C_M_NOSTART                  0x4000     /* if I2C_FUNC_PROTOCOL_MANGLING */ 
  7. #define I2C_M_REV_DIR_ADDR     0x2000     /*if I2C_FUNC_PROTOCOL_MANGLING */ 
  8. #define I2C_M_IGNORE_NAK          0x1000     /*if I2C_FUNC_PROTOCOL_MANGLING */ 
  9. #define I2C_M_NO_RD_ACK           0x0800     /* if I2C_FUNC_PROTOCOL_MANGLING */ 
  10. #define I2C_M_RECV_LEN               0x0400     /* length will be first received byte */ 
  11.          __u16len;                  /* msg length                              */ 
  12.          __u8*buf;                 /* pointer to msgdata                       */ 
  13. }; 

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函数:


[cpp] view plaincopyprint?
  1. static noinline inti2cdev_ioctl_rdrw(struct i2c_client *client, 
  2.                    unsignedlong arg) 
  3.          structi2c_rdwr_ioctl_data rdwr_arg; 
  4.          structi2c_msg *rdwr_pa; 
  5.          u8__user **data_ptrs; 
  6.          inti, res; 
  7.   
  8.          if(copy_from_user(&rdwr_arg, 
  9.                                (struct i2c_rdwr_ioctl_data __user *)arg, 
  10.                                sizeof(rdwr_arg))) 
  11.                    return-EFAULT; 
  12.   
  13.          if(rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) 
  14.                    return-EINVAL; 
  15.   
  16.          rdwr_pa= kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL); 
  17.   
  18.          if(copy_from_user(rdwr_pa, rdwr_arg.msgs, 
  19.                                rdwr_arg.nmsgs * sizeof(struct i2c_msg))) { 
  20.                    kfree(rdwr_pa); 
  21.                    return-EFAULT; 
  22.          } 
  23.   
  24.          res= i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); 
  25.          while(i-- > 0) { 
  26.                    if(res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { 
  27.                             if(copy_to_user(data_ptrs[i], rdwr_pa[i].buf, 
  28.                                                 rdwr_pa[i].len)) 
  29.                                      res= -EFAULT; 
  30.                    } 
  31.                    kfree(rdwr_pa[i].buf); 
  32.          } 

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任意地址字节写的时序:

下面的代码可以按照上面的两个图来阅读:


[cpp] view plaincopyprint?
  1. #include <stdio.h> 
  2. #include <linux/types.h> 
  3. #include <fcntl.h> 
  4. #include <unistd.h> 
  5. #include <stdlib.h> 
  6. #include <sys/types.h> 
  7. #include <sys/ioctl.h> 
  8. #include <errno.h> 
  9. #include <assert.h> 
  10. #include <string.h> 
  11. #include <linux/i2c.h> 
  12. #include <linux/i2c-dev.h> 
  13.   
  14. int main() 
  15.          intfd, ret; 
  16.          unsignedchar rdwr_addr = 0x42;   /* e2prom 读写地址 */ 
  17.          unsignedchar device_addr = 0x50; /* e2prom 设备地址 */ 
  18.          unsignedchar data = 0x12;  /* 向e2prom写的数据 */ 
  19.          structi2c_rdwr_ioctl_data e2prom_data; 
  20.   
  21.          fd= open("/dev/i2c/0", O_RDWR); 
  22.          if(fd < 0) { 
  23.                    perror("openerror"); 
  24.                    exit(1); 
  25.          } 
  26.   
  27.          e2prom_data.msgs= (struct i2c_msg *)malloc(e2prom_data.nmsgs * \ 
  28.                                                sizeof(structi2c_msg)); 
  29.          if(e2prom_data.msgs == NULL) { 
  30.                    perror("mallocerror"); 
  31.                    exit(1); 
  32.          } 
  33.   
  34.          ioctl(fd,I2C_TIMEOUT, 1); /* 设置超时 */ 
  35.          ioctl(fd,I2C_RETRIES, 2); /* 设置重试次数 */ 
  36.   
  37.          
  38.          /*向e2prom的rdwr_addr地址写入数据data*/ 
  39.          e2prom_data.nmsgs= 1; 
  40.          e2prom_data.msgs[0].len= 2; 
  41.          e2prom_data.msgs[0].addr= device_addr; 
  42.          e2prom_data.msgs[0].flags= 0;     /* write */ 
  43.   
  44.          
  45.          e2prom_data.msgs[0].buf= (unsigned char *)malloc(2); 
  46.          e2prom_data.msgs[0].buf[0]= rdwr_addr;    /* write address */ 
  47.          e2prom_data.msgs[0].buf[1]= data;      /* write data */ 
  48.   
  49.          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data); 
  50.          if(ret < 0) { 
  51.                    perror("writedata error"); 
  52.                    exit(1); 
  53.          } 
  54.          printf("writedata: %d to address: %#x\n", data, rdwr_addr); 
  55.          data= 0;  /* be zero*/ 
  56.   
  57.   
  58.          /*从e2prom的rdwr_addr地址读取数据存入buf*/ 
  59.          e2prom_data.nmsgs= 2; 
  60.          e2prom_data.msgs[0].len= 1; 
  61.          e2prom_data.msgs[0].addr= device_addr; 
  62. //      e2prom_data.msgs[0].flags= 0;     /* write */ 
  63.          e2prom_data.msgs[0].buf= &rdwr_addr; 
  64.   
  65.          e2prom_data.msgs[1].len= 1; 
  66.          e2prom_data.msgs[1].addr= device_addr; 
  67.          e2prom_data.msgs[1].flags= 1;     /* read */ 
  68.          e2prom_data.msgs[1].buf= &data; 
  69.   
  70.          ret= ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data); 
  71.          if(ret < 0) { 
  72.                    perror("readerror"); 
  73.                    exit(1); 
  74.          } 
  75.          printf("read  data: %d from address: %#x\n", data,rdwr_addr); 
  76.          
  77.          free(e2prom_data.msgs); 
  78.          close(fd); 
  79.   
  80.          return0; 

#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开发板上已经实验成功。


原创粉丝点击