免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 912 | 回复: 0
打印 上一主题 下一主题

IIC总线数据传输流程分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-03 09:02 |只看该作者 |倒序浏览
这些天研究IIC 总线,得到论坛很多大虾的帮助,谢谢你们的无私,才让我们这样的新手能得到提高. 本着班门弄斧的精神,把自己的学习经验共享出来,以供更多的新手能够少走一些弯路. 里面可能也会有错误,希望指正.谢谢. 别笑我哈,我是新手.呵呵
因为I2C.DEV 中的IOCTL 函数就能完成IIC总线的读写数据,因此
首先分析I2C.DEV 中的 ioctl 函数,
=========两个重要的结构体==============
[color="#000000"]struct i2c_msg [color="#0000cc"]{
__u16 addr[color="#0000cc"]; [color="#ff9900"]/* slave address   */
__u16 flags[color="#0000cc"];
[color="#0000cc"]#[color="#ff0000"]define I2C_M_TEN 0x10 [color="#ff9900"]/* we have a ten bit chip address */
[color="#0000cc"]#[color="#ff0000"]define I2C_M_RD 0x01
[color="#0000cc"]#[color="#ff0000"]define I2C_M_NOSTART 0x4000
[color="#0000cc"]#[color="#ff0000"]define I2C_M_REV_DIR_ADDR 0x2000
[color="#0000cc"]#[color="#ff0000"]define I2C_M_IGNORE_NAK 0x1000
[color="#0000cc"]#[color="#ff0000"]define I2C_M_NO_RD_ACK  0x0800
[color="#0000cc"]#[color="#ff0000"]define I2C_M_RECV_LEN  0x0400 [color="#ff9900"]/* length will be first received byte */
__u16 len[color="#0000cc"];  [color="#ff9900"]/* msg length    */
__u8 [color="#0000cc"]*buf[color="#0000cc"];  [color="#ff9900"]/* pointer to msg data   */
[color="#0000cc"]}[color="#0000cc"];

[color="#0000ff"]struct i2c_rdwr_ioctl_data [color="#0000cc"]{
[color="#0000ff"]struct i2c_msg __user [color="#0000cc"]*msgs[color="#0000cc"]; [color="#ff9900"]/* pointers to i2c_msgs */
__u32 nmsgs[color="#0000cc"];   [color="#ff9900"]/* number of i2c_msgs */
[color="#0000cc"]}[color="#0000cc"];
====================================
[color="#000000"]case I2C_RDWR[color="#0000cc"]:
  [color="#0000ff"]if [color="#0000cc"](copy_from_user[color="#0000cc"]([color="#0000cc"]&rdwr_arg[color="#0000cc"],
       [color="#0000cc"]([color="#0000ff"]struct i2c_rdwr_ioctl_data __user [color="#0000cc"]*[color="#0000cc"])[color="#ff0000"]arg[color="#0000cc"],
       [color="#0000ff"]sizeof[color="#0000cc"](rdwr_arg[color="#0000cc"])[color="#0000cc"])[color="#0000cc"])   // arg 传递过来的是地址,强制转换为结构体类型指针
   [color="#0000ff"]return [color="#0000cc"]-EFAULT[color="#0000cc"];
  /* Put an arbitrary limit on the number of messages that can
   * be sent at once */
  [color="#0000ff"]if [color="#0000cc"](rdwr_arg[color="#0000cc"].nmsgs [color="#0000cc"]> I2C_RDRW_IOCTL_MAX_MSGS[color="#0000cc"])
   [color="#0000ff"]return [color="#0000cc"]-EINVAL[color="#0000cc"];
  rdwr_pa [color="#0000cc"]= [color="#0000cc"]([color="#0000ff"]struct i2c_msg [color="#0000cc"]*[color="#0000cc"])
   kmalloc[color="#0000cc"](rdwr_arg[color="#0000cc"].nmsgs [color="#0000cc"]* [color="#0000ff"]sizeof[color="#0000cc"]([color="#0000ff"]struct i2c_msg[color="#0000cc"])[color="#0000cc"],
   GFP_KERNEL[color="#0000cc"])[color="#0000cc"];
  [color="#0000ff"]if [color="#0000cc"](rdwr_pa [color="#0000cc"]=[color="#0000cc"]= [color="#ff0000"]NULL[color="#0000cc"]) [color="#0000ff"]return [color="#0000cc"]-ENOMEM[color="#0000cc"];
  [color="#0000ff"]if [color="#0000cc"](copy_from_user[color="#0000cc"](rdwr_pa[color="#0000cc"], rdwr_arg[color="#0000cc"].msgs[color="#0000cc"],
       rdwr_arg[color="#0000cc"].nmsgs [color="#0000cc"]* [color="#0000ff"]sizeof[color="#0000cc"]([color="#0000ff"]struct i2c_msg[color="#0000cc"])[color="#0000cc"])[color="#0000cc"]) [color="#0000cc"]{   
   kfree[color="#0000cc"](rdwr_pa[color="#0000cc"])[color="#0000cc"];
   [color="#0000ff"]return [color="#0000cc"]-EFAULT[color="#0000cc"];
  [color="#0000cc"]}    // 第一个copy,已经拷贝arg->msgs指针到rdwr_arg的msgs指针,这里的COPY会把msgs指向的内容拷贝到rdwr_pa中,同时rdwr_pa->buf 指针指向用户空间msgs->buf 的指针
  data_ptrs [color="#0000cc"]= kmalloc[color="#0000cc"](rdwr_arg[color="#0000cc"].nmsgs [color="#0000cc"]* [color="#0000ff"]sizeof[color="#0000cc"](u8 __user [color="#0000cc"]*[color="#0000cc"])[color="#0000cc"], GFP_KERNEL[color="#0000cc"])[color="#0000cc"];
  [color="#0000ff"]if [color="#0000cc"](data_ptrs [color="#0000cc"]=[color="#0000cc"]= [color="#ff0000"]NULL[color="#0000cc"]) [color="#0000cc"]{
   kfree[color="#0000cc"](rdwr_pa[color="#0000cc"])[color="#0000cc"];
   [color="#0000ff"]return [color="#0000cc"]-ENOMEM[color="#0000cc"];
  [color="#0000cc"]}
  res [color="#0000cc"]= 0[color="#0000cc"];
  [color="#0000ff"]for[color="#0000cc"]( i[color="#0000cc"]=0[color="#0000cc"]; i[color="#0000cc"] 8192[color="#0000cc"]) [color="#0000cc"]{
    res [color="#0000cc"]= [color="#0000cc"]-EINVAL[color="#0000cc"];
    [color="#0000ff"]break[color="#0000cc"];
   [color="#0000cc"]}
   data_ptrs[color="#0000cc"][i[color="#0000cc"]] [color="#0000cc"]= [color="#0000cc"](u8 __user [color="#0000cc"]*[color="#0000cc"])rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf[color="#0000cc"];  
     // data_prts 指向 RDWR_pa.buf,即指向用户空间穿过来的buf中数据缓冲区

   rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf [color="#0000cc"]= kmalloc[color="#0000cc"](rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].len[color="#0000cc"], GFP_KERNEL[color="#0000cc"])[color="#0000cc"];//重新分配buf
   [color="#0000ff"]if[color="#0000cc"](rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf [color="#0000cc"]=[color="#0000cc"]= [color="#ff0000"]NULL[color="#0000cc"]) [color="#0000cc"]{
    res [color="#0000cc"]= [color="#0000cc"]-ENOMEM[color="#0000cc"];
    [color="#0000ff"]break[color="#0000cc"];
   [color="#0000cc"]}
   [color="#0000ff"]if[color="#0000cc"](copy_from_user[color="#0000cc"](rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf[color="#0000cc"],
    data_ptrs[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"],               // 把用户空间数据拷贝到 rdwr_pa.buf 中

    rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].len[color="#0000cc"])[color="#0000cc"]) [color="#0000cc"]{
     [color="#0000cc"]+[color="#0000cc"]+i[color="#0000cc"]; [color="#ff9900"]/* Needs to be kfreed too */
     res [color="#0000cc"]= [color="#0000cc"]-EFAULT[color="#0000cc"];
    [color="#0000ff"]break[color="#0000cc"];
   [color="#0000cc"]}
  [color="#0000cc"]}
  [color="#0000ff"]if [color="#0000cc"](res [color="#0000cc"]adapter[color="#0000cc"], // 最终会调用s3c24xx_i2c_xfer函数
   rdwr_pa[color="#0000cc"],
   rdwr_arg[color="#0000cc"].nmsgs[color="#0000cc"])[color="#0000cc"];    // 拷贝完毕,开始发送
  [color="#0000ff"]while[color="#0000cc"](i[color="#0000cc"]-[color="#0000cc"]- [color="#0000cc"]> 0[color="#0000cc"]) [color="#0000cc"]{
   [color="#0000ff"]if[color="#0000cc"]( res[color="#0000cc"]>[color="#0000cc"]=0 [color="#0000cc"]&[color="#0000cc"]& [color="#0000cc"](rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].flags [color="#0000cc"]& I2C_M_RD[color="#0000cc"])[color="#0000cc"]) [color="#0000cc"]{
    [color="#0000ff"]if[color="#0000cc"](copy_to_user[color="#0000cc"](
     data_ptrs[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"],
     rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf[color="#0000cc"],
     rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].len[color="#0000cc"])[color="#0000cc"]) [color="#0000cc"]{
     res [color="#0000cc"]= [color="#0000cc"]-EFAULT[color="#0000cc"];   // 如果为读,把数据拷贝到用户空间
    [color="#0000cc"]}
   [color="#0000cc"]}
   kfree[color="#0000cc"](rdwr_pa[color="#0000cc"][i[color="#0000cc"]][color="#0000cc"].buf[color="#0000cc"])[color="#0000cc"];
  [color="#0000cc"]}
  kfree[color="#0000cc"](data_ptrs[color="#0000cc"])[color="#0000cc"];
  kfree[color="#0000cc"](rdwr_pa[color="#0000cc"])[color="#0000cc"];
  [color="#0000ff"]return res[color="#0000cc"];

=============下面分析i2c_s3c2410.c的传输过程=========
启动s3c24xx_i2c_message_start函数开始传输,
在这个函数里面会首先发送从器件地址
addr = (msg->addr & 0x7f) 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
[color="#000000"]if [color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]-[color="#0000cc"]>flags [color="#0000cc"]& I2C_M_RD[color="#0000cc"])
   i2c[color="#0000cc"]-[color="#0000cc"]>state [color="#0000cc"]= STATE_READ[color="#0000cc"];
  [color="#0000ff"]else
   i2c[color="#0000cc"]-[color="#0000cc"]>state [color="#0000cc"]= STATE_WRITE[color="#0000cc"];   // 判断为读还是写
  
  [color="#0000ff"]if [color="#0000cc"](is_lastmsg[color="#0000cc"](i2c[color="#0000cc"]) [color="#0000cc"]&[color="#0000cc"]& i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]-[color="#0000cc"]>len [color="#0000cc"]=[color="#0000cc"]= 0[color="#0000cc"]) [color="#0000cc"]{ //没有消息,或者长度为0,停止总线
   s3c24xx_i2c_stop[color="#0000cc"](i2c[color="#0000cc"], 0[color="#0000cc"])[color="#0000cc"];
   [color="#0000ff"]goto out_ack[color="#0000cc"];
  [color="#0000cc"]}
  [color="#0000ff"]if [color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>state [color="#0000cc"]=[color="#0000cc"]= STATE_READ[color="#0000cc"])    // 若是读,直接跳到读命令分支
   [color="#0000ff"]goto prepare_read[color="#0000cc"];

                                  // 否则,进入写分支
[color="#0000ff"]case STATE_WRITE[color="#0000cc"]:
  retry_write[color="#0000cc"]:
  [color="#0000ff"]if [color="#0000cc"]([color="#0000cc"]!is_msgend[color="#0000cc"](i2c[color="#0000cc"])[color="#0000cc"]) [color="#0000cc"]{         // 判断是否单个消息中的发送内容msg->buf已经传输完毕

   byte [color="#0000cc"]= i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]-[color="#0000cc"]>buf[color="#0000cc"][i2c[color="#0000cc"]-[color="#0000cc"]>msg_ptr[color="#0000cc"]+[color="#0000cc"]+[color="#0000cc"]][color="#0000cc"];  // 发送下一个内容
   writeb[color="#0000cc"](byte[color="#0000cc"], i2c[color="#0000cc"]-[color="#0000cc"]>regs [color="#0000cc"]+ S3C2410_IICDS[color="#0000cc"])[color="#0000cc"];
     ndelay[color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>tx_setup[color="#0000cc"])[color="#0000cc"];
  [color="#0000cc"]}
                        
[color="#0000ff"]else [color="#0000ff"]if [color="#0000cc"]([color="#0000cc"]!is_lastmsg[color="#0000cc"](i2c[color="#0000cc"])[color="#0000cc"])  // 判断多个消息msgs是否已经结束 了

[color="#0000cc"]{
   [color="#ff9900"]/* we need to go to the next i2c message */
   dev_dbg[color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>dev[color="#0000cc"], [color="#ff00ff"]"WRITE: Next Message\n"[color="#0000cc"])[color="#0000cc"];
   i2c[color="#0000cc"]-[color="#0000cc"]>msg_ptr [color="#0000cc"]= 0[color="#0000cc"];
   i2c[color="#0000cc"]-[color="#0000cc"]>msg_idx [color="#0000cc"]+[color="#0000cc"]+[color="#0000cc"];  
   i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]+[color="#0000cc"]+[color="#0000cc"];                // 指向下一个消息
   
   [color="#ff9900"]/* check to see if we need to do another message */
       [color="#0000ff"]if [color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]-[color="#0000cc"]>flags [color="#0000cc"]& I2C_M_NOSTART[color="#0000cc"])
       [color="#0000cc"]{                           // 不需要重新启动,注释说不支持此模式
             [color="#0000ff"]if [color="#0000cc"](i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"]-[color="#0000cc"]>flags [color="#0000cc"]& I2C_M_RD[color="#0000cc"])
          [color="#0000cc"]{
            s3c24xx_i2c_stop[color="#0000cc"](i2c[color="#0000cc"], [color="#0000cc"]-EINVAL[color="#0000cc"])[color="#0000cc"];
          [color="#0000cc"]}
             [color="#0000ff"]goto retry_write[color="#0000cc"];
        [color="#0000cc"]}
      [color="#0000ff"]else
     [color="#0000cc"]{                                    
      s3c24xx_i2c_message_start[color="#0000cc"](i2c[color="#0000cc"], i2c[color="#0000cc"]-[color="#0000cc"]>msg[color="#0000cc"])[color="#0000cc"]; // 发送下条消息,重新启动,发送从机地址
      i2c[color="#0000cc"]-[color="#0000cc"]>state [color="#0000cc"]= STATE_START[color="#0000cc"];
     [color="#0000cc"]}
  [color="#0000cc"]}
  [color="#0000ff"]else
  [color="#0000cc"]{                             // 全部发送完成,结束
   [color="#ff9900"]/* send stop */
   s3c24xx_i2c_stop[color="#0000cc"](i2c[color="#0000cc"], 0[color="#0000cc"])[color="#0000cc"];
  [color="#0000cc"]}
  [color="#0000ff"]break[color="#0000cc"];

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); 返回,
整个传输完成.
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/66024/showart_1889158.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP