summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
authorMatthew Blecker <matthewb@chromium.org>2022-09-15 13:44:35 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-09-16 05:56:16 +0000
commit92adcc327f606e3e2eb55b4b5869121f1f34f038 (patch)
tree8cea7161ab6e8cf6476865148fd61463c3200acd /extra
parent055da16f465af00cb6e15cf74a0336860af5dcf8 (diff)
downloadchrome-ec-92adcc327f606e3e2eb55b4b5869121f1f34f038.tar.gz
i2c-pseudo: Add documentation in RST and MD formats.
This supplements Documentation.txt, which is plain text as used by documentation in old Linux kernel versions, with Documentation.rst, which is reStructuredText as used in the latest versions of Linux. The plain text Documentation.txt remains and will be maintained as long as backport patches are maintained. Since Chromium OS source code browsing websites such as Gitiles and Code Search can render Markdown, but not reStructuredText, Documentation.md is included as well, generated using rst2md from nb2plots (https://github.com/matthew-brett/nb2plots) which uses Sphinx (https://www.sphinx-doc.org/). BRANCH=none BUG=b:129565355 TEST=viewed the markdown doc in Gitiles using Chrome Change-Id: I332ba9098a1379b2f56efbaab84ed5498ef43d34 Signed-off-by: Matthew Blecker <matthewb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3900487 Reviewed-by: Keith Haddow <haddowk@chromium.org> Commit-Queue: Keith Haddow <haddowk@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r--extra/i2c_pseudo/Documentation.md279
-rw-r--r--extra/i2c_pseudo/Documentation.rst306
-rw-r--r--extra/i2c_pseudo/README14
3 files changed, 594 insertions, 5 deletions
diff --git a/extra/i2c_pseudo/Documentation.md b/extra/i2c_pseudo/Documentation.md
new file mode 100644
index 0000000000..ebcef6a01e
--- /dev/null
+++ b/extra/i2c_pseudo/Documentation.md
@@ -0,0 +1,279 @@
+# i2c-pseudo driver
+
+Usually I2C adapters are implemented in a kernel driver. It is also possible to
+implement an adapter in userspace, through the /dev/i2c-pseudo-controller
+interface. Load module i2c-pseudo for this.
+
+Use cases for this module include:
+
+* Using local I2C device drivers, particularly i2c-dev, with I2C busses on
+ remote systems. For example, interacting with a Device Under Test (DUT)
+ connected to a Linux host through a debug interface, or interacting with a
+ remote host over a network.
+
+* Implementing I2C device driver tests that are impractical with the i2c-stub
+ module. For example, when simulating an I2C device where its driver might
+ issue a sequence of reads and writes without interruption, and the value at a
+ certain address must change during the sequence.
+
+This is not intended to replace kernel drivers for actual I2C busses on the
+local host machine.
+
+## Details
+
+Each time /dev/i2c-pseudo-controller is opened, and the correct initialization
+command is written to it (ADAPTER_START), a new I2C adapter is created. The
+adapter will live until its file descriptor is closed. Multiple pseudo adapters
+can co-exist simultaneously, controlled by the same or different userspace
+processes. When an I2C device driver sends an I2C message to a pseudo adapter,
+the message becomes readable from its file descriptor. If a reply is written
+before the adapter timeout expires, that reply will be sent back to the I2C
+device driver.
+
+Reads and writes are buffered inside i2c-pseudo such that userspace controllers
+may split them up into arbitrarily small chunks. Multiple commands, or portions
+of multiple commands, may be read or written together.
+
+Blocking I/O is the default. Non-blocking I/O is supported as well, enabled by
+O_NONBLOCK. Polling is supported, with or without non-blocking I/O. A special
+command (ADAPTER_SHUTDOWN) is available to unblock any pollers or blocked
+reads or writes, as a convenience for a multi-threaded or multi-process program
+that wants to exit.
+
+It is safe to access a single controller fd from multiple threads or processes
+concurrently, though it is up to the controller to ensure proper ordering, and
+to ensure that writes for different commands do not get interleaved. However,
+it is recommended (not required) that controller implementations have only one
+reader thread and one writer thread, which may or may not be the same thread.
+Avoiding multiple readers and multiple writers greatly simplifies controller
+implementation, and there is likely no performance benefit to be gained from
+concurrent reads or concurrent writes due to how i2c-pseudo serializes them
+internally. After all, on a real I2C bus only one I2C message can be active at
+a time.
+
+Commands are newline-terminated, both those read from the controller device, and
+those written to it.
+
+## Read Commands
+
+The commands that may be read from a pseudo controller device are:
+
+
+---
+
+Read Command
+
+: `I2C_ADAPTER_NUM <num>`
+
+Example
+
+: `"I2C_ADAPTER_NUM 5\\n"`
+
+Details
+
+
+---
+
+Read Command
+
+: `I2C_PSEUDO_ID <num>`
+
+Example
+
+: `"I2C_PSEUDO_ID 98\\n"`
+
+Details
+
+
+---
+
+Read Command
+
+: `I2C_BEGIN_XFER`
+
+Example
+
+: `"I2C_BEGIN_XFER\\n"`
+
+Details
+
+
+---
+
+Read Command
+
+: `I2C_XFER_REQ <xfer_id> <msg_id> <addr> <flags> <data_len> [<write_byte>[:...]]`
+
+Example
+
+: `"I2C_XFER_REQ 3 0 0x0070 0x0000 2 AB:9F\\n"`
+
+Example
+
+: `"I2C_XFER_REQ 3 1 0x0070 0x0001 4\\n"`
+
+Details
+
+
+---
+
+Read Command
+
+: `I2C_COMMIT_XFER`
+
+Example
+
+: `"I2C_COMMIT_XFER\\n"`
+
+Details
+
+## Write Commands
+
+The commands that may be written to a pseudo controller device are:
+
+Write Command
+
+: `SET_ADAPTER_NAME_SUFFIX <suffix>`
+
+Example
+
+: `"SET_ADAPTER_NAME_SUFFIX My Adapter\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `SET_ADAPTER_TIMEOUT_MS <ms>`
+
+Example
+
+: `"SET_ADAPTER_TIMEOUT_MS 2000\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `ADAPTER_START`
+
+Example
+
+: `"ADAPTER_START\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `GET_ADAPTER_NUM`
+
+Example
+
+: `"GET_ADAPTER_NUM\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `GET_PSEUDO_ID`
+
+Example
+
+: `"GET_PSEUDO_ID\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `I2C_XFER_REPLY <xfer_id> <msg_id> <addr> <flags> <errno> [<read_byte>[:...]]`
+
+Example
+
+: `"I2C_XFER_REPLY 3 0 0x0070 0x0000 0\\n"`
+
+Example
+
+: `"I2C_XFER_REPLY 3 1 0x0070 0x0001 0 0B:29:02:D9\\n"`
+
+Details
+
+
+---
+
+Write Command
+
+: `ADAPTER_SHUTDOWN`
+
+Example
+
+: `"ADAPTER_SHUTDOWN\\n"`
+
+Details
+
+## Example userspace controller code
+
+In C, a simple exchange between i2c-pseudo and userspace might look like the
+example below. Note that for brevity this lacks any error checking and
+handling, which a real pseudo controller implementation should have.
+
+```
+int fd;
+char buf[1<<12];
+
+fd = open("/dev/i2c-pseudo-controller", O_RDWR);
+/* Create the I2C adapter. */
+dprintf(fd, "ADAPTER_START\n");
+
+/*
+ * Pretend this I2C adapter number is 5, and the first I2C xfer sent to it was
+ * from this command (using its i2c-dev interface):
+ * $ i2cset -y 5 0x70 0xC2
+ *
+ * Then this read would place the following into *buf:
+ * "I2C_BEGIN_XFER\n"
+ * "I2C_XFER_REQ 0 0 0x0070 0x0000 1 C2\n"
+ * "I2C_COMMIT_XFER\n"
+ */
+read(fd, buf, sizeof(buf));
+
+/* This reply would allow the i2cset command above to exit successfully. */
+dprintf(fd, "I2C_XFER_REPLY 0 0 0x0070 0x0000 0\n");
+
+/*
+ * Now pretend the next I2C xfer sent to this adapter was from:
+ * $ i2cget -y 5 0x70 0xAB
+ *
+ * Then this read would place the following into *buf:
+ * "I2C_BEGIN_XFER\n"
+ * "I2C_XFER_REQ 1 0 0x0070 0x0000 1 AB\n"
+ * "I2C_XFER_REQ 1 1 0x0070 0x0001 1\n'"
+ * "I2C_COMMIT_XFER\n"
+ */
+read(fd, buf, sizeof(buf));
+
+/*
+ * These replies would allow the i2cget command above to print the following to
+ * stdout and exit successfully:
+ * 0x0b
+ *
+ * Note that it is also valid to write these together in one write().
+ */
+dprintf(fd, "I2C_XFER_REPLY 1 0 0x0070 0x0000 0\n");
+dprintf(fd, "I2C_XFER_REPLY 1 1 0x0070 0x0001 0 0B\n");
+
+/* Destroy the I2C adapter. */
+close(fd);
+```
diff --git a/extra/i2c_pseudo/Documentation.rst b/extra/i2c_pseudo/Documentation.rst
new file mode 100644
index 0000000000..2527eb5337
--- /dev/null
+++ b/extra/i2c_pseudo/Documentation.rst
@@ -0,0 +1,306 @@
+=================
+i2c-pseudo driver
+=================
+
+Usually I2C adapters are implemented in a kernel driver. It is also possible to
+implement an adapter in userspace, through the /dev/i2c-pseudo-controller
+interface. Load module i2c-pseudo for this.
+
+Use cases for this module include:
+
+- Using local I2C device drivers, particularly i2c-dev, with I2C busses on
+ remote systems. For example, interacting with a Device Under Test (DUT)
+ connected to a Linux host through a debug interface, or interacting with a
+ remote host over a network.
+
+- Implementing I2C device driver tests that are impractical with the i2c-stub
+ module. For example, when simulating an I2C device where its driver might
+ issue a sequence of reads and writes without interruption, and the value at a
+ certain address must change during the sequence.
+
+This is not intended to replace kernel drivers for actual I2C busses on the
+local host machine.
+
+
+Details
+=======
+
+Each time /dev/i2c-pseudo-controller is opened, and the correct initialization
+command is written to it (ADAPTER_START), a new I2C adapter is created. The
+adapter will live until its file descriptor is closed. Multiple pseudo adapters
+can co-exist simultaneously, controlled by the same or different userspace
+processes. When an I2C device driver sends an I2C message to a pseudo adapter,
+the message becomes readable from its file descriptor. If a reply is written
+before the adapter timeout expires, that reply will be sent back to the I2C
+device driver.
+
+Reads and writes are buffered inside i2c-pseudo such that userspace controllers
+may split them up into arbitrarily small chunks. Multiple commands, or portions
+of multiple commands, may be read or written together.
+
+Blocking I/O is the default. Non-blocking I/O is supported as well, enabled by
+O_NONBLOCK. Polling is supported, with or without non-blocking I/O. A special
+command (ADAPTER_SHUTDOWN) is available to unblock any pollers or blocked
+reads or writes, as a convenience for a multi-threaded or multi-process program
+that wants to exit.
+
+It is safe to access a single controller fd from multiple threads or processes
+concurrently, though it is up to the controller to ensure proper ordering, and
+to ensure that writes for different commands do not get interleaved. However,
+it is recommended (not required) that controller implementations have only one
+reader thread and one writer thread, which may or may not be the same thread.
+Avoiding multiple readers and multiple writers greatly simplifies controller
+implementation, and there is likely no performance benefit to be gained from
+concurrent reads or concurrent writes due to how i2c-pseudo serializes them
+internally. After all, on a real I2C bus only one I2C message can be active at
+a time.
+
+Commands are newline-terminated, both those read from the controller device, and
+those written to it.
+
+
+Read Commands
+=============
+
+The commands that may be read from a pseudo controller device are:
+
+----
+
+:Read Command: ``I2C_ADAPTER_NUM <num>``
+:Example: ``"I2C_ADAPTER_NUM 5\n"``
+:Details:
+ | This is read in response to the GET_ADAPTER_NUM command being written.
+ The number is the I2C adapter number in decimal. This can only occur after
+ ADAPTER_START, because before that the number is not known and cannot be
+ predicted reliably.
+
+----
+
+:Read Command: ``I2C_PSEUDO_ID <num>``
+:Example: ``"I2C_PSEUDO_ID 98\n"``
+:Details:
+ | This is read in response to the GET_PSEUDO_ID command being written.
+ The number is the pseudo ID in decimal.
+
+----
+
+:Read Command: ``I2C_BEGIN_XFER``
+:Example: ``"I2C_BEGIN_XFER\n"``
+:Details:
+ | This indicates the start of an I2C transaction request, in other words
+ the start of the I2C messages from a single invocation of the I2C adapter's
+ master_xfer() callback. This can only occur after ADAPTER_START.
+
+----
+
+:Read Command: ``I2C_XFER_REQ <xfer_id> <msg_id> <addr> <flags> <data_len> [<write_byte>[:...]]``
+:Example: ``"I2C_XFER_REQ 3 0 0x0070 0x0000 2 AB:9F\n"``
+:Example: ``"I2C_XFER_REQ 3 1 0x0070 0x0001 4\n"``
+:Details:
+ | This is a single I2C message that a device driver requested be sent on
+ the bus, in other words a single struct i2c_msg from master_xfer() msgs arg.
+ |
+ | The xfer_id is a number representing the whole I2C transaction, thus all
+ I2C_XFER_REQ between a I2C_BEGIN_XFER + I2C_COMMIT_XFER pair share an
+ xfer_id. The purpose is to ensure replies from the userspace controller are
+ always properly matched to the intended master_xfer() request. The first
+ transaction has xfer_id 0, and it increases by 1 with each transaction,
+ however it will eventually wrap back to 0 if enough transactions happen
+ during the lifetime of a pseudo adapter. It is guaranteed to have a large
+ enough maximum value such that there can never be multiple outstanding
+ transactions with the same ID, due to an internal limit in i2c-pseudo that
+ will block master_xfer() calls when the controller is falling behind in its
+ replies.
+ |
+ | The msg_id is a decimal number representing the index of the I2C message
+ within its transaction, in other words the index in master_xfer() \*msgs
+ array arg. This starts at 0 after each I2C_BEGIN_XFER. This is guaranteed
+ to not wrap.
+ |
+ | The addr is the hexadecimal I2C address for this I2C message. The address
+ is right-aligned without any read/write bit.
+ |
+ | The flags are the same bitmask flags used in struct i2c_msg, in hexadecimal
+ form. Of particular importance to any pseudo controller is the read bit,
+ which is guaranteed to be 0x1 per Linux I2C documentation.
+ |
+ | The data_len is the decimal number of either how many bytes to write that
+ will follow, or how many bytes to read and reply with if this is a read
+ request.
+ |
+ | If this is a read, data_len will be the final field in this command. If
+ this is a write, data_len will be followed by the given number of
+ colon-separated hexadecimal byte values, in the format shown in the example
+ above.
+
+----
+
+:Read Command: ``I2C_COMMIT_XFER``
+:Example: ``"I2C_COMMIT_XFER\n"``
+:Details:
+ | This indicates the end of an I2C transaction request, in other words the
+ end of the I2C messages from a single invocation of the I2C adapter's
+ master_xfer() callback. This should be read exactly once after each
+ I2C_BEGIN_XFER, with a varying number of I2C_XFER_REQ between them.
+
+
+Write Commands
+==============
+
+The commands that may be written to a pseudo controller device are:
+
+
+:Write Command: ``SET_ADAPTER_NAME_SUFFIX <suffix>``
+:Example: ``"SET_ADAPTER_NAME_SUFFIX My Adapter\n"``
+:Details:
+ | Sets a suffix to append to the auto-generated I2C adapter name. Only
+ valid before ADAPTER_START. A space or other separator character will be
+ placed between the auto-generated name and the suffix, so there is no need
+ to include a leading separator in the suffix. If the resulting name is too
+ long for the I2C adapter name field, it will be quietly truncated.
+
+----
+
+:Write Command: ``SET_ADAPTER_TIMEOUT_MS <ms>``
+:Example: ``"SET_ADAPTER_TIMEOUT_MS 2000\n"``
+:Details:
+ | Sets the timeout in milliseconds for each I2C transaction, in other words
+ for each master_xfer() reply. Only valid before ADAPTER_START. The I2C
+ subsystem will automatically time out transactions based on this setting.
+ Set to 0 to use the I2C subsystem default timeout. The default timeout for
+ new pseudo adapters where this command has not been used is configurable at
+ i2c-pseudo module load time, and itself has a default independent from the
+ I2C subsystem default. (If the i2c-pseudo module level default is set to 0,
+ that has the same meaning as here.)
+
+----
+
+:Write Command: ``ADAPTER_START``
+:Example: ``"ADAPTER_START\n"``
+:Details:
+ | Tells i2c-pseudo to actually create the I2C adapter. Only valid once per
+ open controller fd.
+
+----
+
+:Write Command: ``GET_ADAPTER_NUM``
+:Example: ``"GET_ADAPTER_NUM\n"``
+:Details:
+ | Asks i2c-pseudo for the number assigned to this I2C adapter by the I2C
+ subsystem. Only valid after ADAPTER_START, because before that the number
+ is not known and cannot be predicted reliably.
+
+----
+
+:Write Command: ``GET_PSEUDO_ID``
+:Example: ``"GET_PSEUDO_ID\n"``
+:Details:
+ | Asks i2c-pseudo for the pseudo ID of this I2C adapter. The pseudo ID will
+ not be reused for the lifetime of the i2c-pseudo module, unless an internal
+ counter wraps. I2C clients can use this to track specific instances of
+ pseudo adapters, even when adapter numbers have been reused.
+
+----
+
+:Write Command: ``I2C_XFER_REPLY <xfer_id> <msg_id> <addr> <flags> <errno> [<read_byte>[:...]]``
+:Example: ``"I2C_XFER_REPLY 3 0 0x0070 0x0000 0\n"``
+:Example: ``"I2C_XFER_REPLY 3 1 0x0070 0x0001 0 0B:29:02:D9\n"``
+:Details:
+ | This is how a pseudo controller can reply to I2C_XFER_REQ. Only valid
+ after I2C_XFER_REQ. A pseudo controller should write one of these for each
+ I2C_XFER_REQ it reads, including for failures, so that I2C device drivers
+ need not wait for the adapter timeout upon failure (if failure is known
+ sooner).
+ |
+ | The fields in common with I2C_XFER_REQ have their same meanings, and their
+ values are expected to exactly match what was read in the I2C_XFER_REQ
+ command that this is in reply to.
+ |
+ | The errno field is how the pseudo controller indicates success or failure
+ for this I2C message. A 0 value indicates success. A non-zero value
+ indicates a failure. Pseudo controllers are encouraged to use errno values
+ to encode some meaning in a failure response, but that is not a requirement,
+ and the I2C adapter interface does not provide a way to pass per-message
+ errno values to a device driver anyways.
+ |
+ | Pseudo controllers are encouraged to reply in the same order as messages
+ were received, however i2c-pseudo will properly match up out-of-order
+ replies with their original requests.
+
+----
+
+:Write Command: ``ADAPTER_SHUTDOWN``
+:Example: ``"ADAPTER_SHUTDOWN\n"``
+:Details:
+ | This tells i2c-pseudo that the pseudo controller wants to shutdown and
+ intends to close the controller device fd soon. Use of this is OPTIONAL, it
+ is perfectly valid to close the controller device fd without ever using this
+ command.
+ |
+ | This commands unblocks any blocked controller I/O (reads, writes, or polls),
+ and that is its main purpose.
+ |
+ | Any I2C transactions attempted by a device driver after this command will
+ fail, and will not be passed on to the userspace controller.
+ |
+ | This DOES NOT delete the I2C adapter. Only closing the fd will do that.
+ That MAY CHANGE in the future, such that this does delete the I2C adapter.
+ (However this will never be required, it will always be okay to simply close
+ the fd.)
+
+
+Example userspace controller code
+=================================
+
+In C, a simple exchange between i2c-pseudo and userspace might look like the
+example below. Note that for brevity this lacks any error checking and
+handling, which a real pseudo controller implementation should have.
+
+::
+
+ int fd;
+ char buf[1<<12];
+
+ fd = open("/dev/i2c-pseudo-controller", O_RDWR);
+ /* Create the I2C adapter. */
+ dprintf(fd, "ADAPTER_START\n");
+
+ /*
+ * Pretend this I2C adapter number is 5, and the first I2C xfer sent to it was
+ * from this command (using its i2c-dev interface):
+ * $ i2cset -y 5 0x70 0xC2
+ *
+ * Then this read would place the following into *buf:
+ * "I2C_BEGIN_XFER\n"
+ * "I2C_XFER_REQ 0 0 0x0070 0x0000 1 C2\n"
+ * "I2C_COMMIT_XFER\n"
+ */
+ read(fd, buf, sizeof(buf));
+
+ /* This reply would allow the i2cset command above to exit successfully. */
+ dprintf(fd, "I2C_XFER_REPLY 0 0 0x0070 0x0000 0\n");
+
+ /*
+ * Now pretend the next I2C xfer sent to this adapter was from:
+ * $ i2cget -y 5 0x70 0xAB
+ *
+ * Then this read would place the following into *buf:
+ * "I2C_BEGIN_XFER\n"
+ * "I2C_XFER_REQ 1 0 0x0070 0x0000 1 AB\n"
+ * "I2C_XFER_REQ 1 1 0x0070 0x0001 1\n'"
+ * "I2C_COMMIT_XFER\n"
+ */
+ read(fd, buf, sizeof(buf));
+
+ /*
+ * These replies would allow the i2cget command above to print the following to
+ * stdout and exit successfully:
+ * 0x0b
+ *
+ * Note that it is also valid to write these together in one write().
+ */
+ dprintf(fd, "I2C_XFER_REPLY 1 0 0x0070 0x0000 0\n");
+ dprintf(fd, "I2C_XFER_REPLY 1 1 0x0070 0x0001 0 0B\n");
+
+ /* Destroy the I2C adapter. */
+ close(fd);
diff --git a/extra/i2c_pseudo/README b/extra/i2c_pseudo/README
index 96efa062b1..1d1ef75641 100644
--- a/extra/i2c_pseudo/README
+++ b/extra/i2c_pseudo/README
@@ -2,12 +2,16 @@ This directory contains the i2c-pseudo Linux kernel module.
The i2c-pseudo module was written with the intention of being submitted upstream
in the Linux kernel. This copy exists because of as 2019-03 this module is not
-yet in the upstream kernel, and even if/when this is included, it may take years
-before making its way to the prepackaged Linux distribution kernels typically
-used by CrOS developers.
+yet in the upstream kernel, and even if/when this is included, it may take a
+long time to get included in prepackaged Linux distribution kernels, especially
+those based on Linux LTS branches.
-See Documentation.txt for more information about the module itself. That file
-is Documentation/i2c/pseudo-controller-interface in the upstream patch.
+See Documentation.rst or Documentation.md for more information about the module
+itself. The reStructuredText (.rst) file is
+Documentation/i2c/pseudo-controller-interface.rst in the upstream patch. The
+Markdown file (.md) is generated using rst2md from
+nb2plots (https://github.com/matthew-brett/nb2plots) which uses
+Sphinx (https://www.sphinx-doc.org/).
When servod starts, if the i2c-pseudo module is loaded servod will automatically
create an I2C pseudo adapter for the Servo I2C bus. That I2C adapter may then