On 7/22/26 14:57, Ilya Chichkov wrote:
Add a "remote-i2c-master" device that exposes a QEMU I2C bus to the
host system through a FUSE/CUSE character device. This lets external
host programs and standard i2c-tools interact with I2C slaves emulated
inside QEMU as if they were real devices attached to the host.
The implementation is split into three layers:
- A non-blocking finite state machine that drives the QEMU I2C
master. It is pumped by a QEMU Bottom Half and uses virtual timers
to yield during long transfers and to model clock stretching for
asynchronous slaves, so the main loop is never blocked. The FSM
walks IDLE -> ADDR -> SEND/RECV -> WAIT_STRETCH -> END -> FINISHED
and handles NACKs (ENXIO), lost arbitration (EBUSY, with optional
back-off and retry), stretch timeouts, and manual abort/reset.
- An abstract RemoteI2CBackend QOM base class that decouples the
internal I2C hardware state machine (the frontend) from any
host-specific transport, exposing on_tx_complete and on_tx_error
virtual callbacks.
- A concrete remote-i2c-backend-cuse backend implementing that
transport over CUSE. It manages the FUSE session and integrates
its file descriptors into QEMU's main AioContext event loop,
translates Linux I2C_RDWR, I2C_SMBUS and I2C_SLAVE ioctls into
generic byte streams for the FSM, and formats responses back into
Linux I2C/SMBus structures for the FUSE driver. SMBus repeated
start is supported for atomic write-then-read operations.
Example usage:
-device remote-i2c-master,i2cbus=i2c-bus.0,devname=i2c-33
-object remote-i2c-backend-cuse,id=b0,devname=i2c-33
This creates /dev/i2c-33 on the host, usable with i2c-tools:
i2cdetect -y -l
i2cget -y <bus_id> <addr> <reg>
Acked-by: Markus Armbruster <[email protected]>
Signed-off-by: Ilya Chichkov <[email protected]>
---
v2:
- docs/system/devices/remote-i2c-master.rst: Documented concurrent
bus access details and the 'raise-arbitrage-lost' property.
---
v3:
- qapi/qom.json: Fix RemoteI2CBackendCuseProperties formatting and
expand member documentation (devname, fuse-opts, debug).
- tests/qtest: Add remote-i2c-cuse-test covering capabilities,
functional smoke test, synchronous sensor read/write.
---
v4:
- qapi: reference the rst doc via :doc:
- qapi: fuse-opts is now ['str'] instead of a space-separated string
- qapi: drop redundant 'debug' property (pass -d via fuse-opts)
---
v5:
- docs/system/device-emulation.rst: add remote-i2c-master.rst to
the toctree under "Emulated Devices"
- qapi/qom.json: move RemoteI2CBackendCuseProperties definition
before RemoteObjectProperties as requested; change "for usage
information" to "for detailed usage information"
---
---
docs/system/device-emulation.rst | 1 +
docs/system/devices/remote-i2c-master.rst | 217 ++++
hw/i2c/Kconfig | 5 +
hw/i2c/meson.build | 6 +
hw/i2c/remote-i2c-backend.c | 30 +
hw/i2c/remote-i2c-cuse.c | 1173 +++++++++++++++++++++
hw/i2c/remote-i2c-fsm.c | 521 +++++++++
hw/i2c/remote-i2c-master.c | 145 +++
hw/i2c/trace-events | 29 +
include/hw/i2c/remote-i2c-backend.h | 70 ++
include/hw/i2c/remote-i2c-cuse.h | 93 ++
include/hw/i2c/remote-i2c-master.h | 77 ++
qapi/qom.json | 29 +
tests/qtest/meson.build | 2 +
tests/qtest/remote-i2c-cuse-test.c | 326 ++++++
15 files changed, 2724 insertions(+)
create mode 100644 docs/system/devices/remote-i2c-master.rst
create mode 100644 hw/i2c/remote-i2c-backend.c
create mode 100644 hw/i2c/remote-i2c-cuse.c
create mode 100644 hw/i2c/remote-i2c-fsm.c
create mode 100644 hw/i2c/remote-i2c-master.c
create mode 100644 include/hw/i2c/remote-i2c-backend.h
create mode 100644 include/hw/i2c/remote-i2c-cuse.h
create mode 100644 include/hw/i2c/remote-i2c-master.h
create mode 100644 tests/qtest/remote-i2c-cuse-test.c
If I understand this proposal correctly, it lets host-side i2c
tools talk to emulated I2C devices. The example is i2cdetect/i2cget
against a tmp105 on an Aspeed bus.
QEMU already has mechanisms for this though. qtest can read/write I2C
registers programmatically, QMP can query device state, and the guest
OS itself sees the I2C bus natively.
The dependency cost seems high for the use case: libfuse3 + the cuse
kernel module pulled into the system emulator build. It's also ~3K
lines of new code, three QOM types, FUSE session management, etc.
The surface area for bugs is large. It's a heavy burden for what it
gives.
I think a lighter QMP-based approach would serve the same purpose,
maybe better, and fit in better in QEMU, without the dependency and
maintenance cost:
- No host kernel dependency
- Works remotely (QMP over socket)
- Fits QEMU's existing management model
- Much less code
- Testable without root or kernel modules
Have you considered extending QMP with an I2C transaction command
instead? Something like:
{ 'command': 'i2c-transfer',
'data': { 'bus': 'str',
'address': 'uint8',
'read': 'bool',
'*data': ['uint8'],
'*length': 'uint16' },
'returns': { '*data': ['uint8'] } }
Thanks,
C.