[i2c-tools] i2cget: Add support for i2c block data
diff mbox

Message ID 044b3af6a47dfa92e047f0ff57e39a5b61e00058.1463165295.git.leonard.crestez@intel.com
State New
Headers show

Commit Message

Crestez Dan Leonard May 13, 2016, 6:54 p.m. UTC
This adds mode 'i' for I2C_SMBUS_I2C_BLOCK_DATA. This is the same mode
letter from i2cdump.

Length is optional and defaults to 32 (maximum).

The indended use is debugging i2c devices with shell commands.

Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
---
I'm not sure this is where patches for i2c-tools should be sent. This patch is
on top of latest master from https://github.com/groeck/i2c-tools

The README claims the latest version can be downloaded from www.lm-sensors.org
but that seems to have been down for a while.

 tools/i2cget.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 51 insertions(+), 10 deletions(-)

Comments

Guenter Roeck May 14, 2016, 12:38 a.m. UTC | #1
On 05/13/2016 11:54 AM, Crestez Dan Leonard wrote:
> This adds mode 'i' for I2C_SMBUS_I2C_BLOCK_DATA. This is the same mode
> letter from i2cdump.
>
> Length is optional and defaults to 32 (maximum).
>
> The indended use is debugging i2c devices with shell commands.
>
How does this differ from the 'i' option of i2cdump ?

Guenter

> Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
> ---
> I'm not sure this is where patches for i2c-tools should be sent. This patch is
> on top of latest master from https://github.com/groeck/i2c-tools
>
> The README claims the latest version can be downloaded from www.lm-sensors.org
> but that seems to have been down for a while.
>
>   tools/i2cget.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++----------
>   1 file changed, 51 insertions(+), 10 deletions(-)
>
> diff --git a/tools/i2cget.c b/tools/i2cget.c
> index 2503942..5d8dfe7 100644
> --- a/tools/i2cget.c
> +++ b/tools/i2cget.c
> @@ -41,14 +41,16 @@ static void help(void) __attribute__ ((noreturn));
>   static void help(void)
>   {
>   	fprintf(stderr,
> -		"Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]\n"
> +		"Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE] [LENGTH]]\n"
>   		"  I2CBUS is an integer or an I2C bus name\n"
>   		"  ADDRESS is an integer (0x03 - 0x77)\n"
>   		"  MODE is one of:\n"
>   		"    b (read byte data, default)\n"
>   		"    w (read word data)\n"
>   		"    c (write byte/read byte)\n"
> -		"    Append p for SMBus PEC\n");
> +		"    i (read I2C block data)\n"
> +		"    Append p for SMBus PEC\n"
> +		"  LENGTH is length for block data reads\n");
>   	exit(1);
>   }
>
> @@ -89,6 +91,13 @@ static int check_funcs(int file, int size, int daddress, int pec)
>   			return -1;
>   		}
>   		break;
> +
> +	case I2C_SMBUS_I2C_BLOCK_DATA:
> +		if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
> +			fprintf(stderr, MISSING_FUNC_FMT, "SMBus read I2C block data");
> +			return -1;
> +		}
> +		break;
>   	}
>
>   	if (pec
> @@ -101,7 +110,7 @@ static int check_funcs(int file, int size, int daddress, int pec)
>   }
>
>   static int confirm(const char *filename, int address, int size, int daddress,
> -		   int pec)
> +		   int length, int pec)
>   {
>   	int dont = 0;
>
> @@ -132,11 +141,14 @@ static int confirm(const char *filename, int address, int size, int daddress,
>   		fprintf(stderr, "current data\naddress");
>   	else
>   		fprintf(stderr, "data address\n0x%02x", daddress);
> -	fprintf(stderr, ", using %s.\n",
> -		size == I2C_SMBUS_BYTE ? (daddress < 0 ?
> -		"read byte" : "write byte/read byte") :
> -		size == I2C_SMBUS_BYTE_DATA ? "read byte data" :
> -		"read word data");
> +        if (size == I2C_SMBUS_I2C_BLOCK_DATA)
> +            fprintf(stderr, ", %d bytes using read I2C block data.\n", bytes);
> +        else
> +            fprintf(stderr, ", using %s.\n",
> +                    size == I2C_SMBUS_BYTE ? (daddress < 0 ?
> +                    "read byte" : "write byte/read byte") :
> +                    size == I2C_SMBUS_BYTE_DATA ? "read byte data" :
> +                    "read word data");
>   	if (pec)
>   		fprintf(stderr, "PEC checking enabled.\n");
>
> @@ -159,6 +171,8 @@ int main(int argc, char *argv[])
>   	int pec = 0;
>   	int flags = 0;
>   	int force = 0, yes = 0, version = 0;
> +	int length;
> +	__u8 block_data[I2C_SMBUS_BLOCK_MAX];
>
>   	/* handle (optional) flags first */
>   	while (1+flags < argc && argv[1+flags][0] == '-') {
> @@ -208,6 +222,7 @@ int main(int argc, char *argv[])
>   		case 'b': size = I2C_SMBUS_BYTE_DATA; break;
>   		case 'w': size = I2C_SMBUS_WORD_DATA; break;
>   		case 'c': size = I2C_SMBUS_BYTE; break;
> +		case 'i': size = I2C_SMBUS_I2C_BLOCK_DATA; break;
>   		default:
>   			fprintf(stderr, "Error: Invalid mode!\n");
>   			help();
> @@ -215,13 +230,27 @@ int main(int argc, char *argv[])
>   		pec = argv[flags+4][1] == 'p';
>   	}
>
> +	if (argc > flags + 5) {
> +		if (size != I2C_SMBUS_I2C_BLOCK_DATA) {
> +			fprintf(stderr, "Error: Length only valid for I2C block data!\n");
> +			help();
> +		}
> +		length = strtol(argv[flags+5], &end, 0);
> +		if (*end || length < 1 || length > I2C_SMBUS_BLOCK_MAX) {
> +			fprintf(stderr, "Error: Length invalid!\n");
> +			help();
> +		}
> +	} else {
> +		length = I2C_SMBUS_BLOCK_MAX;
> +	}
> +
>   	file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
>   	if (file < 0
>   	 || check_funcs(file, size, daddress, pec)
>   	 || set_slave_addr(file, address, force))
>   		exit(1);
>
> -	if (!yes && !confirm(filename, address, size, daddress, pec))
> +	if (!yes && !confirm(filename, address, size, daddress, length, pec))
>   		exit(0);
>
>   	if (pec && ioctl(file, I2C_PEC, 1) < 0) {
> @@ -243,6 +272,9 @@ int main(int argc, char *argv[])
>   	case I2C_SMBUS_WORD_DATA:
>   		res = i2c_smbus_read_word_data(file, daddress);
>   		break;
> +	case I2C_SMBUS_I2C_BLOCK_DATA:
> +		res = i2c_smbus_read_i2c_block_data(file, daddress, length, block_data);
> +		break;
>   	default: /* I2C_SMBUS_BYTE_DATA */
>   		res = i2c_smbus_read_byte_data(file, daddress);
>   	}
> @@ -253,7 +285,16 @@ int main(int argc, char *argv[])
>   		exit(2);
>   	}
>
> -	printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
> +	if (size == I2C_SMBUS_I2C_BLOCK_DATA) {
> +		int i;
> +                printf("%d:", res);
> +		for (i = 0; i < res; ++i) {
> +			printf(" 0x%02hhx", block_data[i]);
> +		}
> +		printf("\n");
> +	} else {
> +		printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
> +	}
>
>   	exit(0);
>   }
>

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Leonard Crestez May 14, 2016, 11:30 a.m. UTC | #2
On 05/14/2016 03:38 AM, Guenter Roeck wrote:
> On 05/13/2016 11:54 AM, Crestez Dan Leonard wrote:
>> This adds mode 'i' for I2C_SMBUS_I2C_BLOCK_DATA. This is the same mode
>> letter from i2cdump.
>>
>> Length is optional and defaults to 32 (maximum).
>>
>> The indended use is debugging i2c devices with shell commands.
>>
> How does this differ from the 'i' option of i2cdump ?

Apparently i2cdump doesn't support a range in "i" mode. I considered 
adding a range to i2cdump in all modes but:
- i2cdump code is a more complicated
- Not all devices interpret i2c bulk read as a register range. Reading X 
bytes from register Y can be different from reading registers from X to X+Y.
Guenter Roeck May 14, 2016, 3:10 p.m. UTC | #3
On 05/14/2016 04:30 AM, Crestez Dan Leonard wrote:
> On 05/14/2016 03:38 AM, Guenter Roeck wrote:
>> On 05/13/2016 11:54 AM, Crestez Dan Leonard wrote:
>>> This adds mode 'i' for I2C_SMBUS_I2C_BLOCK_DATA. This is the same mode
>>> letter from i2cdump.
>>>
>>> Length is optional and defaults to 32 (maximum).
>>>
>>> The indended use is debugging i2c devices with shell commands.
>>>
>> How does this differ from the 'i' option of i2cdump ?
>
> Apparently i2cdump doesn't support a range in "i" mode. I considered adding a range to i2cdump in all modes but:
> - i2cdump code is a more complicated

Maybe, but it already supports the command.

> - Not all devices interpret i2c bulk read as a register range. Reading X bytes from register Y can be different from reading registers from X to X+Y.
>
Not sure I understand what that has to do with supporting i2c block data.
Both commands call i2c_smbus_read_i2c_block_data(). Please explain.

Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Crestez Dan Leonard May 16, 2016, 11:39 a.m. UTC | #4
On 05/14/2016 06:10 PM, Guenter Roeck wrote:
> On 05/14/2016 04:30 AM, Crestez Dan Leonard wrote:
>> On 05/14/2016 03:38 AM, Guenter Roeck wrote:
>>> On 05/13/2016 11:54 AM, Crestez Dan Leonard wrote:
>>>> This adds mode 'i' for I2C_SMBUS_I2C_BLOCK_DATA. This is the same mode
>>>> letter from i2cdump.
>>>>
>>>> Length is optional and defaults to 32 (maximum).
>>>>
>>>> The indended use is debugging i2c devices with shell commands.
>>>>
>>> How does this differ from the 'i' option of i2cdump ?
>>
>> Apparently i2cdump doesn't support a range in "i" mode. I considered
>> adding a range to i2cdump in all modes but:
>> - i2cdump code is a more complicated
> 
> Maybe, but it already supports the command.
> 
>> - Not all devices interpret i2c bulk read as a register range. Reading
>> X bytes from register Y can be different from reading registers from X
>> to X+Y.
>>
> Not sure I understand what that has to do with supporting i2c block data.
> Both commands call i2c_smbus_read_i2c_block_data(). Please explain.

I just found it easier to implement this as an extension to i2cget
rather than add range support to i2cdump 'i' mode. I also think it's a
better fit for i2cget because the "length" parameter doesn't always
imply a register range.

As to how this is useful: I have an i2c sensor where new data can come
in while the driver is still reading and this will prevent further
interrupts. I can confirm this by issuing a command like:

    i2cget -f -y 0 0x18 0xa8 i 6

This bulk read of 6 bytes will unlock the driver for a short while.

This can't be done with current i2cdump's 'i' mode because that just
dumps all registers. i2cdump's byte/word modes issue multiple reads
which is not fast enough.

Patch
diff mbox

diff --git a/tools/i2cget.c b/tools/i2cget.c
index 2503942..5d8dfe7 100644
--- a/tools/i2cget.c
+++ b/tools/i2cget.c
@@ -41,14 +41,16 @@  static void help(void) __attribute__ ((noreturn));
 static void help(void)
 {
 	fprintf(stderr,
-		"Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]\n"
+		"Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE] [LENGTH]]\n"
 		"  I2CBUS is an integer or an I2C bus name\n"
 		"  ADDRESS is an integer (0x03 - 0x77)\n"
 		"  MODE is one of:\n"
 		"    b (read byte data, default)\n"
 		"    w (read word data)\n"
 		"    c (write byte/read byte)\n"
-		"    Append p for SMBus PEC\n");
+		"    i (read I2C block data)\n"
+		"    Append p for SMBus PEC\n"
+		"  LENGTH is length for block data reads\n");
 	exit(1);
 }
 
@@ -89,6 +91,13 @@  static int check_funcs(int file, int size, int daddress, int pec)
 			return -1;
 		}
 		break;
+
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+			fprintf(stderr, MISSING_FUNC_FMT, "SMBus read I2C block data");
+			return -1;
+		}
+		break;
 	}
 
 	if (pec
@@ -101,7 +110,7 @@  static int check_funcs(int file, int size, int daddress, int pec)
 }
 
 static int confirm(const char *filename, int address, int size, int daddress,
-		   int pec)
+		   int length, int pec)
 {
 	int dont = 0;
 
@@ -132,11 +141,14 @@  static int confirm(const char *filename, int address, int size, int daddress,
 		fprintf(stderr, "current data\naddress");
 	else
 		fprintf(stderr, "data address\n0x%02x", daddress);
-	fprintf(stderr, ", using %s.\n",
-		size == I2C_SMBUS_BYTE ? (daddress < 0 ?
-		"read byte" : "write byte/read byte") :
-		size == I2C_SMBUS_BYTE_DATA ? "read byte data" :
-		"read word data");
+        if (size == I2C_SMBUS_I2C_BLOCK_DATA)
+            fprintf(stderr, ", %d bytes using read I2C block data.\n", bytes);
+        else
+            fprintf(stderr, ", using %s.\n",
+                    size == I2C_SMBUS_BYTE ? (daddress < 0 ?
+                    "read byte" : "write byte/read byte") :
+                    size == I2C_SMBUS_BYTE_DATA ? "read byte data" :
+                    "read word data");
 	if (pec)
 		fprintf(stderr, "PEC checking enabled.\n");
 
@@ -159,6 +171,8 @@  int main(int argc, char *argv[])
 	int pec = 0;
 	int flags = 0;
 	int force = 0, yes = 0, version = 0;
+	int length;
+	__u8 block_data[I2C_SMBUS_BLOCK_MAX];
 
 	/* handle (optional) flags first */
 	while (1+flags < argc && argv[1+flags][0] == '-') {
@@ -208,6 +222,7 @@  int main(int argc, char *argv[])
 		case 'b': size = I2C_SMBUS_BYTE_DATA; break;
 		case 'w': size = I2C_SMBUS_WORD_DATA; break;
 		case 'c': size = I2C_SMBUS_BYTE; break;
+		case 'i': size = I2C_SMBUS_I2C_BLOCK_DATA; break;
 		default:
 			fprintf(stderr, "Error: Invalid mode!\n");
 			help();
@@ -215,13 +230,27 @@  int main(int argc, char *argv[])
 		pec = argv[flags+4][1] == 'p';
 	}
 
+	if (argc > flags + 5) {
+		if (size != I2C_SMBUS_I2C_BLOCK_DATA) {
+			fprintf(stderr, "Error: Length only valid for I2C block data!\n");
+			help();
+		}
+		length = strtol(argv[flags+5], &end, 0);
+		if (*end || length < 1 || length > I2C_SMBUS_BLOCK_MAX) {
+			fprintf(stderr, "Error: Length invalid!\n");
+			help();
+		}
+	} else {
+		length = I2C_SMBUS_BLOCK_MAX;
+	}
+
 	file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
 	if (file < 0
 	 || check_funcs(file, size, daddress, pec)
 	 || set_slave_addr(file, address, force))
 		exit(1);
 
-	if (!yes && !confirm(filename, address, size, daddress, pec))
+	if (!yes && !confirm(filename, address, size, daddress, length, pec))
 		exit(0);
 
 	if (pec && ioctl(file, I2C_PEC, 1) < 0) {
@@ -243,6 +272,9 @@  int main(int argc, char *argv[])
 	case I2C_SMBUS_WORD_DATA:
 		res = i2c_smbus_read_word_data(file, daddress);
 		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		res = i2c_smbus_read_i2c_block_data(file, daddress, length, block_data);
+		break;
 	default: /* I2C_SMBUS_BYTE_DATA */
 		res = i2c_smbus_read_byte_data(file, daddress);
 	}
@@ -253,7 +285,16 @@  int main(int argc, char *argv[])
 		exit(2);
 	}
 
-	printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
+	if (size == I2C_SMBUS_I2C_BLOCK_DATA) {
+		int i;
+                printf("%d:", res);
+		for (i = 0; i < res; ++i) {
+			printf(" 0x%02hhx", block_data[i]);
+		}
+		printf("\n");
+	} else {
+		printf("0x%0*x\n", size == I2C_SMBUS_WORD_DATA ? 4 : 2, res);
+	}
 
 	exit(0);
 }