[i2c] [RFC/PATCH] Add I2C_M_STOP flag

Jean Delvare khali at linux-fr.org
Tue May 22 20:45:41 CEST 2007


Hi Trent,

On Mon, 21 May 2007 17:45:32 -0700 (PDT), Trent Piepho wrote:
> A problem came up with a certain i2c client used in the DVB card.  A typical i2c
> transaction to read a register with most device looks like this:
> 
> S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P
> 
> That would read one byte from the register Comm.  It's the same as the smbus
> read byte data command.  The stv0297 doesn't want a command like this, it needs
> something slightly different:
> 
> S Addr Wr [A] Comm [A] P S Addr Rd [A] [Data] NA P
> 
> It doesn't want a repeated start, instead it wants a stop and then a start.
> 
> Now, one can generate a sequence like this already, with code like this:
> char comm = 0x42, data;
> struct i2c_msg msgs[2] = {
> 	{ .addr = 0x61, .buf = &comm, .len = 1, .flags = 0 },
> 	{ .addr = 0x61, .buf = &data, .len = 1, .flags = I2C_M_RD } };
> i2c_transfer(adap, msgs, 1);
> i2c_transfer(adap, msgs+1, 1);

Or, easier and more portable:

	i2c_smbus_write_byte(client, comm);
	i2c_smbus_read_byte(client, data);

> The problem with this is that it's now two transactions, and not one single
> atomic transaction.  It's possible for another message by another process
> (different driver, i2c-dev, etc.) to slip in between the write message and
> read message.

Two clients cannot be created at the same address on a given i2c
adapter, so you should be safe. The only exception is indeed i2c-dev,
because the address can be forcibly used even if already busy, and
because i2c-dev itself doesn't register its client (which I consider a
bug). But people using i2c-dev in force mode are supposed to know what
they are doing.

> To solve this, my patch introduces a new flag I2C_M_STOP which tells the
> i2c bus driver to send a stop after the flagged message.  This way one can
> send a single transaction with i2c_transfer() that contains multiple stop
> conditions.  None of the i2c bus drivers need to care about I2C_M_STOP, it
> is handled by the code in i2c_transfer(), which splits the message on STOP
> boundaries into multiple calls to master_xfer(), while the
> i2c_adapter->bus_lock mutex ensures the entire transaction is atomic.
> 
> This allows the follow code to send the proper command in one transaction:
> char comm = 0x42, data;
> struct i2c_msg msgs[2] = {
>         { .addr = 0x61, .buf = &comm, .len = 1, .flags = I2C_M_STOP },
>         { .addr = 0x61, .buf = &data, .len = 1, .flags = I2C_M_RD } };
> i2c_transfer(adap, msgs, 2);
> 
> 
> -----------------------------------------------------------------------------
> diff -r 56b4c3e8f350 drivers/i2c/i2c-core.c
> --- a/drivers/i2c/i2c-core.c	Sat May 19 05:00:32 2007 +0000
> +++ b/drivers/i2c/i2c-core.c	Sat May 19 17:35:19 2007 -0700
> @@ -862,6 +862,7 @@ int i2c_transfer(struct i2c_adapter * ad
>  	int ret;
> 
>  	if (adap->algo->master_xfer) {
> +		int n, total = 0;
>  #ifdef DEBUG
>  		for (ret = 0; ret < num; ret++) {
>  			dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
> @@ -872,10 +873,28 @@ int i2c_transfer(struct i2c_adapter * ad
>  #endif
> 
>  		mutex_lock_nested(&adap->bus_lock, adap->level);
> -		ret = adap->algo->master_xfer(adap,msgs,num);
> +		for (n=1; n<=num; n++) {
> +			/* xfer the message(s) if we want a stop or if
> +			   it's the last message. */
> +			if (n == num || (msgs[n-1].flags & I2C_M_STOP)) {
> +				ret = adap->algo->master_xfer(adap,msgs,n);
> +
> +				total += ret;
> +				if (ret < n) {
> +					/* Return error code, if any */
> +					if (ret < 0)
> +						total = ret;
> +					break;
> +				}
> +
> +				/* Process any remaining messages */
> +				msgs += n;
> +				num -= n;
> +				n = 0;
> +			}
> +		}
>  		mutex_unlock(&adap->bus_lock);
> -
> -		return ret;
> +		return total;
>  	} else {
>  		dev_dbg(&adap->dev, "I2C level transfers not supported\n");
>  		return -ENOSYS;
> diff -r 56b4c3e8f350 include/linux/i2c.h
> --- a/include/linux/i2c.h	Sat May 19 05:00:32 2007 +0000
> +++ b/include/linux/i2c.h	Sat May 19 17:35:03 2007 -0700
> @@ -448,6 +448,7 @@ struct i2c_msg {
>  #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 */
> +#define I2C_M_STOP		0x0200 /* send STOP condition after this msg */
>  	__u16 len;		/* msg length				*/
>  	__u8 *buf;		/* pointer to msg data			*/
>  };

I don't see how this is necessary. Once you have a client at a given
address, your driver has full control on it, so you can define and use
your own mutex to guarantee that device accesses are serialized. All
I2C-based hardware monitoring drivers do that for example.

-- 
Jean Delvare



More information about the i2c mailing list