[RFC PATCH v5 0/3] iio: position: add Rust driver for ams AS5600
Muchamad Coirul Anwar <[email protected]>
| Newsgroups | org.kernel.vger.linux-iio,org.kernel.vger.linux-i2c,org.kernel.vger.linux-kernel,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
This is v5 of the Rust driver for the ams AS5600 12-bit magnetic rotary position sensor. Link: https://lore.kernel.org/linux-iio/[email protected]/ Base tree and dependencies: This series is based on driver-core-testing [1], not vanilla rust-next. It depends on Gary Guo's io_projection-v6 [2] for IoBackend, IoBase, Region, and KnownSize. FallibleIoCapable is included in patch 1/3 following Danilo's suggestion [3] to carry it as a prerequisite until it lands upstream. [1] https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/log/?h=driver-core-testing [2] https://lore.kernel.org/driver-core/[email protected]/ [3] https://lore.kernel.org/all/[email protected]/ Changes since RFC v4: I2C abstraction: - Switch to FallibleIoCapable — IoCapable is infallible by design, but I2C can fail at transport level (Igor) - Restrict I/O ops to I2cClient<Bound> (Danilo) - Add smbus_read_word() and smbus_read_word_swapped() for odd offsets - Add FallibleIoCapable trait to io.rs with blanket impl - Add bit_usize() to bits.rs IIO abstraction: - Replace raw isize mask with IioChanInfo enum (Nuno) - Expand PinnedDrop SAFETY comment re: kernfs_drain() (Danilo) - build_device() takes modes parameter instead of hardcoding - channels() now returns &'static slice Driver: - Use smbus_read_word_swapped() instead of manual swap_bytes() - Use ARef<I2cClient<Bound>> (Danilo) - Drop pr_info! debug logging - Kconfig cleanup (Jonathan) Known limitations (unchanged): - No power management - No write_raw, buffer, or trigger support Design notes: The IIO abstraction uses iio_device_alloc (not devm_*) so the Rust Drop controls cleanup ordering: unregister, drop driver data, then free iio_dev. iio_device_unregister() drains in-flight sysfs reads via kernfs_drain(). This is sufficient for INDIO_DIRECT_MODE without buffer/trigger. Character device paths need separate analysis. Module ownership via __iio_device_register(), not iio_info.owner. Muchamad Coirul Anwar (3): i2c: rust: implement SMBus access via IoBackend and FallibleIoCapable rust: add minimal IIO subsystem abstractions iio: position: add Rust driver for ams AS5600 drivers/iio/position/Kconfig | 11 + drivers/iio/position/Makefile | 1 + drivers/iio/position/as5600.rs | 189 +++++++++++++++++ rust/bindings/bindings_helper.h | 2 + rust/kernel/bits.rs | 29 +++ rust/kernel/error.rs | 1 + rust/kernel/i2c.rs | 302 +++++++++++++++++++++++++++ rust/kernel/iio.rs | 384 +++++++++++++++++++++++++++++++++++ rust/kernel/io.rs | 66 ++++-- rust/kernel/lib.rs | 2 + 10 files changed, 967 insertions(+), 20 deletions(-) create mode 100644 drivers/iio/position/as5600.rs create mode 100644 rust/kernel/iio.rs --- Tested on BeagleBone Black (AM335x), kernel 7.2.0-rc1+, AS5600 on i2c-2 (0x36) at 3.3V, 6mm diametric neodymium magnet. Full test session: 2026-08-17. Build: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules (zero warnings, zero errors) Functional tests: 1. Probe and registration: $ sudo insmod as5600.ko $ echo "as5600 0x36" > /sys/bus/i2c/devices/i2c-2/new_device $ cat /sys/bus/iio/devices/iio:device0/name as5600 $ ls /sys/bus/i2c/devices/2-0036/driver 2-0036 bind module uevent unbind 2. Raw angle and scale (magnet present): $ cat /sys/bus/iio/devices/iio:device0/in_angl_raw 3366 $ cat /sys/bus/iio/devices/iio:device0/in_angl_scale 0.001533981 20 consecutive reads, all within 0-4095. Computed: 3366 * 0.001533981 = 5.163 rad (~295.9 degrees). 3. Unbind/rebind lifecycle (PinnedDrop with ARef cleanup): $ echo "2-0036" > /sys/bus/i2c/devices/2-0036/driver/unbind $ ls /sys/bus/iio/devices/iio:device0 2>&1 ls: cannot access '...': No such file or directory $ echo "2-0036" > /sys/bus/i2c/drivers/as5600/bind $ cat /sys/bus/iio/devices/iio:device0/name as5600 $ dmesg | grep -i "oops\|panic\|bug:" (empty) 4. Concurrent stress (Mutex serialization under contention): 10 parallel readers hammering in_angl_raw for 5 seconds. Repeated as 20-cycle unbind/rebind loop with readers active throughout (8 seconds total). $ dmesg | grep -i "oops\|panic\|bug:\|rcu" (empty) 5. Module removal under active I/O: a) rmmod while flood readers are running — no crash. iio_device_unregister() drains in-flight read_raw callbacks before PinnedDrop proceeds; subsequent reads return ENOENT. b) rmmod while a sysfs fd is held open — no crash. iio_dev kref not released until fd is closed. $ dmesg | grep -i "oops\|panic\|bug:" (empty) 6. Lifecycle stress: 50x rapid unbind/rebind — no crash, device functional after all cycles. 50x insmod/rmmod — no crash. $ dmesg | grep -i "oops\|panic\|bug:" (empty) 7. I2C bus disconnect: SCL/SDA physically pulled while read loop is running. Driver returns "Remote I/O error" immediately on each failed transfer; no hang, no internal retry loop. Cable reconnected — reads resume from the next iteration without rmmod (about 2 seconds downtime). rmmod issued while bus still in error state — exits cleanly. ARef<I2cClient> drop is put_device() only, no bus transaction. $ dmesg | grep -i "oops\|panic\|bug:" (empty) 8. Memory and locking: dmesg contains no strings matching "KASAN:" or "possible deadlock". The test kernel was not built with CONFIG_KASAN or CONFIG_PROVE_LOCKING; the above is a pattern match against kernel log output, not sanitizer or lockdep instrumentation. kmemleak not available (CONFIG_DEBUG_KMEMLEAK not set). -- 2.50.0