All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
To: Jean Delvare <jdelvare-l3A5Bk7waGM@public.gmane.org>
Cc: Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>,
	Randy Dunlap <rdunlap-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [RFC PATCH] i2c: stub: Add support for SMBus block commands
Date: Mon, 07 Jul 2014 06:32:02 -0700	[thread overview]
Message-ID: <53BAA152.9040002@roeck-us.net> (raw)
In-Reply-To: <20140707102711.1b630fe2-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>

On 07/07/2014 01:27 AM, Jean Delvare wrote:
> Hi Guenter,
>
> On Sun,  6 Jul 2014 20:55:12 -0700, Guenter Roeck wrote:
>> SMBus block commands are different to I2C block commands since
>> the returned data is not normally accessible with byte or word
>> commands on other command offsets. Add linked list of 'block'
>> commands to support those commands.
>>
>> Access mechanism is quite simple: Block commands must be written
>> before they can be read. The first write selects the block length.
>> Subsequent writes can be partial. Block read commands always return
>> the number of bytes selected with the first write.
>
> Very smart, I like it.
>
>> Signed-off-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
>> ---
>>   Documentation/i2c/i2c-stub |  7 +++-
>>   drivers/i2c/i2c-stub.c     | 96 ++++++++++++++++++++++++++++++++++++++++++++--
>>   2 files changed, 98 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub
>> index fa4b669..8a112cc 100644
>> --- a/Documentation/i2c/i2c-stub
>> +++ b/Documentation/i2c/i2c-stub
>> @@ -2,9 +2,9 @@ MODULE: i2c-stub
>>
>>   DESCRIPTION:
>>
>> -This module is a very simple fake I2C/SMBus driver.  It implements five
>> +This module is a very simple fake I2C/SMBus driver.  It implements six
>>   types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, (r/w)
>> -word data, and (r/w) I2C block data.
>> +word data, (r/w) I2C block data, and (r/w) SMBus block data.
>>
>>   You need to provide chip addresses as a module parameter when loading this
>>   driver, which will then only react to SMBus commands to these addresses.
>> @@ -19,6 +19,9 @@ A pointer register with auto-increment is implemented for all byte
>>   operations.  This allows for continuous byte reads like those supported by
>>   EEPROMs, among others.
>>
>> +SMBus block commands must be written to configure an SMBus command for
>> +SMBus block operations. The first SMBus block write selects the block length.
>
> I think you should add valuable parts of the patch description here:
> "Subsequent writes can be partial. Block read commands always return
> the number of bytes selected with the first write."
>
Ok, done.

>> +
>>   The typical use-case is like this:
>>   	1. load this module
>>   	2. use i2cset (from the i2c-tools project) to pre-load some data
>> diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c
>> index 77e4849..e08aa9d 100644
>> --- a/drivers/i2c/i2c-stub.c
>> +++ b/drivers/i2c/i2c-stub.c
>> @@ -27,11 +27,12 @@
>>   #include <linux/slab.h>
>>   #include <linux/errno.h>
>>   #include <linux/i2c.h>
>> +#include <linux/list.h>
>>
>>   #define MAX_CHIPS 10
>>   #define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
>>   		   I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
>> -		   I2C_FUNC_SMBUS_I2C_BLOCK)
>> +		   I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA)
>
> As discussed earlier, I don't think SMBus block support should be
> enabled by default, as it can confuse some device drivers. I think we
> want:
>
> #define STUB_FUNC_DEFAULT \
> 	(I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
> 	 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
> 	 I2C_FUNC_SMBUS_I2C_BLOCK)
>
> #define STUB_FUNC_ALL \
> 	(STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA)
>
> static unsigned long functionality = STUB_FUNC_DEFAULT;
>
> static u32 stub_func(struct i2c_adapter *adapter)
> {
> 	return STUB_FUNC_ALL & functionality;
> }
>
> Would that be OK with you? You'd simply need to load the driver with
> functionality=0xffffffff to get the SMBus block support.
>
Yes; it is what I actually had in an earlier version of the document,
except for the 0xffffffff part in my test script which is an excellent
idea.

>>
>>   static unsigned short chip_addr[MAX_CHIPS];
>>   module_param_array(chip_addr, ushort, NULL, S_IRUGO);
>> @@ -42,14 +43,44 @@ static unsigned long functionality = STUB_FUNC;
>>   module_param(functionality, ulong, S_IRUGO | S_IWUSR);
>>   MODULE_PARM_DESC(functionality, "Override functionality bitfield");
>>
>> +struct smbus_block_data {
>> +	struct list_head node;
>> +	u8 command;
>> +	u8 len;
>> +	u8 block[I2C_SMBUS_BLOCK_MAX];
>> +};
>> +
>>   struct stub_chip {
>>   	u8 pointer;
>>   	u16 words[256];		/* Byte operations use the LSB as per SMBus
>>   				   specification */
>> +	struct list_head smbus_blocks;
>>   };
>>
>>   static struct stub_chip *stub_chips;
>>
>> +static struct smbus_block_data *stub_find_block(struct device *dev,
>> +						struct stub_chip *chip,
>> +						u8 command, bool create)
>> +{
>> +	struct smbus_block_data *b, *rb = NULL;
>> +
>> +	list_for_each_entry(b, &chip->smbus_blocks, node) {
>> +		if (b->command == command) {
>> +			rb = b;
>> +			break;
>> +		}
>> +	}
>> +	if (rb == NULL && create) {
>> +		rb = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
>
> While this is exactly the same, sizeof(*rb) might be more intuitive and
> make static code analyzers happier too.
>
Makes sense.

>> +		if (rb == NULL)
>> +			return rb;
>> +		rb->command = command;
>> +		list_add(&rb->node, &chip->smbus_blocks);
>> +	}
>> +	return rb;
>> +}
>> +
>>   /* Return negative errno on error. */
>>   static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	char read_write, u8 command, int size, union i2c_smbus_data *data)
>> @@ -57,6 +88,7 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	s32 ret;
>>   	int i, len;
>>   	struct stub_chip *chip = NULL;
>> +	struct smbus_block_data *b;
>>
>>   	/* Search for the right chip */
>>   	for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
>> @@ -68,6 +100,8 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	if (!chip)
>>   		return -ENODEV;
>>
>> +	b = stub_find_block(&adap->dev, chip, command, false);
>
> I'm not too happy to see this executed with every command. That's one
> function call plus one list search, this isn't cheap. I would prefer if
> this was only executed for actual SMBus block transfers. I think this
> is possible, see below.
>
>> +
>>   	switch (size) {
>>
>>   	case I2C_SMBUS_QUICK:
>> @@ -93,13 +127,20 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>
>>   	case I2C_SMBUS_BYTE_DATA:
>>   		if (read_write == I2C_SMBUS_WRITE) {
>> +			if (b) {
>> +				ret = -EINVAL;
>> +				break;
>> +			}
>
> Is this really necessary? I very much doubt a device driver would do
> that in the first place. And if it did, would a real device actually
> fail?
>
No, it wouldn't fail unless the written length byte is invalid. I don't know
if drivers try to do it; it doesn't make much sense, so most likely not.

>>   			chip->words[command] &= 0xff00;
>>   			chip->words[command] |= data->byte;
>>   			dev_dbg(&adap->dev,
>>   				"smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n",
>>   				addr, data->byte, command);
>>   		} else {
>> -			data->byte = chip->words[command] & 0xff;
>> +			if (b)
>> +				data->byte = b->len;
>> +			else
>> +				data->byte = chip->words[command] & 0xff;
>
> You could avoid this conditional (and the same below in case
> I2C_SMBUS_WORD_DATA) by writing to chip->words at the same time you
> write to b->block. Block transfers being rare and reads occurring more
> frequently than writes, I think the performance benefit is clear.
>
Makes sense, I'll do that. Great idea.

Question is if I should cover attempts to write a byte or word into block data.
I don't think it is worth the effort, as drivers won't usually do that.
My take is that we could add it later if really needed.
What do you think ?

>>   			dev_dbg(&adap->dev,
>>   				"smbus byte data - addr 0x%02x, read  0x%02x at 0x%02x.\n",
>>   				addr, data->byte, command);
>> @@ -111,12 +152,19 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>
>>   	case I2C_SMBUS_WORD_DATA:
>>   		if (read_write == I2C_SMBUS_WRITE) {
>> +			if (b) {
>> +				ret = -EINVAL;
>> +				break;
>> +			}
>>   			chip->words[command] = data->word;
>>   			dev_dbg(&adap->dev,
>>   				"smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n",
>>   				addr, data->word, command);
>>   		} else {
>> -			data->word = chip->words[command];
>> +			if (b)
>> +				data->word = (b->block[0] << 8) | b->len;
>> +			else
>> +				data->word = chip->words[command];
>>   			dev_dbg(&adap->dev,
>>   				"smbus word data - addr 0x%02x, read  0x%04x at 0x%02x.\n",
>>   				addr, data->word, command);
>> @@ -148,6 +196,46 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   		ret = 0;
>>   		break;
>>
>> +	case I2C_SMBUS_BLOCK_DATA:
>> +		if (read_write == I2C_SMBUS_WRITE) {
>> +			len = data->block[0];
>> +			if (len == 0 || len > I2C_SMBUS_BLOCK_MAX ||
>> +			    (b && len > b->len)) {
>
> A useful debug message in the latter case might be good to have.
>
Ok.

>> +				ret = -EINVAL;
>> +				break;
>> +			}
>> +			if (b == NULL) {
>> +				b = stub_find_block(&adap->dev, chip, command,
>> +						    true);
>> +				if (b == NULL) {
>> +					ret = -ENOMEM;
>> +					break;
>> +				}
>> +				/* First write sets block length */
>> +				b->len = len;
>> +			}
>> +			for (i = 0; i < len; i++)
>> +				b->block[i] = data->block[i + 1];
>> +			dev_dbg(&adap->dev,
>> +				"smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n",
>> +				addr, len, command);
>> +		} else {
>> +			if (b == NULL) {
>> +				ret = -EINVAL;
>
> I would suggest -EOPNOTSUPP with a useful debug message.
>
Ok.

>> +				break;
>> +			}
>> +			len = b->len;
>> +			data->block[0] = len;
>> +			for (i = 0; i < len; i++)
>> +				data->block[i + 1] = b->block[i];
>> +			dev_dbg(&adap->dev,
>> +				"smbus block data - addr 0x%02x, read  %d bytes at 0x%02x.\n",
>> +				addr, len, command);
>> +		}
>> +
>> +		ret = 0;
>> +		break;
>> +
>>   	default:
>>   		dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
>>   		ret = -EOPNOTSUPP;
>> @@ -199,6 +287,8 @@ static int __init i2c_stub_init(void)
>>   		pr_err("i2c-stub: Out of memory\n");
>>   		return -ENOMEM;
>>   	}
>> +	for (i--; i >= 0; i--)
>> +		INIT_LIST_HEAD(&stub_chips[i].smbus_blocks);
>
> That works but I have to admit it confused me at first, because
> traditionally reverse iterations like that are for cleanups on failure
> paths. I think it might make sense to introduce an additional variable
> to store the actual number of instantiated chips, so that we can use
> the regular iteration direction (which I think modern memory
> controllers can anticipate and optimize.) This would also let us
> optimize the first test in stub_xfer.
>
> But well this can be left as a separate cleanup patch, I'll take care
> of that (unless you object.)
>
Ok with me. I'll leave it alone.

Thanks,
Guenter

>>
>>   	ret = i2c_add_adapter(&stub_adapter);
>>   	if (ret)
>
>

WARNING: multiple messages have this Message-ID (diff)
From: Guenter Roeck <linux@roeck-us.net>
To: Jean Delvare <jdelvare@suse.de>
Cc: Wolfram Sang <wsa@the-dreams.de>,
	Randy Dunlap <rdunlap@infradead.org>,
	linux-i2c@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] i2c: stub: Add support for SMBus block commands
Date: Mon, 07 Jul 2014 06:32:02 -0700	[thread overview]
Message-ID: <53BAA152.9040002@roeck-us.net> (raw)
In-Reply-To: <20140707102711.1b630fe2@endymion.delvare>

On 07/07/2014 01:27 AM, Jean Delvare wrote:
> Hi Guenter,
>
> On Sun,  6 Jul 2014 20:55:12 -0700, Guenter Roeck wrote:
>> SMBus block commands are different to I2C block commands since
>> the returned data is not normally accessible with byte or word
>> commands on other command offsets. Add linked list of 'block'
>> commands to support those commands.
>>
>> Access mechanism is quite simple: Block commands must be written
>> before they can be read. The first write selects the block length.
>> Subsequent writes can be partial. Block read commands always return
>> the number of bytes selected with the first write.
>
> Very smart, I like it.
>
>> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>>   Documentation/i2c/i2c-stub |  7 +++-
>>   drivers/i2c/i2c-stub.c     | 96 ++++++++++++++++++++++++++++++++++++++++++++--
>>   2 files changed, 98 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub
>> index fa4b669..8a112cc 100644
>> --- a/Documentation/i2c/i2c-stub
>> +++ b/Documentation/i2c/i2c-stub
>> @@ -2,9 +2,9 @@ MODULE: i2c-stub
>>
>>   DESCRIPTION:
>>
>> -This module is a very simple fake I2C/SMBus driver.  It implements five
>> +This module is a very simple fake I2C/SMBus driver.  It implements six
>>   types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, (r/w)
>> -word data, and (r/w) I2C block data.
>> +word data, (r/w) I2C block data, and (r/w) SMBus block data.
>>
>>   You need to provide chip addresses as a module parameter when loading this
>>   driver, which will then only react to SMBus commands to these addresses.
>> @@ -19,6 +19,9 @@ A pointer register with auto-increment is implemented for all byte
>>   operations.  This allows for continuous byte reads like those supported by
>>   EEPROMs, among others.
>>
>> +SMBus block commands must be written to configure an SMBus command for
>> +SMBus block operations. The first SMBus block write selects the block length.
>
> I think you should add valuable parts of the patch description here:
> "Subsequent writes can be partial. Block read commands always return
> the number of bytes selected with the first write."
>
Ok, done.

>> +
>>   The typical use-case is like this:
>>   	1. load this module
>>   	2. use i2cset (from the i2c-tools project) to pre-load some data
>> diff --git a/drivers/i2c/i2c-stub.c b/drivers/i2c/i2c-stub.c
>> index 77e4849..e08aa9d 100644
>> --- a/drivers/i2c/i2c-stub.c
>> +++ b/drivers/i2c/i2c-stub.c
>> @@ -27,11 +27,12 @@
>>   #include <linux/slab.h>
>>   #include <linux/errno.h>
>>   #include <linux/i2c.h>
>> +#include <linux/list.h>
>>
>>   #define MAX_CHIPS 10
>>   #define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
>>   		   I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
>> -		   I2C_FUNC_SMBUS_I2C_BLOCK)
>> +		   I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA)
>
> As discussed earlier, I don't think SMBus block support should be
> enabled by default, as it can confuse some device drivers. I think we
> want:
>
> #define STUB_FUNC_DEFAULT \
> 	(I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
> 	 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
> 	 I2C_FUNC_SMBUS_I2C_BLOCK)
>
> #define STUB_FUNC_ALL \
> 	(STUB_FUNC_DEFAULT | I2C_FUNC_SMBUS_BLOCK_DATA)
>
> static unsigned long functionality = STUB_FUNC_DEFAULT;
>
> static u32 stub_func(struct i2c_adapter *adapter)
> {
> 	return STUB_FUNC_ALL & functionality;
> }
>
> Would that be OK with you? You'd simply need to load the driver with
> functionality=0xffffffff to get the SMBus block support.
>
Yes; it is what I actually had in an earlier version of the document,
except for the 0xffffffff part in my test script which is an excellent
idea.

>>
>>   static unsigned short chip_addr[MAX_CHIPS];
>>   module_param_array(chip_addr, ushort, NULL, S_IRUGO);
>> @@ -42,14 +43,44 @@ static unsigned long functionality = STUB_FUNC;
>>   module_param(functionality, ulong, S_IRUGO | S_IWUSR);
>>   MODULE_PARM_DESC(functionality, "Override functionality bitfield");
>>
>> +struct smbus_block_data {
>> +	struct list_head node;
>> +	u8 command;
>> +	u8 len;
>> +	u8 block[I2C_SMBUS_BLOCK_MAX];
>> +};
>> +
>>   struct stub_chip {
>>   	u8 pointer;
>>   	u16 words[256];		/* Byte operations use the LSB as per SMBus
>>   				   specification */
>> +	struct list_head smbus_blocks;
>>   };
>>
>>   static struct stub_chip *stub_chips;
>>
>> +static struct smbus_block_data *stub_find_block(struct device *dev,
>> +						struct stub_chip *chip,
>> +						u8 command, bool create)
>> +{
>> +	struct smbus_block_data *b, *rb = NULL;
>> +
>> +	list_for_each_entry(b, &chip->smbus_blocks, node) {
>> +		if (b->command == command) {
>> +			rb = b;
>> +			break;
>> +		}
>> +	}
>> +	if (rb == NULL && create) {
>> +		rb = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL);
>
> While this is exactly the same, sizeof(*rb) might be more intuitive and
> make static code analyzers happier too.
>
Makes sense.

>> +		if (rb == NULL)
>> +			return rb;
>> +		rb->command = command;
>> +		list_add(&rb->node, &chip->smbus_blocks);
>> +	}
>> +	return rb;
>> +}
>> +
>>   /* Return negative errno on error. */
>>   static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	char read_write, u8 command, int size, union i2c_smbus_data *data)
>> @@ -57,6 +88,7 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	s32 ret;
>>   	int i, len;
>>   	struct stub_chip *chip = NULL;
>> +	struct smbus_block_data *b;
>>
>>   	/* Search for the right chip */
>>   	for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
>> @@ -68,6 +100,8 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   	if (!chip)
>>   		return -ENODEV;
>>
>> +	b = stub_find_block(&adap->dev, chip, command, false);
>
> I'm not too happy to see this executed with every command. That's one
> function call plus one list search, this isn't cheap. I would prefer if
> this was only executed for actual SMBus block transfers. I think this
> is possible, see below.
>
>> +
>>   	switch (size) {
>>
>>   	case I2C_SMBUS_QUICK:
>> @@ -93,13 +127,20 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>
>>   	case I2C_SMBUS_BYTE_DATA:
>>   		if (read_write == I2C_SMBUS_WRITE) {
>> +			if (b) {
>> +				ret = -EINVAL;
>> +				break;
>> +			}
>
> Is this really necessary? I very much doubt a device driver would do
> that in the first place. And if it did, would a real device actually
> fail?
>
No, it wouldn't fail unless the written length byte is invalid. I don't know
if drivers try to do it; it doesn't make much sense, so most likely not.

>>   			chip->words[command] &= 0xff00;
>>   			chip->words[command] |= data->byte;
>>   			dev_dbg(&adap->dev,
>>   				"smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n",
>>   				addr, data->byte, command);
>>   		} else {
>> -			data->byte = chip->words[command] & 0xff;
>> +			if (b)
>> +				data->byte = b->len;
>> +			else
>> +				data->byte = chip->words[command] & 0xff;
>
> You could avoid this conditional (and the same below in case
> I2C_SMBUS_WORD_DATA) by writing to chip->words at the same time you
> write to b->block. Block transfers being rare and reads occurring more
> frequently than writes, I think the performance benefit is clear.
>
Makes sense, I'll do that. Great idea.

Question is if I should cover attempts to write a byte or word into block data.
I don't think it is worth the effort, as drivers won't usually do that.
My take is that we could add it later if really needed.
What do you think ?

>>   			dev_dbg(&adap->dev,
>>   				"smbus byte data - addr 0x%02x, read  0x%02x at 0x%02x.\n",
>>   				addr, data->byte, command);
>> @@ -111,12 +152,19 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>
>>   	case I2C_SMBUS_WORD_DATA:
>>   		if (read_write == I2C_SMBUS_WRITE) {
>> +			if (b) {
>> +				ret = -EINVAL;
>> +				break;
>> +			}
>>   			chip->words[command] = data->word;
>>   			dev_dbg(&adap->dev,
>>   				"smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n",
>>   				addr, data->word, command);
>>   		} else {
>> -			data->word = chip->words[command];
>> +			if (b)
>> +				data->word = (b->block[0] << 8) | b->len;
>> +			else
>> +				data->word = chip->words[command];
>>   			dev_dbg(&adap->dev,
>>   				"smbus word data - addr 0x%02x, read  0x%04x at 0x%02x.\n",
>>   				addr, data->word, command);
>> @@ -148,6 +196,46 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
>>   		ret = 0;
>>   		break;
>>
>> +	case I2C_SMBUS_BLOCK_DATA:
>> +		if (read_write == I2C_SMBUS_WRITE) {
>> +			len = data->block[0];
>> +			if (len == 0 || len > I2C_SMBUS_BLOCK_MAX ||
>> +			    (b && len > b->len)) {
>
> A useful debug message in the latter case might be good to have.
>
Ok.

>> +				ret = -EINVAL;
>> +				break;
>> +			}
>> +			if (b == NULL) {
>> +				b = stub_find_block(&adap->dev, chip, command,
>> +						    true);
>> +				if (b == NULL) {
>> +					ret = -ENOMEM;
>> +					break;
>> +				}
>> +				/* First write sets block length */
>> +				b->len = len;
>> +			}
>> +			for (i = 0; i < len; i++)
>> +				b->block[i] = data->block[i + 1];
>> +			dev_dbg(&adap->dev,
>> +				"smbus block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n",
>> +				addr, len, command);
>> +		} else {
>> +			if (b == NULL) {
>> +				ret = -EINVAL;
>
> I would suggest -EOPNOTSUPP with a useful debug message.
>
Ok.

>> +				break;
>> +			}
>> +			len = b->len;
>> +			data->block[0] = len;
>> +			for (i = 0; i < len; i++)
>> +				data->block[i + 1] = b->block[i];
>> +			dev_dbg(&adap->dev,
>> +				"smbus block data - addr 0x%02x, read  %d bytes at 0x%02x.\n",
>> +				addr, len, command);
>> +		}
>> +
>> +		ret = 0;
>> +		break;
>> +
>>   	default:
>>   		dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
>>   		ret = -EOPNOTSUPP;
>> @@ -199,6 +287,8 @@ static int __init i2c_stub_init(void)
>>   		pr_err("i2c-stub: Out of memory\n");
>>   		return -ENOMEM;
>>   	}
>> +	for (i--; i >= 0; i--)
>> +		INIT_LIST_HEAD(&stub_chips[i].smbus_blocks);
>
> That works but I have to admit it confused me at first, because
> traditionally reverse iterations like that are for cleanups on failure
> paths. I think it might make sense to introduce an additional variable
> to store the actual number of instantiated chips, so that we can use
> the regular iteration direction (which I think modern memory
> controllers can anticipate and optimize.) This would also let us
> optimize the first test in stub_xfer.
>
> But well this can be left as a separate cleanup patch, I'll take care
> of that (unless you object.)
>
Ok with me. I'll leave it alone.

Thanks,
Guenter

>>
>>   	ret = i2c_add_adapter(&stub_adapter);
>>   	if (ret)
>
>


  parent reply	other threads:[~2014-07-07 13:32 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-07  3:55 [RFC PATCH] i2c: stub: Add support for SMBus block commands Guenter Roeck
2014-07-07  8:27 ` Jean Delvare
     [not found]   ` <20140707102711.1b630fe2-R0o5gVi9kd7kN2dkZ6Wm7A@public.gmane.org>
2014-07-07 13:32     ` Guenter Roeck [this message]
2014-07-07 13:32       ` Guenter Roeck
     [not found]       ` <53BAA152.9040002-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
2014-07-07 14:04         ` Jean Delvare
2014-07-07 14:04           ` Jean Delvare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53BAA152.9040002@roeck-us.net \
    --to=linux-0h96xk9xttrk1umjsbkqmq@public.gmane.org \
    --cc=jdelvare-l3A5Bk7waGM@public.gmane.org \
    --cc=linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=rdunlap-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org \
    --cc=wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.