Linux I2C核心、总线与设备驱动

来源:互联网 发布:互联网进入大数据时代 编辑:程序博客网 时间:2024/05/16 18:06
i2c_transfer函数
i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。
struct i2c_algorithm {
 /* If an adapter algorithm can't do I2C-level access, set master_xfer
    to NULL. If an adapter algorithm can do SMBus access, set
    smbus_xfer. If set to NULL, the SMBus protocol is simulated
    using common I2C messages */
 /* master_xfer should return the number of messages successfully
    processed, or a negative value on error */
 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,   //i2c传输函数指针,在amlogic芯片中是在drivers/amlogic/i2c/aml_i2c.c中定义
      int num);                   //master_xfer提供的是i2c_transfer实现部分。更多的I2C adapter工作于I2C总线主机模式。
 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
      unsigned short flags, char read_write,       //smbus传输函数指针
      u8 command, int size, union i2c_smbus_data *data);   // smbus_xfer提供i2c_smbus_xfer的实现部分。只有I2C adapter工作于SMBus模式,需要提供。

 /* To determine what the adapter supports */
 u32 (*functionality) (struct i2c_adapter *);     //返回适配器支持的功能
};

struct i2c_adapter {
 struct module *owner;   //所属模块
 unsigned int id;        //algorithm 的类型,定义于i2c-id.h,以I2C_ALGO_开始
 unsigned int class;    /* classes to allow probing for */
 const struct i2c_algorithm *algo;  //总线通信方法的结构体指针
 void *algo_data;   /*使用标准算法时,提供算法需要的特殊函数入口*/

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

 int timeout;   /* in jiffies */   //超时时间
 int retries;                                   //重试次数
 struct device dev;  /* the adapter device */  //适配器

 int nr;
 char name[48];    //适配器名称
 struct completion dev_released;   //用于同步

 struct list_head userspace_clients;
};

struct i2c_driver {
 unsigned int class;

 /* Notifies the driver that a new bus has appeared or is about to be
  * removed. You should avoid using this if you can, it will probably
  * be removed in a near future.
  */
        /* 寻找当前client对应的adapter是否存在。
           若存在,调用i2c_attach_client更新adapter的client
        */
 int (*attach_adapter)(struct i2c_adapter *);   //依附i2c_adapter函数指针
 int (*detach_adapter)(struct i2c_adapter *);   //脱离i2c_adapter函数指针

 /* Standard driver model interfaces */
 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
 int (*remove)(struct i2c_client *);

 /* driver model interfaces that don't relate to enumeration  */
 void (*shutdown)(struct i2c_client *);
 int (*suspend)(struct i2c_client *, pm_message_t mesg);
 int (*resume)(struct i2c_client *);

 /* Alert callback, for example for the SMBus alert protocol.
  * The format and meaning of the data value depends on the protocol.
  * For the SMBus alert protocol, there is a single bit of data passed
  * as the alert response's low bit ("event flag").
  */
 void (*alert)(struct i2c_client *, unsigned int data);

 /* a ioctl like command that can be used to perform specific functions
  * with the device.
  */
 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);   //类似ioctl

 struct device_driver driver;   //设备驱动结构体
 const struct i2c_device_id *id_table;

 /* Device detection callback for automatic device creation */
 int (*detect)(struct i2c_client *, struct i2c_board_info *);
 const unsigned short *address_list;
 struct list_head clients;   //链表头
};


struct i2c_client {
 unsigned short flags;  /* div., see below  */   //标志
 unsigned short addr;  /* chip address - NOTE: 7bit */   //低7位为芯片地址
     /* addresses are stored in the */
     /* _LOWER_ 7 bits  */
 char name[I2C_NAME_SIZE];                                           //client 名字
 struct i2c_adapter *adapter; /* the adapter we sit on */   //client所在I2C bus对应的adapter
 struct i2c_driver *driver; /* and our access routines */          //client 使用的驱动
 struct device dev;  /* the device structure  */    //struct device定义在linux/device.h中
 int irq;   /* irq issued by device  */   
 struct list_head detected;      //链表头
};


这i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及关系
1、i2c_adapter与i2c_algorithm
i2c_adapter 对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配 器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm的指针。
i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键,它的定义如下:
i2c_msg结构体
struct i2c_msg {
   __u16 addr; /* 设备地址*/
   __u16 flags; /* 标志 */
   __u16 len;   /* 消息长度*/
   __u8 *buf;   /* 消息数据*/
};

2、i2c_driver与i2c_client
i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。
i2c_driver 与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,
把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter, driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。
相反的过程发生在 i2c_driver 的detach_client()函数被调用的时候。


3、i2c_adpater与i2c_client
i2c_adpater 与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client 的链表。

假设I2C总线适配器xxx上有两个使用相同驱动程序的yyy I2C设备,在打开该I2C总线的设备结点后相关数据结构之间的逻辑组织关系将如下图所示。

Linux I2C核心、总线与设备驱动 - Tanatseng - Tanatseng Blog

 

图I2C驱动各数据结构关系

 I2C驱动各数据结构关系 从上面的分析可知,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的 Linux I2C子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?
理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。
一方面,适配器驱动可能是Linux内核本身还不包含的。另一方面,挂接在适配器上的具体设备驱动可能也是Linux不存在的。即便上述设备驱动都存在于Linux内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作将包括:
1、提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2C的I/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。
2、提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。
3、实现I2C设备驱动与i2c_driver接口,用具体设备yyy的yyy_attach_adapter()函数指针、 yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driver的attach_adapter、 detach_adapter和detach_client指针。
4、实现I2C设备驱动的文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等。
上述工作中1、2属于I2C总线驱动,3、4属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。


1) I2C核心(kernel/drivers/i2c/i2c-core.c)中提供了一组不依赖于硬件平台的接口函数,这个文件一般不需要被工程师修改,
结构的填充及外围芯片驱动函数还得自己写,如amlogle芯片的i2c模块函数。

I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带。I2C核心中的主要函数包括:
1、增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);
2、增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);
3、 i2c_client依附/脱离
int i2c_attach_client(struct i2c_client *client);
int i2c_detach_client(struct i2c_client *client);
当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反地,在client被取消关联的时候,sysfs文件和设备也被注销。
4、i2c传输、发送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);
i2c_transfer()函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。
i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)         
{
 unsigned long orig_jiffies;
 int ret, try;

 /* REVISIT the fault reporting model here is weak:
  *
  *  - When we get an error after receiving N bytes from a slave,
  *    there is no way to report "N".
  *
  *  - When we get a NAK after transmitting N bytes to a slave,
  *    there is no way to report "N" ... or to let the master
  *    continue executing the rest of this combined message, if
  *    that's the appropriate response.
  *
  *  - When for example "num" is two and we successfully complete
  *    the first message but get an error part way through the
  *    second, it's unclear whether that should be reported as
  *    one (discarding status on the second message) or errno
  *    (discarding status on the first one).
  */

 if (adap->algo->master_xfer) {
#ifdef DEBUG
  for (ret = 0; ret < num; ret++) {
   dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
    "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
    ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
    (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
  }
#endif

        //首先判断判断是否充许睡眠,如果不允许,尝试获锁.如果获锁失败,则返回,这样的操作是避免进入睡眠,我们在后面也可以看到,实际的传输工作交给了adap->algo->master_xfer()完成.
  if (in_atomic() || irqs_disabled()) {
   ret = rt_mutex_trylock(&adap->bus_lock);   //获取自旋锁
   if (!ret)
    /* I2C activity is ongoing. */
    return -EAGAIN;
  } else {
   rt_mutex_lock(&adap->bus_lock);
  }

  /* Retry automatically on arbitration loss */
  orig_jiffies = jiffies;
  for (ret = 0, try = 0; try <= adap->retries; try++) {
   ret = adap->algo->master_xfer(adap, msgs, num);   //调用master_xfer传送信息
   if (ret != -EAGAIN)
    break;
   if (time_after(jiffies, orig_jiffies + adap->timeout))
    break;
  }
  rt_mutex_unlock(&adap->bus_lock);   //解锁

  return ret;
 } else {
  dev_dbg(&adap->dev, "I2C level transfers not supported\n");
  return -EOPNOTSUPP;
 }
}
EXPORT_SYMBOL(i2c_transfer);


说明:i2c_transfer(),这其实是个会调用我们自己写的传输方法函数。通过下面这句函数:
ret = adap->algo->master_xfer(adap,msgs,num);
这是个回调函数,或者说是函数指针,他指向我们自己写xxx_i2c_transfer()。(linux里面有很多这种类似的回调函数)。这实际上就是我们自己根据I2C通信协议写的I2C传输方法。
然后每个I2C设备都会调用这个xxx_i2c_transfer()。
在amlogic芯片的I2C是在drivers/amlogic/i2c/aml_i2c.c中,不过名字为aml_i2c_xfer()。如下:
static const struct i2c_algorithm aml_i2c_algorithm = {
 .master_xfer = aml_i2c_xfer,
 .functionality = aml_i2c_func,
};

/*General i2c master transfer*/
static int aml_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
       int num)
{
 struct aml_i2c *i2c = &aml_i2c_ddata;
 struct i2c_msg * p;
 unsigned int i;
 unsigned int ret=0;
 
 spin_lock(&i2c->lock);
 
 i2c->ops->xfer_prepare(i2c);

 for (i = 0; !ret && i < num; i++) {
  p = &msgs[i];
  i2c->msg_flags = p->flags;
  ret = i2c->ops->do_address(i2c, p->addr, p->buf, p->flags & I2C_M_RD, p->len);
  if (ret || !p->len)
   continue;
  if (p->flags & I2C_M_RD)  
   ret = i2c->ops->read(i2c, p->buf, p->len);
  else
   ret = i2c->ops->write(i2c, p->buf, p->len);
 }
 
 i2c->ops->stop(i2c);

 spin_unlock(&i2c->lock);
 
 if (p->flags & I2C_M_RD){
  AML_I2C_DBG("read ");
 }
 else {
  AML_I2C_DBG("write ");
 }
 for(i=0;i<p->len;i++)
  AML_I2C_DBG("%x-",*(p->buf)++);
 AML_I2C_DBG("\n");
 
 /* Return the number of messages processed, or the error code*/
 if (ret == 0)
  return num;
 else {
  printk("[aml_i2c_xfer] error ret = %d \t", ret);
  printk( "i2c master %s current slave addr is 0x%x\n",
      i2c->master_no?"a":"b", i2c->cur_slave_addr);
  return ret;
 }
}
最终转换成了自己编写的读、写i2c函数了。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

5、I2C控制命令分派
下面函数有助于将发给I2C适配器设备文件ioctl的命令分派给对应适配器的algorithm的algo_control()函数或i2c_driver的command()函数:
int i2c_control(struct i2c_client *client, unsigned int cmd, unsigned long arg);
void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg);


2) Linux I2C总线驱动
1、初始化I2C适配器所使用的硬件资源,申请I/O地址、中断号等。
2、通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。
I2C总线驱动模块的卸载函数要完成的工作与加载函数的相反:
3、释放I2C适配器所使用的硬件资源,释放I/O地址、中断号等。
4、通过i2c_del_adapter()删除i2c_adapter的数据结构。

I2C总线驱动模块加载和卸载函数模板
static int __init i2c_adapter_xxx_init(void)
{
  xxx_adpater_hw_init();
  i2c_add_adapter(&xxx_adapter);
}
 
static void __exit i2c_adapter_xxx_exit(void)
{
   xxx_adpater_hw_free();
   i2c_del_adapter(&xxx_adapter);
}
上述代码中xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和I2C设备硬件直接相关。


3) I2C总线通信方法
我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。
functionality ()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、 I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。
master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息。I2C总线驱动master_xfer函数模板:
static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
 ...
 for (i = 0; i flags &I2C_M_RD)
    {
      i2c_adapter_xxx_setaddr((msg->addr buf, msgs->len); /*读取msgs->len长的数据到msgs->buf*/
    }
    else
    /*是写消息*/
    {
      i2c_adapter_xxx_setaddr(msg->addr buf, msgs->len); /*读取msgs->len长的数据到msgs->buf*/
    }
 }
 i2c_adapter_xxx_stop(); /*产生停止位*/
}
上述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为 (msg->addr addr。如下图:algorithm中master_xfer的时序

Linux I2C核心、总线与设备驱动 - Tanatseng - Tanatseng Blog

 
algorithm中master_xfer的时序
master_xfer()函 数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()、 i2c_adapter_xxx_wait_ack()、i2c_adapter_xxx_readbytes()、 i2c_adapter_xxx_writebytes()和
i2c_adapter_xxx_stop()函数用于完成适配器的底层硬件操作,与I2C 适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。
如amlogic芯片在上面已经所说到一点了。

下面谈论具体的流程:
多数I2C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapter的algo_data(类似“私有数据”),其中包含 I2C消息数组指针、数组索引及I2C适配器algorithm访问控制用的自旋锁、等待队列等,
而master_xfer()函数完成消息数组中消息的 处理也可通过对xxx_i2c结构体相关成员的访问来控制。下面给出了xxx_i2c结构体的定义。
xxx_i2c结构体模板:
struct aml_i2c {
 spinlock_t  lock;
 unsigned int   i2c_debug;
 unsigned int  cur_slave_addr;
 unsigned int   wait_count;
 unsigned int   wait_ack_interval;
 unsigned int   wait_read_interval;
 unsigned int   wait_xfer_interval;
 unsigned int   master_no;/*master a:0 master b:1*/
 #define    MASTER_A  0
 #define   MASTER_B  1
 unsigned char  token_tag[AML_I2C_MAX_TOKENS];
 unsigned int   msg_flags;

 struct i2c_adapter* adap;
 struct aml_i2c_ops* ops;

 struct aml_i2c_reg_master __iomem* master_regs;
 
//#ifdef CONFIG_I2C_AML_SLAVE
 struct aml_i2c_reg_slave __iomem* slave_regs;
 struct i2c_slave_client *slave;
 
 struct aml_pinmux_reg_bit slave_pinmux;
 unsigned int  slave_dev_addr;
//#endif

 void __iomem  *reg_base;

 struct aml_pinmux_reg_bit master_pinmux;
  
 int   irq;
 unsigned int  use_pio;/*0: hardware i2c, 1: pio i2c*/
 unsigned int  master_i2c_speed;
};

4) Linux I2C设备驱动

1、I2C 设备驱动要使用i2c_driver和i2c_client数据结构并填充其中的成员函数。i2c_client一般被包含在设备的私有信息结构体 yyy_data中,
而i2c_driver则适宜被定义为全局变量并初始化,下面显示了被初始化的i2c_driver。

以ft5406触摸屏为例
static struct i2c_driver ft5x0x_ts_driver = {
 .probe  = ft5x0x_ts_probe, //当有i2c_client和i2c_driver匹配时调用
 .remove  = __devexit_p(ft5x0x_ts_remove),
 .id_table = ft5x0x_ts_id,  //匹配规则
 .driver = {
  .name = FT5X0X_NAME,
  .owner = THIS_MODULE,
 },
};

2、Linux I2C设备驱动模块加载与卸载

I2C设备驱动模块加载函数通用的方法是在I2C设备驱动模块加载函数中完成两件事:
1>通过register_chrdev()函数将I2C设备注册为一个字符设备。
2>通过I2C核心的i2c_add_driver()函数添加i2c_driver。
在模块卸载函数中需要做相反的两件事:
3>通过I2C核心的i2c_del_driver()函数删除i2c_driver。
4>通过unregister_chrdev()函数注销字符设备。
I2C设备驱动加载与卸载函数的模板。

以ft5406触摸屏为例
static int __init ft5x0x_ts_init(void)
{
 return i2c_add_driver(&ft5x0x_ts_driver);
}

static void __exit ft5x0x_ts_exit(void)
{
 i2c_del_driver(&ft5x0x_ts_driver);
}

3. Linux I2C设备驱动i2c_driver成员函数
i2c_add_driver(&ft5x0x_ts_driver)的执行会引发i2c_driver结构体中ft5x0x_ts_probe()函数的执行,我们可以在 ft5x0x_ts_probe()函数里探测物理设备。具体的可以看我上篇probe何时执行文章。
I2C 设备驱动卸载函数进行i2c_del_driver(&yyy_driver)调用后,会引发与yyy_driver关联的每个 i2c_client与之解除关联。


4. Linux I2C设备驱动文件操作接口
作为一种字符类设备,Linux I2C设备驱动文件操作接口与普通的设备驱动是完全一致的,但是在其中要使用i2c_client、i2c_driver、i2c_adapter和 i2c_algorithm结构体和I2C核心,并且对设备的读写和控制需要借助体系结构中各组成部分的协同合作。
I2C设备文件接口写函数范例
static ssize_t yyy_write(struct file *file, char *buf, size_t count, loff_t off)
{
    struct i2c_client *client = (struct i2c_client*)file->private_data;
    i2c_msg msg[1];
    char *tmp;
    int ret;
 
    tmp = kmalloc(count, GFP_KERNEL);
    if (tmp == NULL)
       return  - ENOMEM;
    if (copy_from_user(tmp, buf, count))
    {
       kfree(tmp);
       return  - EFAULT;
    }
 
    msg[0].addr = client->addr;//地址
    msg[0].flags = 0;       //0为写
    msg[0].len = count;     //要写的字节数
    msg[0].buf = tmp;      //要写的数据
    ret = i2c_transfer(client->adapter, msg, 1);  //传输i2c消息
    return (ret == 1) ? count : ret;
}
上述程序给出的仅仅是一个写函数的例子,具体的写操作与设备密切相关。我们通过这个例来仔细分析I2C设备读写过程中数据的流向和函数的调用关系。I2C设备的写操作经历了如下几个步骤:
① 从用户空间到字符设备驱动写函数接口,写函数构造I2C消息数组。
② 写函数把构造的I2C消息数组传递给I2C核心的传输函数i2c_transfer()。
③ I2C核心的传输函数i2c_transfer()找到对应适配器algorithm的通信方法函数master_xfer()去最终完成I2C消息的处理。

描述了从用户空间发起读写操作到algorithm进行消息传输的流程

Linux I2C核心、总线与设备驱动 - Tanatseng - Tanatseng Blog

 
I2C设备读写完整流程
通常,如果I2C设备不是一个输入输出设备或存储设备,就并不需要给I2C设备提供读写函数。许多I2C设备只是需要被设置以某种方式工作,而不是被读写。
另外,I2C设备驱动的文件操作接口也不是必需的,甚至极少被需要。大多数情况下,我们只需要通过i2c-dev.c文件提供的I2C适配器设备文件接口 就可完成对I2C设备的读写。
Linux i2c-dev.c文件分析
i2c-dev.c文件完全可以被看作一个I2C设 备驱动,其结构与上述的描述是基本一致的,不过,它实现的一个i2c_client是虚拟的、临时的,随着设备文件的打开而产生,并随设备文件的关闭而撤销,
并没有被添加到i2c_adapter的clients链表中。i2c-dev.c针对每个I2C适配器生成一个主设备为 89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主体是“i2c_driver成员函数 + 字符设备驱动”。
i2c-dev.c中提供i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的read()和 write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()和i2c_master_send()函数来构造1条 I2C消息并引发适配器algorithm通信函数的调用,
完成消息的传输,对应于下图1所示的时序。但是,很遗憾,大多数稍微复杂一点I2C设备的读写流程并不对应于1条消息,往往需要2条甚至更多的消息来进行一次读写周期(即如图2所示的重复开始位RepStart模式),
这种情况下,在应用层仍然调用read()、write()文件API来读写I2C设备,将不能正确地读写。许多工程师碰到过类似的问题,往往经过相当长时间的调试都没法 解决I2C设备的读写,连错误的原因也无法找到,显然是对i2cdev_read()和i2cdev_write()函数的作用有所误解。

鉴于上述原因,i2c-dev.c中i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能适用于非 RepStart模式的情况。对于2条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。