和菜鸟一起学linux总线驱动之初识i2c驱动数据传输流程

来源:互联网 发布:网络老虎机赌博网站 编辑:程序博客网 时间:2024/05/29 04:01

 

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

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

原创粉丝点击