|
文件: |
iic_test.rar |
大小: |
0KB |
下载: |
下载 | |
网上分析IIC驱动数据传输流程的的文章还是比较少,大多数就是一个驱动的框架在哪里,没办法,偷懒不成就只好自己仔细的分析了,过程中也有一些不懂的地方,得到论坛里很多热心的朋友帮助, 感谢他们。发到网上跟大家共享自己的经验,让一些初学者能够少走一些弯路, 如果有错误希望大家能及时指正。谢谢!
首先分析I2C.DEV 中的 ioctl 函数,
=========两个重要的结构体==============
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
struct i2c_rdwr_ioctl_data {
struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32 nmsgs; /* number of i2c_msgs */
};
====================================
case I2C_RDWR:
if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg))) // arg 传递过来的是地址,强制转换为结构体类型指针
return -EFAULT;
/* Put an arbitrary limit on the number of messages that can
* be sent at once */
if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
return -EINVAL;
rdwr_pa = (struct i2c_msg *)
kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
GFP_KERNEL);
if (rdwr_pa == NULL) return -ENOMEM;
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
} // 第一个copy,已经拷贝arg->msgs指针到rdwr_arg的msgs指针,这里的COPY会把msgs指向的内容拷贝到rdwr_pa中,同时rdwr_pa->buf 指针指向用户空间msgs->buf 的指针
data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
res = 0;
for( i=0; i /* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) {
res = -EINVAL;
break;
}
data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;
// data_prts 指向 RDWR_pa[i].buf,即指向用户空间穿过来的buf中数据缓冲区
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);//重新分配buf
if(rdwr_pa[i].buf == NULL) {
res = -ENOMEM;
break;
}
if(copy_from_user(rdwr_pa[i].buf,
data_ptrs[i], // 把用户空间数据拷贝到 rdwr_pa[i].buf 中
rdwr_pa[i].len)) {
++i; /* Needs to be kfreed too */
res = -EFAULT;
break;
}
}
if (res < 0) {
int j;
for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
}
res = i2c_transfer(client->adapter, // 最终会调用s3c24xx_i2c_xfer函数
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);
}
kfree(data_ptrs);
kfree(rdwr_pa);
return res;
=============下面分析i2c_s3c2410.c的传输过程=========
启动s3c24xx_i2c_message_start函数开始传输,
在这个函数里面会首先发送从器件地址
addr = (msg->addr & 0x7f) << 1; // 我的24C02地址为0xa0,此处msg->addr=0x50
if (msg->flags & I2C_M_RD) {
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
} else
stat |= S3C2410_IICSTAT_MASTER_TX;
writeb(addr, i2c->regs + S3C2410_IICDS);
发送完后,会进入中断s3c24xx_i2c_irq中,处理中断标志后,进入发送下一个字节i2s_s3c_irq_nextbyte函数,此函数非常重要,几乎所有的功能在此完成
进入s3c24xx_i2c_doxfer函数的时候,会设置status=STATE_START,
因此,i2s_s3c_irq_nextbyte 函数首先分析 case state_start
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE; // 判断为读还是写
if (is_lastmsg(i2c) && i2c->msg->len == 0) { //没有消息,或者长度为0,停止总线
s3c24xx_i2c_stop(i2c, 0);
goto out_ack;
}
if (i2c->state == STATE_READ) // 若是读,直接跳到读命令分支
goto prepare_read;
// 否则,进入写分支
case STATE_WRITE:
retry_write:
if (!is_msgend(i2c)) { // 判断是否单个消息中的发送内容msg->buf[i]已经传输完毕
byte = i2c->msg->buf[i2c->msg_ptr++]; // 发送下一个内容
writeb(byte, i2c->regs + S3C2410_IICDS);
ndelay(i2c->tx_setup);
}
else if (!is_lastmsg(i2c)) // 判断多个消息msgs[i]是否已经结束 了
{
/* we need to go to the next i2c message */
dev_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0;
i2c->msg_idx ++;
i2c->msg++; // 指向下一个消息
/* check to see if we need to do another message */
if (i2c->msg->flags & I2C_M_NOSTART)
{ // 不需要重新启动,注释说不支持此模式
if (i2c->msg->flags & I2C_M_RD)
{
s3c24xx_i2c_stop(i2c, -EINVAL);
}
goto retry_write;
}
else
{
s3c24xx_i2c_message_start(i2c, i2c->msg); // 发送下条消息,重新启动,发送从机地址
i2c->state = STATE_START;
}
}
else
{ // 全部发送完成,结束
/* send stop */
s3c24xx_i2c_stop(i2c, 0);
}
break;
case STATE_READ: 读部分,比较简单就不分析了
就这样不停的在中断函数s3c24xx_i2c_irq里面跳啊跳,又跳到i2s_s3c_irq_nextbyte
最后到了结束的时候,s3c24xx_i2c_stop调用s3c24xx_i2c_master_complete函数,
接着使 i2c->msg_num = 0 唤醒睡眠
s3c24xx_i2c_doxfer函数中
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); 返回,
整个传输完成.
II2 测试程序