zoukankan      html  css  js  c++  java
  • 和菜鸟一起学linux总线驱动之初识i2c驱动数据传输流程

           吃个晚饭,画个流程图,没想到已经这么晚了。还是速度把这篇文章搞定,收拾回去了。

    先看下linux中的i2c的数据流程图吧。这里主要是用gpio模拟的i2c的。

     

    还是具体看下代码吧,流程只是个大概,和i2c的总线协议差不多的。

     

    首先从数据调用来看吧。一般的都是通过

    i2c_transfer来来实现的,

     

    int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
    
    {
    
           unsigned long orig_jiffies;
    
           int ret, try;
    
     
    
           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
    
     
    
                  if (in_atomic() || irqs_disabled()) {
    
                         ret = i2c_trylock_adapter(adap);
    
                         if (!ret)
    
                                /* I2C activity is ongoing. */
    
                                return -EAGAIN;
    
                  } else {
    
                         i2c_lock_adapter(adap);
    
                  }
    
     
    
                  /* 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);
    
                         if (ret != -EAGAIN)
    
                                break;
    
                         if (time_after(jiffies, orig_jiffies + adap->timeout))
    
                                break;
    
                  }
    
                  i2c_unlock_adapter(adap);
    
     
    
                  return ret;
    
           } else {
    
                  dev_dbg(&adap->dev, "I2C level transfers not supported\n");
    
                  return -EOPNOTSUPP;
    
           }
    
    }
    
    


     

    adap->algo->master_xfer,通过这个函数指针,这个函数指针赋值先看下面的代码

     

    drivers/i2c/busses/i2c-gpio.c

     

    static int __devinit i2c_gpio_probe(struct platform_device *pdev)
    
    {
    
           struct i2c_gpio_platform_data *pdata;
    
           struct i2c_algo_bit_data *bit_data;
    
           struct i2c_adapter *adap;
    
           int ret;
    
     
    
           pdata = pdev->dev.platform_data;
    
           if (!pdata)
    
                  return -ENXIO;
    
     
    
           ret = -ENOMEM;
    
           adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
    
           if (!adap)
    
                  goto err_alloc_adap;
    
           bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
    
           if (!bit_data)
    
                  goto err_alloc_bit_data;
    
     
    
           ret = gpio_request(pdata->sda_pin, "sda");
    
           if (ret)
    
                  goto err_request_sda;
    
           ret = gpio_request(pdata->scl_pin, "scl");
    
           if (ret)
    
                  goto err_request_scl;
    
     
    
           if (pdata->sda_is_open_drain) {
    
                  gpio_direction_output(pdata->sda_pin, 1);
    
                  bit_data->setsda = i2c_gpio_setsda_val;
    
           } else {
    
                  gpio_direction_input(pdata->sda_pin);
    
                  bit_data->setsda = i2c_gpio_setsda_dir;
    
           }
    
     
    
           if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
    
                  gpio_direction_output(pdata->scl_pin, 1);
    
                  bit_data->setscl = i2c_gpio_setscl_val;
    
           } else {
    
                  gpio_direction_input(pdata->scl_pin);
    
                  bit_data->setscl = i2c_gpio_setscl_dir;
    
           }
    
     
    
           if (!pdata->scl_is_output_only)
    
                  bit_data->getscl = i2c_gpio_getscl;
    
           bit_data->getsda = i2c_gpio_getsda;
    
     
    
           if (pdata->udelay)
    
                  bit_data->udelay = pdata->udelay;
    
           else if (pdata->scl_is_output_only)
    
                  bit_data->udelay = 50;                 /* 10 kHz */
    
           else
    
                  bit_data->udelay = 5;                   /* 100 kHz */
    
     
    
           if (pdata->timeout)
    
                  bit_data->timeout = pdata->timeout;
    
           else
    
                  bit_data->timeout = HZ / 10;        /* 100 ms */
    
     
    
           bit_data->data = pdata;
    
     
    
           adap->owner = THIS_MODULE;
    
           snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
    
           adap->algo_data = bit_data;
    
           adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
    
           adap->dev.parent = &pdev->dev;
    
     
    
           /*
    
            * If "dev->id" is negative we consider it as zero.
    
            * The reason to do so is to avoid sysfs names that only make
    
            * sense when there are multiple adapters.
    
            */
    
           adap->nr = (pdev->id != -1) ? pdev->id : 0;
    
           ret = i2c_bit_add_numbered_bus(adap);
    
           if (ret)
    
                  goto err_add_bus;
    
     
    
           platform_set_drvdata(pdev, adap);
    
     
    
           dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n",
    
                   pdata->sda_pin, pdata->scl_pin,
    
                   pdata->scl_is_output_only
    
                   ? ", no clock stretching" : "");
    
     
    
           return 0;
    
     
    
    err_add_bus:
    
           gpio_free(pdata->scl_pin);
    
    err_request_scl:
    
           gpio_free(pdata->sda_pin);
    
    err_request_sda:
    
           kfree(bit_data);
    
    err_alloc_bit_data:
    
           kfree(adap);
    
    err_alloc_adap:
    
           return ret;
    
    }
    
    


     

    看到了

    ret = i2c_bit_add_numbered_bus(adap);

    然后这个函数是在

    Drivers/i2c/algo/i2c-algo-bit.c

     

    int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
    
    {
    
           return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
    
    }
    
     
    
    


    然后

    int i2c_bit_add_bus(struct i2c_adapter *adap)
    
    {
    
           return __i2c_bit_add_bus(adap, i2c_add_adapter);
    
    }
    
    


     

    接着调用这个函数

    static int __i2c_bit_add_bus(struct i2c_adapter *adap,
    
                              int (*add_adapter)(struct i2c_adapter *))
    
    {
    
           struct i2c_algo_bit_data *bit_adap = adap->algo_data;
    
           int ret;
    
     
    
           if (bit_test) {
    
                  ret = test_bus(adap);
    
                  if (ret < 0)
    
                         return -ENODEV;
    
           }
    
     
    
           /* register new adapter to i2c module... */
    
           adap->algo = &i2c_bit_algo;
    
           adap->retries = 3;
    
     
    
           ret = add_adapter(adap);
    
           if (ret < 0)
    
                  return ret;
    
     
    
           /* Complain if SCL can't be read */
    
           if (bit_adap->getscl == NULL) {
    
                  dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
    
                  dev_warn(&adap->dev, "Bus may be unreliable\n");
    
           }
    
           return 0;
    
    }
    
    


     

    然后可以看到这两句

    adap->algo = &i2c_bit_algo;

    adap->retries = 3;

     

    算法指向了i2c_bit_algo,尝试3次。

     

    接着,我们回到刚才的调用adap->algo->master_xfer。然后就是

     

    static const struct i2c_algorithm i2c_bit_algo = {
    
           .master_xfer   = bit_xfer,
    
           .functionality  = bit_func,
    
    };
    
    


     

    这个函数指针调用的是bit_xfer函数

     

    static int bit_xfer(struct i2c_adapter *i2c_adap,
    
                      struct i2c_msg msgs[], int num)
    
    {
    
           struct i2c_msg *pmsg;
    
           struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
           int i, ret;
    
           unsigned short nak_ok;
    
     
    
           if (adap->pre_xfer) {
    
                  ret = adap->pre_xfer(i2c_adap);
    
                  if (ret < 0)
    
                         return ret;
    
           }
    
     
    
           bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
    
           i2c_start(adap);
    
           for (i = 0; i < num; i++) {
    
                  pmsg = &msgs[i];
    
                  nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
    
                  if (!(pmsg->flags & I2C_M_NOSTART)) {
    
                         if (i) {
    
                                bit_dbg(3, &i2c_adap->dev, "emitting "
    
                                       "repeated start condition\n");
    
                                i2c_repstart(adap);
    
                         }
    
                         ret = bit_doAddress(i2c_adap, pmsg);
    
                         if ((ret != 0) && !nak_ok) {
    
                                bit_dbg(1, &i2c_adap->dev, "NAK from "
    
                                       "device addr 0x%02x msg #%d\n",
    
                                       msgs[i].addr, i);
    
                                goto bailout;
    
                         }
    
                  }
    
                  if (pmsg->flags & I2C_M_RD) {
    
                         /* read bytes into buffer*/
    
                         ret = readbytes(i2c_adap, pmsg);
    
                         if (ret >= 1)
    
                                bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
    
                                       ret, ret == 1 ? "" : "s");
    
                         if (ret < pmsg->len) {
    
                                if (ret >= 0)
    
                                       ret = -EREMOTEIO;
    
                                goto bailout;
    
                         }
    
                  } else {
    
                         /* write bytes from buffer */
    
                         ret = sendbytes(i2c_adap, pmsg);
    
                         if (ret >= 1)
    
                                bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
    
                                       ret, ret == 1 ? "" : "s");
    
                         if (ret < pmsg->len) {
    
                                if (ret >= 0)
    
                                       ret = -EREMOTEIO;
    
                                goto bailout;
    
                         }
    
                  }
    
           }
    
           ret = i;
    
     
    
    bailout:
    
           bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
    
           i2c_stop(adap);
    
     
    
           if (adap->post_xfer)
    
                  adap->post_xfer(i2c_adap);
    
           return ret;
    
    }
    
    


     

    这里就是算法的所有过程了。根据协议来,先发个i2c_start(adap);

    /* --- other auxiliary functions --------------------------------------      */
    
    static void i2c_start(struct i2c_algo_bit_data *adap)
    
    {
    
           /* assert: scl, sda are high */
    
           setsda(adap, 0);
    
           udelay(adap->udelay);
    
           scllo(adap);
    
    }
    
    


     

     

    然后再发送设备地址,ret = bit_doAddress(i2c_adap, pmsg);

    static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    
    {
    
           unsigned short flags = msg->flags;
    
           unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    
           struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
     
    
           unsigned char addr;
    
           int ret, retries;
    
     
    
           retries = nak_ok ? 0 : i2c_adap->retries;
    
     
    
           if (flags & I2C_M_TEN) {
    
                  /* a ten bit address */
    
                  addr = 0xf0 | ((msg->addr >> 7) & 0x06);
    
                  bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
    
                  /* try extended address code...*/
    
                  ret = try_address(i2c_adap, addr, retries);
    
                  if ((ret != 1) && !nak_ok)  {
    
                         dev_err(&i2c_adap->dev,
    
                                "died at extended address code\n");
    
                         return -EREMOTEIO;
    
                  }
    
                  /* the remaining 8 bit address */
    
                  ret = i2c_outb(i2c_adap, msg->addr & 0xff);
    
                  if ((ret != 1) && !nak_ok) {
    
                         /* the chip did not ack / xmission error occurred */
    
                         dev_err(&i2c_adap->dev, "died at 2nd address code\n");
    
                         return -EREMOTEIO;
    
                  }
    
                  if (flags & I2C_M_RD) {
    
                         bit_dbg(3, &i2c_adap->dev, "emitting repeated "
    
                                "start condition\n");
    
                         i2c_repstart(adap);
    
                         /* okay, now switch into reading mode */
    
                         addr |= 0x01;
    
                         ret = try_address(i2c_adap, addr, retries);
    
                         if ((ret != 1) && !nak_ok) {
    
                                dev_err(&i2c_adap->dev,
    
                                       "died at repeated address code\n");
    
                                return -EREMOTEIO;
    
                         }
    
                  }
    
           } else {          /* normal 7bit address    */
    
                  addr = msg->addr << 1;
    
                  if (flags & I2C_M_RD)
    
                         addr |= 1;
    
                  if (flags & I2C_M_REV_DIR_ADDR)
    
                         addr ^= 1;
    
                  ret = try_address(i2c_adap, addr, retries);
    
                  if ((ret != 1) && !nak_ok)
    
                         return -ENXIO;
    
           }
    
     
    
           return 0;
    
    }
    
    


     

    这里尝试3次,ret = try_address(i2c_adap, addr, retries);

    static int try_address(struct i2c_adapter *i2c_adap,
    
                         unsigned char addr, int retries)
    
    {
    
           struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
           int i, ret = 0;
    
     
    
           for (i = 0; i <= retries; i++) {
    
                  ret = i2c_outb(i2c_adap, addr);
    
                  if (ret == 1 || i == retries)
    
                         break;
    
                  bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
    
                  i2c_stop(adap);
    
                  udelay(adap->udelay);
    
                  yield();
    
                  bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
    
                  i2c_start(adap);
    
           }
    
           if (i && ret)
    
                  bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
    
                         "0x%02x: %s\n", i + 1,
    
                         addr & 1 ? "read from" : "write to", addr >> 1,
    
                         ret == 1 ? "success" : "failed, timeout?");
    
           return ret;
    
    }
    
    


     

    这个ret = i2c_outb(i2c_adap, addr);是发送一个字节的函数,其具体就是通过gpio的拉高拉低来实现的

     

    /* send a byte without start cond., look for arbitration,
    
       check ackn. from slave */
    
    /* returns:
    
     * 1 if the device acknowledged
    
     * 0 if the device did not ack
    
     * -ETIMEDOUT if an error occurred (while raising the scl line)
    
     */
    
    static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
    
    {
    
           int i;
    
           int sb;
    
           int ack;
    
           struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
     
    
           /* assert: scl is low */
    
           for (i = 7; i >= 0; i--) {
    
                  sb = (c >> i) & 1;
    
                  setsda(adap, sb);
    
                  udelay((adap->udelay + 1) / 2);
    
                  if (sclhi(adap) < 0) { /* timed out */
    
                         bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
    
                                "timeout at bit #%d\n", (int)c, i);
    
                         return -ETIMEDOUT;
    
                  }
    
                  /* FIXME do arbitration here:
    
                   * if (sb && !getsda(adap)) -> ouch! Get out of here.
    
                   *
    
                   * Report a unique code, so higher level code can retry
    
                   * the whole (combined) message and *NOT* issue STOP.
    
                   */
    
                  scllo(adap);
    
           }
    
           sdahi(adap);
    
           if (sclhi(adap) < 0) { /* timeout */
    
                  bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
    
                         "timeout at ack\n", (int)c);
    
                  return -ETIMEDOUT;
    
           }
    
     
    
           /* read ack: SDA should be pulled down by slave, or it may
    
            * NAK (usually to report problems with the data we wrote).
    
            */
    
           ack = !getsda(adap);    /* ack: sda is pulled low -> success */
    
           bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
    
                  ack ? "A" : "NA");
    
     
    
           scllo(adap);
    
           return ack;
    
           /* assert: scl is low (sda undef) */
    
    }
    
    
    


     

    然后根据if (pmsg->flags & I2C_M_RD)这个来判断是发送数据,还是接收数据。

    如果是接收数据,那么

     

    ret = readbytes(i2c_adap, pmsg);
    
    


     

    static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    
    {
    
           int inval;
    
           int rdcount = 0;      /* counts bytes read */
    
           unsigned char *temp = msg->buf;
    
           int count = msg->len;
    
           const unsigned flags = msg->flags;
    
     
    
           while (count > 0) {
    
                  inval = i2c_inb(i2c_adap);
    
                  if (inval >= 0) {
    
                         *temp = inval;
    
                         rdcount++;
    
                  } else {   /* read timed out */
    
                         break;
    
                  }
    
     
    
                  temp++;
    
                  count--;
    
     
    
                  /* Some SMBus transactions require that we receive the
    
                     transaction length as the first read byte. */
    
                  if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
    
                         if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
    
                                if (!(flags & I2C_M_NO_RD_ACK))
    
                                       acknak(i2c_adap, 0);
    
                                dev_err(&i2c_adap->dev, "readbytes: invalid "
    
                                       "block length (%d)\n", inval);
    
                                return -EREMOTEIO;
    
                         }
    
                         /* The original count value accounts for the extra
    
                            bytes, that is, either 1 for a regular transaction,
    
                            or 2 for a PEC transaction. */
    
                         count += inval;
    
                         msg->len += inval;
    
                  }
    
     
    
                  bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
    
                         inval,
    
                         (flags & I2C_M_NO_RD_ACK)
    
                                ? "(no ack/nak)"
    
                                : (count ? "A" : "NA"));
    
     
    
                  if (!(flags & I2C_M_NO_RD_ACK)) {
    
                         inval = acknak(i2c_adap, count);
    
                         if (inval < 0)
    
                                return inval;
    
                  }
    
           }
    
           return rdcount;
    
    }
    
    


     

    然后通过这个inval = i2c_inb(i2c_adap);

     

    static int i2c_inb(struct i2c_adapter *i2c_adap)
    
    {
    
           /* read byte via i2c port, without start/stop sequence   */
    
           /* acknowledge is sent in i2c_read.                     */
    
           int i;
    
           unsigned char indata = 0;
    
           struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
     
    
           /* assert: scl is low */
    
           sdahi(adap);
    
           for (i = 0; i < 8; i++) {
    
                  if (sclhi(adap) < 0) { /* timeout */
    
                         bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit "
    
                                "#%d\n", 7 - i);
    
                         return -ETIMEDOUT;
    
                  }
    
                  indata *= 2;
    
                  if (getsda(adap))
    
                         indata |= 0x01;
    
                  setscl(adap, 0);
    
                  udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
    
           }
    
           /* assert: scl is low */
    
           return indata;
    
    }
    
    
    


     

    这样就把数据接收到了。

     

    如果是发送数据的话,那么就是ret = sendbytes(i2c_adap, pmsg);

     

    static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    
    {
    
           const unsigned char *temp = msg->buf;
    
           int count = msg->len;
    
           unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    
           int retval;
    
           int wrcount = 0;
    
     
    
           while (count > 0) {
    
                  retval = i2c_outb(i2c_adap, *temp);
    
     
    
                  /* OK/ACK; or ignored NAK */
    
                  if ((retval > 0) || (nak_ok && (retval == 0))) {
    
                         count--;
    
                         temp++;
    
                         wrcount++;
    
     
    
                  /* A slave NAKing the master means the slave didn't like
    
                   * something about the data it saw.  For example, maybe
    
                   * the SMBus PEC was wrong.
    
                   */
    
                  } else if (retval == 0) {
    
                         dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n");
    
                         return -EIO;
    
     
    
                  /* Timeout; or (someday) lost arbitration
    
                   *
    
                   * FIXME Lost ARB implies retrying the transaction from
    
                   * the first message, after the "winning" master issues
    
                   * its STOP.  As a rule, upper layer code has no reason
    
                   * to know or care about this ... it is *NOT* an error.
    
                   */
    
                  } else {
    
                         dev_err(&i2c_adap->dev, "sendbytes: error %d\n",
    
                                       retval);
    
                         return retval;
    
                  }
    
           }
    
           return wrcount;
    
    }
    
    


     

    然后通过retval = i2c_outb(i2c_adap, *temp);

    发送出给从机。

     

    最后

    i2c_stop(adap);

     

    数据发送完了

     

    static void i2c_stop(struct i2c_algo_bit_data *adap)
    
    {
    
           /* assert: scl is low */
    
           sdalo(adap);
    
           sclhi(adap);
    
           setsda(adap, 1);
    
           udelay(adap->udelay);
    
    }
    
    


     

    好了,整个gpio模拟的i2c的数据流程就是这样了。具体的很多细节都没有分析,可以通过细读代码来理解。

  • 相关阅读:
    二叉树的最大距离
    MS CRM 2011 RibbonExport Utility下载以及实用说明
    MS CRM 2011中的解决方案——使用
    MS CRM 2011的自定义与开发(5)——关系编辑器
    MS CRM 2011 RC中的新特性(5)——定期约会
    MS CRM2011中的事件脚本——入门
    MS CRM 2011 汇总更新 3
    MS CRM 4中模拟PartyList字段的方法
    MS CRM 2011的自定义与开发(4)——属性编辑器
    MS CRM 2011中的解决方案Solution_简介
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300055.html
Copyright © 2011-2022 走看看