public inbox for linux-i2c@vger.kernel.org
 help / color / mirror / Atom feed
From: "Benoît Monin" <benoit.monin@bootlin.com>
To: linux-i2c@vger.kernel.org
Cc: "Jean Delvare" <jdelvare@suse.de>,
	"Wolfram Sang" <wsa+renesas@sang-engineering.com>,
	"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
	"Benoît Monin" <benoit.monin@bootlin.com>
Subject: [PATCH i2c-tools v3 2/2] i2ctransfer: Add optional message modifier flags
Date: Tue, 27 Jan 2026 10:25:58 +0100	[thread overview]
Message-ID: <20260127-msg-flags-v3-2-e7539945db2b@bootlin.com> (raw)
In-Reply-To: <20260127-msg-flags-v3-0-e7539945db2b@bootlin.com>

Allow setting protocol mangling and repeated start elision flags of an i2c
message with a set of optional command-line flags. These optional flags
are parsed at the beginning of the DESC field up to a read or write flag.

For example, to read one byte from address 0x50 followed by a stop, then
write two bytes at 0x54 on bus 0, one would call i2ctransfer as follow:

    i2ctransfer 0 pr1@0x50 w2@0x54 0x10 0x20

Since the new flags are optional, this patch preserves the compatibility
of the i2ctransfer syntax.

Handling of the message flags is done in add_flag_if_supported(). This
function checks if the flag is defined at compile time and if the adapter
supports the required functionality to handle the flag.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
---
 tools/i2ctransfer.8 |  28 ++++++++++++-
 tools/i2ctransfer.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 133 insertions(+), 14 deletions(-)

diff --git a/tools/i2ctransfer.8 b/tools/i2ctransfer.8
index 4bdf436..ca41241 100644
--- a/tools/i2ctransfer.8
+++ b/tools/i2ctransfer.8
@@ -96,8 +96,11 @@ The number of blocks is limited by the Linux Kernel and defined by I2C_RDWR_IOCT
 .I desc
 blocks are composed like this:
 
-.I {r|w}<length_of_message>[@address]
+.I [inpst]{r|w}<length_of_message>[@address]
 
+.TP
+.B [inpst]
+If supported, specifies optional MESSAGE MODIFIER FLAGS. See the section below for details.
 .TP
 .B {r|w}
 specifies if the message is read or write
@@ -141,6 +144,29 @@ decrease value by 1 until end of message (i.e. 0xff- means 0xff, 0xfe, 0xfd, ...
 p
 use value as seed for an 8 bit pseudo random sequence (i.e. 0p means 0x00, 0x50, 0xb0, ...)
 
+.SH "MESSAGE MODIFIER FLAGS"
+.PP
+These optional flags can be used to change the handling of a message when it is sent on the bus.
+They allow you to deviate from the I2C standard to work around device issues.
+They should be used with care, as they can confuse your I2C bus.
+Note that not all flags (or any) may be supported by your particular hardware and your kernel version.
+
+.TP
+.B i
+ignore NACK from client, treat them as ACK.
+.TP
+.B n
+in a read message, master ACK/NACK bit is skipped.
+.TP
+.B p
+emit a STOP after the message.
+.TP
+.B s
+skip repeated start.
+.TP
+.B t
+toggle read/write bit.
+
 .SH EXAMPLES
 .PP
 On bus 0, from an EEPROM at address 0x50, read 8 byte from offset 0x64
diff --git a/tools/i2ctransfer.c b/tools/i2ctransfer.c
index 4db98e3..446a174 100644
--- a/tools/i2ctransfer.c
+++ b/tools/i2ctransfer.c
@@ -52,9 +52,16 @@ static void help(void)
 		"           -V version info\n"
 		"           -y yes to all confirmations\n"
 		"  I2CBUS is an integer or an I2C bus name\n"
-		"  DESC describes the transfer in the form: {r|w}LENGTH[@address]\n"
-		"    1) read/write-flag 2) LENGTH (range 0-65535, or '?')\n"
-		"    3) I2C address (use last one if omitted)\n"
+		"  DESC describes the transfer in the form: [inpst]{r|w}LENGTH[@address]\n"
+		"    1) optional message modifier flags, if supported\n"
+		"       i: ignore NACK from client\n"
+		"       n: no master ACK/NACK bit in a read message\n"
+		"       p: emit a STOP after the message\n"
+		"       s: skip repeated start\n"
+		"       t: toggle read/write bit\n"
+		"    2) mandatory read/write flag\n"
+		"    3) LENGTH (range 0-65535, or '?')\n"
+		"    4) I2C address (use last one if omitted)\n"
 		"  DATA are LENGTH bytes for a write message. They can be shortened by a suffix:\n"
 		"    = (keep value constant until LENGTH)\n"
 		"    + (increase value by 1 until LENGTH)\n"
@@ -66,17 +73,20 @@ static void help(void)
 		"  # i2ctransfer 0 w17@0x50 0x42 0xff-\n");
 }
 
-static int check_funcs(int file)
+static int get_funcs(int file, unsigned long *funcs)
 {
-	unsigned long funcs;
-
 	/* check adapter functionality */
-	if (ioctl(file, I2C_FUNCS, &funcs) < 0) {
+	if (ioctl(file, I2C_FUNCS, funcs) < 0) {
 		fprintf(stderr, "Error: Could not get the adapter "
 			"functionality matrix: %s\n", strerror(errno));
 		return -1;
 	}
 
+	return 0;
+}
+
+static int check_funcs(unsigned long funcs)
+{
 	if (!(funcs & I2C_FUNC_I2C)) {
 		fprintf(stderr, MISSING_FUNC_FMT, "I2C transfers");
 		return -1;
@@ -140,6 +150,85 @@ static int confirm(const char *filename, struct i2c_msg *msgs, __u32 nmsgs)
 	return 1;
 }
 
+/*
+ * Parse one message flag and check if the adapter supports it.
+ * Return:
+ * 1 if we have the direction of the message (read or write)
+ * 0 if the argument is a valid and supported optional flag
+ * -1 for unsupported flag
+ */
+static int add_flag_if_supported(__u16 *flags, unsigned long funcs, char arg)
+{
+	int mangling = 0, nostart = 0;
+
+	/* Get the message flag from the argument */
+	switch (arg) {
+		/* mandatory direction flag: ends the parsing of flags */
+		case 'r':
+			*flags |= I2C_M_RD;
+			return 1;
+		case 'w':
+			return 1;
+
+		/* optional flags */
+#ifdef I2C_M_IGNORE_NAK
+		case 'i':
+			*flags |= I2C_M_IGNORE_NAK;
+			mangling = 1;
+			break;
+#endif
+#ifdef I2C_M_NO_RD_ACK
+		case 'n':
+			*flags |= I2C_M_NO_RD_ACK;
+			mangling = 1;
+			break;
+#endif
+#ifdef I2C_M_STOP
+		case 'p':
+			*flags |= I2C_M_STOP;
+			mangling = 1;
+			break;
+#endif
+#ifdef I2C_M_NOSTART
+		case 's':
+			*flags |= I2C_M_NOSTART;
+			nostart = 1;
+			break;
+#endif
+#ifdef I2C_M_REV_DIR_ADDR
+		case 't':
+			*flags |= I2C_M_REV_DIR_ADDR;
+			mangling = 1;
+			break;
+#endif
+
+		default:
+			fprintf(stderr, "Error: Unsupported flag '%c'\n", arg);
+			return -1;
+	}
+
+	/* Check that the adapter supports the requested flags */
+#ifdef I2C_FUNC_PROTOCOL_MANGLING
+	if (funcs & I2C_FUNC_PROTOCOL_MANGLING)
+		mangling = 0;
+#endif
+	if (mangling) {
+		fprintf(stderr, MISSING_FUNC_FMT, "protocol mangling");
+		return -1;
+	}
+
+#ifdef I2C_FUNC_NOSTART
+	if (funcs & I2C_FUNC_NOSTART)
+		nostart = 0;
+#endif
+	if (nostart) {
+		fprintf(stderr, MISSING_FUNC_FMT, "repeated start skipping");
+		return -1;
+	}
+
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	char filename[20];
@@ -148,6 +237,7 @@ int main(int argc, char *argv[])
 	struct i2c_msg msgs[I2C_RDRW_IOCTL_MAX_MSGS];
 	enum parse_state state = PARSE_GET_DESC;
 	unsigned int buf_idx = 0;
+	unsigned long funcs;
 
 	memset(msgs, 0, sizeof(msgs));
 
@@ -182,7 +272,7 @@ int main(int argc, char *argv[])
 		exit(1);
 
 	file = open_i2c_dev(i2cbus, filename, sizeof(filename), 0);
-	if (file < 0 || check_funcs(file))
+	if (file < 0 || get_funcs(file, &funcs) || check_funcs(funcs))
 		exit(1);
 
 	while (optind < argc) {
@@ -191,6 +281,7 @@ int main(int argc, char *argv[])
 		__u16 flags;
 		__u8 data, *buf;
 		char *end;
+		int ret;
 
 		if (nmsgs == I2C_RDRW_IOCTL_MAX_MSGS) {
 			fprintf(stderr, "Error: Too many messages (max: %d)\n",
@@ -202,11 +293,13 @@ int main(int argc, char *argv[])
 		case PARSE_GET_DESC:
 			flags = 0;
 
-			switch (*arg_ptr++) {
-			case 'r': flags |= I2C_M_RD; break;
-			case 'w': break;
-			default:
-				fprintf(stderr, "Error: Invalid direction\n");
+			for (ret = 0; *arg_ptr && !ret; arg_ptr++) {
+				ret = add_flag_if_supported(&flags, funcs, *arg_ptr);
+				if (ret < 0)
+					goto err_out_with_arg;
+			}
+			if (ret != 1) {
+				fprintf(stderr, "Error: Missing direction flag 'r' or 'w'\n");
 				goto err_out_with_arg;
 			}
 

-- 
2.52.0


  parent reply	other threads:[~2026-01-27  9:26 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-27  9:25 [PATCH i2c-tools v3 0/2] Add support for message modifier flags Benoît Monin
2026-01-27  9:25 ` [PATCH i2c-tools v3 1/2] i2cdetect: Display mangling and nostart support Benoît Monin
2026-01-27  9:25 ` Benoît Monin [this message]
2026-02-17 12:13 ` [PATCH i2c-tools v3 0/2] Add support for message modifier flags Jean Delvare
2026-03-23  9:12   ` Benoît Monin

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=20260127-msg-flags-v3-2-e7539945db2b@bootlin.com \
    --to=benoit.monin@bootlin.com \
    --cc=jdelvare@suse.de \
    --cc=linux-i2c@vger.kernel.org \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=wsa+renesas@sang-engineering.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox