I2C驱动之总线接口i2c_transfer

1. i2c_transfer()

    函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程. 成功返回0

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
	 int ret;

 	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\n", ret, msgs[ret].flags & I2C_M_RD ?
		 'R' : 'W', msgs[ret].addr, msgs[ret].len);
 	}
#endif
	 down(&adap->bus_lock);
       /* 主要就是这一句,调用master_xfer() */
        ret = adap->algo->master_xfer(adap,msgs,num);
	 up(&adap->bus_lock);

 	return ret;
	 } else {
 		dev_dbg(&adap->dev, "I2C level transfers not supported\n");
		return -ENOSYS;
	}
}
2. master_xfer()

    下面的代码是基于s3c2410,代码在driver/i2c/busses/i2c_s3c2410.c    

static struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.name			= "S3C2410-I2C-Algorithm",
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
}
上面的是i2c总线的注册信息,master_xfer被设置成 s3c24xx_i2c_xfer.当信息通道i2c总线传输,这是第一个被调用的端口

该函数实现了重试功能, 数据传输的实现是调用s3c24xx_i2c_doxfer().

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{           
//从适配器的私有数据中获得适配器s3c24xx_i2c结构体
     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;int retry;int ret; /* 重试的次数,adap->retries */
     for (retry = 0; retry < adap->retries; retry++) { 
         /* 主要就这一句,调用s3c24xx_i2c_doxfer()*/
         ret = s3c24xx_i2c_doxfer(i2c, msgs, num); 
         if (ret != -EAGAIN) 
             return ret; 
         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); 
         udelay(100); 
      }
      return -EREMOTEIO;
}


 3. s3c24xx_i2c_doxfer() 

     该函数开始传输数据,成功返回0,否则返回-EAGAIN

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{
	unsigned long timeout;
	int ret;
        /*设置成主设备发送状态,判断是否属于busy,不忙返回0,否则返回-ETIMEOUT*/
	ret = s3c24xx_i2c_set_master(i2c);
 	if (ret != 0) {
		 dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
 		 ret = -EAGAIN;
		 goto out;
	}

	spin_lock_irq(&i2c->lock);
        /* 设置参数 将状态设置成开始*/
	i2c->msg     = msgs;
	i2c->msg_num = num;
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;

	s3c24xx_i2c_enable_irq(i2c);       /* 使能中断 */
	s3c24xx_i2c_message_start(i2c, msgs); /* 重要 主设备将信息发送到总线上*/
	spin_unlock_irq(&i2c->lock);
	/* 进入睡眠,直到 i2c->msg_num == 0 或 HZ * 5 时间后;正常返回meg_num*/
	timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

	ret = i2c->msg_idx;

	if (timeout == 0)
		 dev_dbg(i2c->dev, "timeout\n");
	 else if (ret != num)
		 dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

	 msleep(1);

  out:
	 return ret;
}

4.s3c24xx_i2c_message_start()

    该函数配置寄存器,完成在I2C总线上广播第一个slave address + write/read


static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,struct i2c_msg *msg){
        /* 设置从设备地址 */
        unsigned int addr = (msg->addr & 0x7f) << 1; 
	unsigned long stat; //slave device address
	unsigned long iiccon;
        /*配置register设置i2c总线start,并完成前面广播从设备地址的功能*/
	stat = 0;
	stat |=  S3C2410_IICSTAT_TXRXEN;

	if (msg->flags & I2C_M_RD) {
		/* 设置写操作 */
		stat |= S3C2410_IICSTAT_MASTER_RX;
		addr |= 1; //last bit = 1
	 } else
		/* 设置读操作 */
		stat |= S3C2410_IICSTAT_MASTER_TX;
         //this should has "addr |=0;"
 	if (msg->flags & I2C_M_REV_DIR_ADDR)
		addr ^= 1;

	 /* 使能ACK */
	 s3c24xx_i2c_enable_ack(i2c);

	 iiccon = readl(i2c->regs + S3C2410_IICCON);
	 writel(stat, i2c->regs + S3C2410_IICSTAT);

	 dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
	 writeb(addr, i2c->regs + S3C2410_IICDS);

	 ndelay(i2c->tx_setup);

	 dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
	 writel(iiccon, i2c->regs + S3C2410_IICCON);

	 stat |= S3C2410_IICSTAT_START;
	 writel(stat, i2c->regs + S3C2410_IICSTAT);
}


 
  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`i2c_transfer`是Linux内核提供的函数,用于在I2C总线上进行数据传输。该函数可以同时处理多个I2C消息,包括读取和写入操作。 函数原型如下: ```c int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); ``` 参数说明: - `adap`:指向要进行传输的I2C适配器(总线)的指针。 - `msgs`:指向I2C消息数组的指针,每个消息包含了要传输的数据、设备地址和标志位等信息。 - `num`:要传输的消息数量。 返回值为传输的消息数量,如果返回值小于`num`,则表示传输过程中发生了错误。 下面是一个简单的示例代码,演示如何使用`i2c_transfer`函数进行I2C数据传输: ```c #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("/dev/i2c-0", O_RDWR); if (fd < 0) { perror("Failed to open I2C bus"); return 1; } struct i2c_msg msgs[2]; unsigned char buf[2]; int ret; // 设置从设备地址 msgs[0].addr = 0x50; msgs[0].flags = 0; msgs[0].buf = buf; msgs[0].len = 1; // 读取数据 buf[0] = 0x00; msgs[1].addr = 0x50; msgs[1].flags = I2C_M_RD; msgs[1].buf = buf; msgs[1].len = 4; ret = i2c_transfer(fd, msgs, 2); if (ret < 0) { perror("Failed to transfer data"); close(fd); return 1; } // 处理读取的数据 // ... close(fd); return 0; } ``` 上述示例代码中,首先打开I2C总线设备文件`/dev/i2c-0`,然后使用`i2c_msg`结构创建一个I2C消息数组。在示例中,我们设置了两个消息:第一个消息用于写入要读取的寄存器地址,第二个消息用于读取4个字节的数据。最后,我们使用`i2c_transfer`函数进行数据传输,返回值表示成功传输的消息数量。 需要注意的是,上述代码适用于在用户空间中进行I2C数据传输,如果需要在内核驱动中使用`i2c_transfer`函数,可以参考内核源代码中相关的I2C驱动实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值