i2c-core信息交互(5)

          前面我们已经说过i2c适配器的具体通信操作时靠struct io_algorithm来实现的。struct io_algorithm的结构非常简单只有三个成员master_xfer只要勇于i2c,smbus_xfer勇于smbus,functionality发挥适配器所提供的功能,在drivers/i2c/algos下提供了常用的struct io_algorithm的实现。前面我们在对驱动中的地址列表的探测中就用到i2c_smbus_xfer这个函数,这里就是让适配器来执行一次操作,我们具体来看看代码:

 

/**
 * i2c_smbus_xfer - execute SMBus protocol operations
 * @adapter: Handle to I2C bus
 * @addr: Address of SMBus slave on that bus
 * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
 * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
 * @command: Byte interpreted by slave, for protocols which use such bytes
 * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
 * @data: Data to be read or written
 *
 * This executes an SMBus protocol operation, and returns a negative
 * errno code else zero on success.
 */
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
     char read_write, u8 command, int protocol,
                   union i2c_smbus_data * data)
{
 s32 res;
 unsigned short tmp;
 
  flags &= I2C_M_TEN | I2C_CLIENT_PEC; 
//检测是否是10位地址或设置包错误检验标志

 if (adapter->algo->smbus_xfer) {
  mutex_lock(&adapter->bus_lock);
  res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,  //如果适配器是smbus总线则调用这个函数
      command, protocol, data);
  mutex_unlock(&adapter->bus_lock);
 } else
  res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,  //i2c总线的情况
           command, protocol, data);

 return res;
}

这里我们对smbus适配器的情况不做具体分析,而且是直接调用struct io_algorithm中的成员函数,i2c交互主要最终是调用i2c_transfer,然后这个函数中调用struct io_algorithm 中的master_xfer成员,交互中传递的阐参数主要是struct i2c_msg结构,而i2c_smbus_xfer_emulated函数的主要工作就是对各种操作情况下为i2c_transfer准备好参数,然后调用之,下面我们具体分析i2c_smbus_xfer_emulated源码:

/* Simulate a SMBus command using the i2c protocol
   No checking of parameters is done!  */
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
                                   unsigned short flags,
                                   char read_write, u8 command, int size,
                                   union i2c_smbus_data * data)
{
 /* So we need to generate a series of msgs. In the case of writing, we
   need to use only one message; when reading, we need two. We initialize
   most things with sane defaults, to keep the code below somewhat
   simpler. */

      // 从上面的解释中得到

      //写操作只会进行一次交互,而读操作,有时会有两次操作.
      //因为有时候读操作要先写command,再从总线上读数据
      //在这里为了代码的简洁.使用了两个缓存区,将两种情况统一起来.

 unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
 unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
 int num = read_write == I2C_SMBUS_READ?2:1;                 //读写需要消息的个数

     //与设备交互的数据,一般在msg[0]存放写入设备的信息,在msb[1]里存放接收到的信息.不过也有例外的
    //msg[2]的初始化,默认发送缓存区占一个字节,无接收缓存
 struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },           //初始化两个消息默认发送缓存区占一个字节,无接收缓存
                           { addr, flags | I2C_M_RD, 0, msgbuf1 }
                         };
 int i;
 u8 partial_pec = 0;
 int status;

 msgbuf0[0] = command;
 switch(size) {                         //交互的字节数

       //quick类型的,其它并不传输有效数据,只是将地址写到总线上,等待应答即可
        //所以将发送缓存区长度置为0 .再根据读/写操作,调整msg[0]的标志位 ,根据前面的标志,这里肯定是写
        //这类传输只需要一次总线交互
 case I2C_SMBUS_QUICK:
  msg[0].len = 0;
  /* Special case: The read/write field is used as data */
  msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
  num = 1;
  break;

        //BYTE类型指一次写和读只有一个字节.这种情况下,读和写都只会交互一次
        //这种类型的读有例外,它读取出来的数据不是放在msg[1]中的,而是存放在msg[0]
 case I2C_SMBUS_BYTE:
  if (read_write == I2C_SMBUS_READ) {
   /* Special case: only a read! */
   msg[0].flags = I2C_M_RD | flags;
   num = 1;
  }
  break;

       //Byte_Data是指命令+数据的传输形式.在这种情况下,写只需要一次交互,读却要两次
        //第一次将command写到总线上,第二次要转换方向.要将设备地址和read标志写入总线.
        //应回答之后再进行read操作
        //写操作占两字节,分别是command+data.读操作的有效数据只有一个字节
        //交互次数用初始化值就可以了
 case I2C_SMBUS_BYTE_DATA:
  if (read_write == I2C_SMBUS_READ)
   msg[1].len = 1;
  else {
   msg[0].len = 2;
   msgbuf0[1] = data->byte;
  }
  break;

        //Word_Data是指命令+双字节的形式.这种情况跟Byte_Data的情况类似
        //两者相比只是交互的数据大小不同
 case I2C_SMBUS_WORD_DATA:
  if (read_write == I2C_SMBUS_READ)
   msg[1].len = 2;
  else {
   msg[0].len=3;
   msgbuf0[1] = data->word & 0xff;
   msgbuf0[2] = data->word >> 8;     //高位在高字节
  }
  break;

        //Proc_Call的方式与write 的Word_Data相似,只不过写完Word_Data之后,要等待它的应答
        //应该它需要交互两次,一次写一次读
 case I2C_SMBUS_PROC_CALL:
  num = 2; /* Special case */
  read_write = I2C_SMBUS_READ;
  msg[0].len = 3;
  msg[1].len = 2;
  msgbuf0[1] = data->word & 0xff;
  msgbuf0[2] = data->word >> 8;
  break;

        //Block_Data:指command+N段数据的情况.
        //如果是读操作,它首先要写command到总线,然后再读N段数据.要写的command已经
        //放在msg[0]了.现在只需要将msg[1]的标志置I2C_M_RECV_LEN位,msg[1]有效长度为1字节.因为
        //adapter驱动会处理好的.现在现在还不知道要传多少段数据.
 
        //对于写的情况:msg[1]照例不需要.将要写的数据全部都放到msb[0]中.相应的也要更新
        //msg[0]中的缓存区长度
 case I2C_SMBUS_BLOCK_DATA:
  if (read_write == I2C_SMBUS_READ) {
   msg[1].flags |= I2C_M_RECV_LEN;
   msg[1].len = 1; /* block length will be added by
        the underlying bus driver */
  } else {

//data->block[0]表示后面有多少段数据.总长度要加2是因为command+count+N段数据
   msg[0].len = data->block[0] + 2;
   if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
    dev_err(&adapter->dev,
     "Invalid block write size %d\n",
     data->block[0]);
    return -EINVAL;
   }
   for (i = 1; i < msg[0].len; i++)
    msgbuf0[i] = data->block[i-1];
  }
  break;

//Proc_Call:表示写完Block_Data之后,要等它的应答消息它和Block_Data相比,只是多了一部份应答而已
 case I2C_SMBUS_BLOCK_PROC_CALL:
  num = 2; /* Another special case */
  read_write = I2C_SMBUS_READ;
  if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
   dev_err(&adapter->dev,
    "Invalid block write size %d\n",
    data->block[0]);
   return -EINVAL;
  }
  msg[0].len = data->block[0] + 2;
  for (i = 1; i < msg[0].len; i++)
   msgbuf0[i] = data->block[i-1];
  msg[1].flags |= I2C_M_RECV_LEN;
  msg[1].len = 1; /* block length will be added by
       the underlying bus driver */
  break;

        //I2c Block_Data与Block_Data相似,只不过read的时候,数据长度是预先定义好了的.另外
        //与Block_Data相比,中间不需要传输Count字段.(Count表示数据段数目)
 case I2C_SMBUS_I2C_BLOCK_DATA:
  if (read_write == I2C_SMBUS_READ) {
   msg[1].len = data->block[0];
  } else {
   msg[0].len = data->block[0] + 1;
   if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
    dev_err(&adapter->dev,
     "Invalid block write size %d\n",
     data->block[0]);
    return -EINVAL;
   }
   for (i = 1; i <= data->block[0]; i++)
    msgbuf0[i] = data->block[i];
  }
  break;
 default:
  dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
  return -EOPNOTSUPP;
 }

 i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK  //没有数据
          && size != I2C_SMBUS_I2C_BLOCK_DATA);  //不支持
 if (i) {
  /* Compute PEC if first message is a write */
  if (!(msg[0].flags & I2C_M_RD)) {
   if (num == 1) /* Write only */

    //如果只有写操作,写缓存区要扩充一个字节,用来存放计算出来的PEC

    i2c_smbus_add_pec(&msg[0]);
   else /* Write followed by read */

                //如果后面还有读操作,先计算前面写部份的PEC(注意这种情况下不需要
                //扩充写缓存区,因为不需要发送PEC.只会接收到PEC)
    partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
  }
  /* Ask for PEC if last message is a read */
  if (msg[num-1].flags & I2C_M_RD)
   msg[num-1].len++;
 }

 status = i2c_transfer(adapter, msg, num); //执行一个或几个消息的适配器操作
 if (status < 0)
  return status;

 /* Check PEC if last message is a read */

//操作完了之后,如果最后一个操作是PEC的读操作.检验后面的PEC是否正确
 if (i && (msg[num-1].flags & I2C_M_RD)) {
  status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
  if (status < 0)
   return status;
 }

 if (read_write == I2C_SMBUS_READ)  //把读到的数据返回
  switch(size) {
   case I2C_SMBUS_BYTE:
    data->byte = msgbuf0[0];
    break;
   case I2C_SMBUS_BYTE_DATA:
    data->byte = msgbuf1[0];
    break;
   case I2C_SMBUS_WORD_DATA:
   case I2C_SMBUS_PROC_CALL:
    data->word = msgbuf1[0] | (msgbuf1[1] << 8);
    break;
   case I2C_SMBUS_I2C_BLOCK_DATA:
    for (i = 0; i < data->block[0]; i++)
     data->block[i+1] = msgbuf1[i];
    break;
   case I2C_SMBUS_BLOCK_DATA:
   case I2C_SMBUS_BLOCK_PROC_CALL:
    for (i = 0; i < msgbuf1[0] + 1; i++)
     data->block[i] = msgbuf1[i];
    break;
  }
 return 0;
}

i2c_transfer就是在适配器空闲的情况下调用master_xfer完成实际操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值