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的数据流程就是这样了。具体的很多细节都没有分析,可以通过细读代码来理解。

  • 相关阅读:
    Android的数据存储
    Servlet第一天
    JavaScript高级程序设计读书笔记(3)
    Interesting Papers on Face Recognition
    Researchers Study Ear Biometrics
    IIS 发生意外错误 0x8ffe2740
    Father of fractal geometry, Benoit Mandelbrot has passed away
    Computer vision scientist David Mumford wins National Medal of Science
    Pattern Recognition Review Papers
    盒模型bug的解决方法
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300055.html
Copyright © 2011-2022 走看看