Linux IIO development
 help / color / mirror / Atom feed
From: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
To: jic23@kernel.org, lars@metafoo.de
Cc: linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-i2c@vger.kernel.org, rust-for-linux@vger.kernel.org,
	andi.shyti@kernel.org, wsa+renesas@sang-engineering.com,
	ojeda@kernel.org, dakr@kernel.org, igor.korotin@linux.dev,
	branstj@gmail.com, brucer42@gmail.com,
	Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
Subject: [RFC PATCH v5 1/3] i2c: rust: implement SMBus access via IoBackend and FallibleIoCapable
Date: Sat, 22 Aug 2026 14:26:56 +0800	[thread overview]
Message-ID: <20260822062725.60519-2-muchamadcoirulanwar@gmail.com> (raw)
In-Reply-To: <20260822062725.60519-1-muchamadcoirulanwar@gmail.com>

Implement SMBus byte and word read/write operations for I2cClient using
the FallibleIoCapable trait from the generic I/O backend infrastructure.

I2cClient now exposes an I2cBackend that implements FallibleIoCapable<u8>
and FallibleIoCapable<u16>, replacing the previous IoCapable approach.
I2C/SMBus bus transactions are inherently fallible (NACK, arbitration
loss, timeout), so the infallible IoCapable is not appropriate here.
FallibleIoCapable carries the errno from i2c_smbus_read_byte_data and
i2c_smbus_read_word_data directly to the caller via Result<T>.

The implementation is restricted to I2cClient<Bound> as I/O operations
require a live device context.

I2cClient<Bound>::smbus_io() returns an I2cView handle for use with the
generic try_read8/try_read16 methods. Two standalone methods are also
provided for odd-offset word access that bypasses the alignment check
in the Io trait:

  smbus_read_word()        - CPU-native byte order (SMBus LE wire format)
  smbus_read_word_swapped() - byte-swapped result for big-endian devices

maxsize is 256, covering the SMBus command byte range 0x00-0xFF. This
is the command byte space, not the 7-bit device address which is handled
by the I2C core at adapter level.

Link: https://lore.kernel.org/rust-for-linux/20260131-i2c-adapter-v1-4-5a436e34cd1a@gmail.com/
Link: https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git/commit/?h=driver-core-testing&id=121d87b28e1d9061d3aaa156c43a627d3cb5e620
Suggested-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Muchamad Coirul Anwar <muchamadcoirulanwar@gmail.com>
---
 rust/kernel/bits.rs |  29 +++++
 rust/kernel/i2c.rs  | 302 ++++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/io.rs   |  66 +++++++---
 3 files changed, 377 insertions(+), 20 deletions(-)

diff --git a/rust/kernel/bits.rs b/rust/kernel/bits.rs
index 2daead125626..a6537a668dd6 100644
--- a/rust/kernel/bits.rs
+++ b/rust/kernel/bits.rs
@@ -41,6 +41,7 @@ pub const fn [<bit_ $ty>](n: u32) -> $ty {
 impl_bit_fn!(u32);
 impl_bit_fn!(u16);
 impl_bit_fn!(u8);
+impl_bit_fn!(usize);
 
 macro_rules! impl_genmask_fn {
     (
@@ -203,3 +204,31 @@ pub const fn [<genmask_ $ty>](range: RangeInclusive<u32>) -> $ty {
     /// assert_eq!(genmask_u8(0..=7), u8::MAX);
     /// ```
 );
+
+impl_genmask_fn!(
+    usize,
+    /// # Examples
+    ///
+    /// ```
+    /// # #![expect(clippy::reversed_empty_ranges)]
+    /// # use kernel::bits::genmask_checked_usize;
+    /// assert_eq!(genmask_checked_usize(0..=0), Some(0b1));
+    /// assert_eq!(genmask_checked_usize(0..=3), Some(0b1111));
+    /// assert_eq!(genmask_checked_usize(1..=3), Some(0b1110));
+    ///
+    /// // `200` is out of the supported bit range on all platforms.
+    /// assert_eq!(genmask_checked_usize(0..=200), None);
+    ///
+    /// // Invalid range where the start is bigger than the end.
+    /// assert_eq!(genmask_checked_usize(5..=2), None);
+    /// ```
+    ,
+    /// # Examples
+    ///
+    /// ```
+    /// # use kernel::bits::genmask_usize;
+    /// assert_eq!(genmask_usize(0..=0), 0b1);
+    /// assert_eq!(genmask_usize(0..=3), 0b1111);
+    /// assert_eq!(genmask_usize(1..=3), 0b1110);
+    /// ```
+);
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 624b971ca8b0..f939907573a6 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -14,8 +14,15 @@
     devres::Devres,
     driver,
     error::*,
+    io::{
+        FallibleIoCapable,
+        IoBackend,
+        IoBase,
+        Region, //
+    },
     of,
     prelude::*,
+    ptr::KnownSize,
     sync::aref::{
         ARef,
         AlwaysRefCounted, //
@@ -601,3 +608,298 @@ unsafe impl Send for Registration {}
 // SAFETY: `Registration` offers no interior mutability (no mutation through &self
 // and no mutable access is exposed)
 unsafe impl Sync for Registration {}
+
+// SAFETY: `I2cClient<Bound>` wraps a kernel `struct i2c_client`. The I2C core
+// and bus locking mechanisms ensure that the underlying client structure can
+// be safely transferred between threads.
+unsafe impl Send for I2cClient<device::Bound> {}
+
+// SAFETY: `I2cClient<Bound>` wraps a kernel `struct i2c_client`. All methods
+// that access the client go through kernel I2C core functions that provide
+// their own synchronization. No &self method exposes interior mutability.
+unsafe impl Sync for I2cClient<device::Bound> {}
+
+// SAFETY: `I2cClient<Bound>` is always reference-counted via the embedded
+// `struct device`. `get_device`/`put_device` increment and decrement the
+// device refcount atomically. A separate impl is needed for `I2cClient<Bound>`
+// because `AlwaysRefCounted` is not implemented generically over all
+// `DeviceContext`s — only the specific contexts that are safe to refcount
+// from arbitrary threads.
+unsafe impl AlwaysRefCounted for I2cClient<device::Bound> {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::get_device(self.as_ref().as_raw()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::put_device(&raw mut (*obj.as_ref().as_raw()).dev) }
+    }
+}
+
+/// I/O backend for SMBus register access via I2C.
+///
+/// This backend implements only [`FallibleIoCapable`] and not [`IoCapable`],
+/// because I2C/SMBus bus transactions are inherently fallible — NACK,
+/// arbitration loss, and timeout can occur regardless of address validity.
+/// The infallible [`Io::read`], [`Io::write`], and [`Io::update`] methods
+/// are therefore compile-time unavailable for this backend.
+pub struct I2cBackend;
+
+/// View type for [`I2cBackend`], carrying a reference to an I2C client and
+/// a fake pointer that encodes the register offset and address-space size
+/// as fat-pointer metadata.
+///
+/// The pointer field is never dereferenced. After [`IoBackend::project_view`]
+/// projects an offset into the pointer, `addr()` yields that offset as the
+/// SMBus command byte. [`KnownSize::size()`] reads the fat-pointer metadata
+/// length (256 for the SMBus command space).
+///
+/// # Invariants
+///
+/// `ptr` is a non-dereferenceable fat pointer. Its address component encodes
+/// the SMBus register offset (0..=255) after [`IoBackend::project_view`]
+/// projection; its length metadata is 256 (the SMBus command byte address
+/// space). `client` points to a valid `I2cClient<Bound>` that remains live
+/// for `'a`.
+pub struct I2cView<'a, T: ?Sized> {
+    client: &'a I2cClient<device::Bound>,
+    ptr: *mut T,
+}
+
+impl<T: ?Sized> Copy for I2cView<'_, T> {}
+
+impl<T: ?Sized> Clone for I2cView<'_, T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl IoBackend for I2cBackend {
+    type View<'a, T: ?Sized + KnownSize> = I2cView<'a, T>;
+
+    #[inline]
+    fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
+        view.ptr
+    }
+
+    #[inline]
+    unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
+        view: Self::View<'a, T>,
+        ptr: *mut U,
+    ) -> Self::View<'a, U> {
+        // INVARIANT: Per safety requirement.
+        I2cView {
+            client: view.client,
+            ptr,
+        }
+    }
+}
+
+impl FallibleIoCapable<u8> for I2cBackend {
+    #[inline]
+    fn io_try_read<'a>(view: I2cView<'a, u8>) -> Result<u8> {
+        // `io_view()` ensures `offset + 1 <= 256`, so `addr()` is at most 255;
+        // the `as u8` cast below is therefore lossless.
+        let reg = Self::as_ptr(view).addr() as u8;
+        // SAFETY: `view.client.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        // `i2c_smbus_read_byte_data` is safe to call with any valid client pointer
+        // and any u8 command byte.
+        let ret = unsafe { bindings::i2c_smbus_read_byte_data(view.client.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(ret as u8)
+        }
+    }
+
+    #[inline]
+    fn io_try_write<'a>(view: I2cView<'a, u8>, value: u8) -> Result {
+        // `io_view()` ensures `offset + 1 <= 256`, so `addr()` is at most 255;
+        // the `as u8` cast below is therefore lossless.
+        let reg = Self::as_ptr(view).addr() as u8;
+        // SAFETY: `view.client.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        // `i2c_smbus_write_byte_data` is safe to call with any valid client pointer
+        // and any u8 command byte and value.
+        let ret = unsafe { bindings::i2c_smbus_write_byte_data(view.client.as_raw(), reg, value) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl FallibleIoCapable<u16> for I2cBackend {
+    #[inline]
+    fn io_try_read<'a>(view: I2cView<'a, u16>) -> Result<u16> {
+        // `io_view()` ensures `offset + 2 <= 256`, so `addr()` is at most 254;
+        // the `as u8` cast below is therefore lossless.
+        let reg = Self::as_ptr(view).addr() as u8;
+        // SAFETY: `view.client.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        // `i2c_smbus_read_word_data` is safe to call with any valid client pointer
+        // and any u8 command byte.
+        let ret = unsafe { bindings::i2c_smbus_read_word_data(view.client.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(ret as u16)
+        }
+    }
+
+    #[inline]
+    fn io_try_write<'a>(view: I2cView<'a, u16>, value: u16) -> Result {
+        // `io_view()` ensures `offset + 2 <= 256`, so `addr()` is at most 254;
+        // the `as u8` cast below is therefore lossless.
+        let reg = Self::as_ptr(view).addr() as u8;
+        // SAFETY: `view.client.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        // `i2c_smbus_write_word_data` is safe to call with any valid client pointer
+        // and any u8 command byte and u16 value.
+        let ret = unsafe { bindings::i2c_smbus_write_word_data(view.client.as_raw(), reg, value) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl<'a, T: ?Sized + KnownSize> IoBase<'a> for I2cView<'a, T> {
+    type Backend = I2cBackend;
+    type Target = T;
+
+    #[inline]
+    fn as_view(self) -> I2cView<'a, T> {
+        self
+    }
+}
+
+// SAFETY: `I2cView` contains `&'a I2cClient<Bound>` (which is `Send` because
+// `I2cClient<Bound>: Sync`) and `*mut T`. The raw pointer is never
+// dereferenced — it only encodes the SMBus register offset as its address.
+// With `T: Sync`, moving the view to another thread cannot cause data races.
+unsafe impl<T: ?Sized + Sync> Send for I2cView<'_, T> {}
+
+// SAFETY: `I2cView` contains `&'a I2cClient<Bound>` (which is `Sync`) and
+// `*mut T`. The raw pointer is never dereferenced; sharing an `&I2cView`
+// across threads is equivalent to sharing `&I2cClient<Bound>` and a
+// read-only address value. `T: Sync` ensures the addressed data is
+// safe to access from multiple threads.
+unsafe impl<T: ?Sized + Sync> Sync for I2cView<'_, T> {}
+
+impl I2cClient<device::Bound> {
+    /// Returns an I/O handle for SMBus register access on this I2C client.
+    ///
+    /// The returned handle provides fallible read/write methods for the
+    /// 256-byte SMBus command address space (0x00–0xFF). This is the SMBus
+    /// command byte range, NOT the 7-bit device address, which is handled
+    /// by the I2C core at the adapter level.
+    ///
+    /// Note: [`Io::try_read16`] and [`Io::try_write16`] on the returned handle
+    /// reject odd offsets. The underlying [`Region`] base address is 0, so
+    /// [`offset_valid`] checks `(0 + offset) % 2 == 0` — only even offsets
+    /// pass. For word-sized access to odd-offset registers use
+    /// [`smbus_read_word`] or [`smbus_read_word_swapped`] instead.
+    ///
+    /// The underlying pointer in the returned [`I2cView`] is never
+    /// dereferenced; it encodes the register address space size as
+    /// fat-pointer metadata and the register offset as the pointer address.
+    ///
+    /// [`smbus_read_word`]: Self::smbus_read_word
+    /// [`smbus_read_word_swapped`]: Self::smbus_read_word_swapped
+    #[inline]
+    pub fn smbus_io(&self) -> I2cView<'_, Region<256>> {
+        // INVARIANT: `client` is `self`, a valid `I2cClient<Bound>`.
+        //
+        // `ptr` is a "fake pointer" — it is constructed solely to carry two
+        // pieces of metadata through the `IoBase` machinery:
+        //   - address component: 0 initially; after each `project_view` call,
+        //     this becomes the register offset (the SMBus command byte).
+        //   - length metadata: 256, encoding the SMBus command address space
+        //     size so `io_view()` can bounds-check offsets.
+        //
+        // `without_provenance_mut(0)` produces a pointer with no memory
+        // provenance — it cannot be used to read or write memory. This is safe
+        // because `I2cBackend::as_ptr()` extracts the address as a `usize`
+        // offset and passes it to `i2c_smbus_*` functions, never dereferencing
+        // the pointer itself. Using a provenance-free base avoids accidentally
+        // creating a pointer that appears to alias real memory.
+        I2cView {
+            client: self,
+            ptr: Region::<256>::ptr_from_raw_parts_mut(core::ptr::without_provenance_mut(0), 256),
+        }
+    }
+
+    /// Reads a 16-bit word from an SMBus register in CPU-native byte order.
+    ///
+    /// Wraps `i2c_smbus_read_word_data`. The `reg` parameter is the SMBus
+    /// command byte (0x00–0xFF) — an instruction sent to the device over the
+    /// serial bus, not a memory address. There is no alignment requirement:
+    /// any command byte value is valid regardless of whether it is odd or even.
+    ///
+    /// SMBus transmits the low byte first (little-endian on the wire), and this
+    /// method returns the value in CPU-native byte order without further
+    /// conversion. Use [`Self::smbus_read_word_swapped`] for devices that store
+    /// multi-byte registers in big-endian (MSB-first) format.
+    ///
+    /// Returns `Err` if the bus transaction fails (e.g. NACK, arbitration loss,
+    /// or timeout).
+    #[inline]
+    pub fn smbus_read_word(&self, reg: u8) -> Result<u16> {
+        // SAFETY: `self.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        // `i2c_smbus_read_word_data` is safe to call with any valid client
+        // pointer and any u8 command byte.
+        let ret = unsafe { bindings::i2c_smbus_read_word_data(self.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok(ret as u16)
+        }
+    }
+
+    /// Reads a 16-bit word from an SMBus register with bytes unconditionally
+    /// swapped.
+    ///
+    /// Wraps `i2c_smbus_read_word_data` and applies [`u16::swap_bytes`] to the
+    /// result. Use this for devices that store multi-byte registers in
+    /// big-endian (MSB-first) format, which is common among I2C sensors whose
+    /// datasheets do not reference the SMBus specification.
+    ///
+    /// The swap is **unconditional** — it is not equivalent to `be16_to_cpu`.
+    /// On a big-endian CPU, `be16_to_cpu` would be a no-op, but this method
+    /// still swaps. The reason: SMBus always transmits the low byte first, so
+    /// the driver always receives data in little-endian wire order regardless
+    /// of CPU endianness. The swap corrects for the device's wire-level byte
+    /// order, not the CPU's native order.
+    ///
+    /// The `reg` parameter is the SMBus command byte (0x00–0xFF). There is no
+    /// alignment requirement; any command byte value is valid.
+    ///
+    /// Returns `Err` if the bus transaction fails (e.g. NACK, arbitration loss,
+    /// or timeout).
+    ///
+    /// # Example
+    ///
+    /// ```ignore
+    /// // AS5600 stores the 12-bit raw angle big-endian at register 0x0C.
+    /// let raw = client.smbus_read_word_swapped(0x0C)?;
+    /// let angle = raw & 0x0FFF;
+    /// ```
+    #[inline]
+    pub fn smbus_read_word_swapped(&self, reg: u8) -> Result<u16> {
+        // SAFETY: `self.as_raw()` returns a valid `*mut struct i2c_client`
+        // pointer as guaranteed by the type invariant of `I2cClient`.
+        let ret = unsafe { bindings::i2c_smbus_read_word_data(self.as_raw(), reg) };
+        if ret < 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            Ok((ret as u16).swap_bytes())
+        }
+    }
+}
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 95f46bb75f9e..516895ca2082 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -276,6 +276,36 @@ pub trait IoCapable<T>: IoBackend {
     fn io_write<'a>(view: Self::View<'a, T>, value: T);
 }
 
+/// Fallible counterpart of [`IoCapable`] for I/O backends where operations can fail at the
+/// transport level (e.g. I2C, SPI).
+///
+/// Infallible backends ([`IoCapable`] implementors) get this for free via blanket implementation.
+/// Fallible-only backends implement this trait directly without implementing [`IoCapable`]; the
+/// infallible [`Io::read`], [`Io::write`], and [`Io::update`] methods will then be unavailable,
+/// enforcing that callers use the `try_*` variants instead.
+pub trait FallibleIoCapable<T>: IoBackend {
+    /// Performs an I/O read of type `T` at `view` and returns the result, or an error if the
+    /// transport-level operation fails.
+    fn io_try_read<'a>(view: Self::View<'a, T>) -> Result<T>;
+
+    /// Performs an I/O write of `value` at `view`, or returns an error if the transport-level
+    /// operation fails.
+    fn io_try_write<'a>(view: Self::View<'a, T>, value: T) -> Result;
+}
+
+impl<B: IoCapable<T>, T> FallibleIoCapable<T> for B {
+    #[inline(always)]
+    fn io_try_read<'a>(view: Self::View<'a, T>) -> Result<T> {
+        Ok(Self::io_read(view))
+    }
+
+    #[inline(always)]
+    fn io_try_write<'a>(view: Self::View<'a, T>, value: T) -> Result {
+        Self::io_write(view, value);
+        Ok(())
+    }
+}
+
 /// Trait indicating that an I/O backend supports memory copy operations.
 pub trait IoCopyable: IoBackend {
     /// Copy contents of `view` to `buffer`.
@@ -645,7 +675,7 @@ fn copy_to_slice(self, data: &mut [u8])
     fn try_read8(self, offset: usize) -> Result<u8>
     where
         usize: IoLoc<Self::Target, u8, IoType = u8>,
-        Self::Backend: IoCapable<u8>,
+        Self::Backend: FallibleIoCapable<u8>,
     {
         self.try_read(offset)
     }
@@ -655,7 +685,7 @@ fn try_read8(self, offset: usize) -> Result<u8>
     fn try_read16(self, offset: usize) -> Result<u16>
     where
         usize: IoLoc<Self::Target, u16, IoType = u16>,
-        Self::Backend: IoCapable<u16>,
+        Self::Backend: FallibleIoCapable<u16>,
     {
         self.try_read(offset)
     }
@@ -665,7 +695,7 @@ fn try_read16(self, offset: usize) -> Result<u16>
     fn try_read32(self, offset: usize) -> Result<u32>
     where
         usize: IoLoc<Self::Target, u32, IoType = u32>,
-        Self::Backend: IoCapable<u32>,
+        Self::Backend: FallibleIoCapable<u32>,
     {
         self.try_read(offset)
     }
@@ -675,7 +705,7 @@ fn try_read32(self, offset: usize) -> Result<u32>
     fn try_read64(self, offset: usize) -> Result<u64>
     where
         usize: IoLoc<Self::Target, u64, IoType = u64>,
-        Self::Backend: IoCapable<u64>,
+        Self::Backend: FallibleIoCapable<u64>,
     {
         self.try_read(offset)
     }
@@ -685,7 +715,7 @@ fn try_read64(self, offset: usize) -> Result<u64>
     fn try_write8(self, value: u8, offset: usize) -> Result
     where
         usize: IoLoc<Self::Target, u8, IoType = u8>,
-        Self::Backend: IoCapable<u8>,
+        Self::Backend: FallibleIoCapable<u8>,
     {
         self.try_write(offset, value)
     }
@@ -695,7 +725,7 @@ fn try_write8(self, value: u8, offset: usize) -> Result
     fn try_write16(self, value: u16, offset: usize) -> Result
     where
         usize: IoLoc<Self::Target, u16, IoType = u16>,
-        Self::Backend: IoCapable<u16>,
+        Self::Backend: FallibleIoCapable<u16>,
     {
         self.try_write(offset, value)
     }
@@ -705,7 +735,7 @@ fn try_write16(self, value: u16, offset: usize) -> Result
     fn try_write32(self, value: u32, offset: usize) -> Result
     where
         usize: IoLoc<Self::Target, u32, IoType = u32>,
-        Self::Backend: IoCapable<u32>,
+        Self::Backend: FallibleIoCapable<u32>,
     {
         self.try_write(offset, value)
     }
@@ -715,7 +745,7 @@ fn try_write32(self, value: u32, offset: usize) -> Result
     fn try_write64(self, value: u64, offset: usize) -> Result
     where
         usize: IoLoc<Self::Target, u64, IoType = u64>,
-        Self::Backend: IoCapable<u64>,
+        Self::Backend: FallibleIoCapable<u64>,
     {
         self.try_write(offset, value)
     }
@@ -827,10 +857,10 @@ fn write64(self, value: u64, offset: usize)
     fn try_read<T, L>(self, location: L) -> Result<T>
     where
         L: IoLoc<Self::Target, T>,
-        Self::Backend: IoCapable<L::IoType>,
+        Self::Backend: FallibleIoCapable<L::IoType>,
     {
         let view = io_view::<Self, L::IoType>(self, location.offset())?;
-        Ok(Self::Backend::io_read(view).into())
+        Ok(Self::Backend::io_try_read(view)?.into())
     }
 
     /// Generic fallible write with runtime bounds check.
@@ -860,12 +890,11 @@ fn try_read<T, L>(self, location: L) -> Result<T>
     fn try_write<T, L>(self, location: L, value: T) -> Result
     where
         L: IoLoc<Self::Target, T>,
-        Self::Backend: IoCapable<L::IoType>,
+        Self::Backend: FallibleIoCapable<L::IoType>,
     {
         let view = io_view::<Self, L::IoType>(self, location.offset())?;
         let io_value = value.into();
-        Self::Backend::io_write(view, io_value);
-        Ok(())
+        Self::Backend::io_try_write(view, io_value)
     }
 
     /// Generic fallible write of a fully-located register value.
@@ -905,7 +934,7 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
     where
         L: IoLoc<Self::Target, T>,
         V: LocatedRegister<Self::Target, Location = L, Value = T>,
-        Self::Backend: IoCapable<L::IoType>,
+        Self::Backend: FallibleIoCapable<L::IoType>,
     {
         let (location, value) = value.into_io_op();
 
@@ -938,16 +967,13 @@ fn try_write_reg<T, L, V>(self, value: V) -> Result
     fn try_update<T, L, F>(self, location: L, f: F) -> Result
     where
         L: IoLoc<Self::Target, T>,
-        Self::Backend: IoCapable<L::IoType>,
+        Self::Backend: FallibleIoCapable<L::IoType>,
         F: FnOnce(T) -> T,
     {
         let view = io_view::<Self, L::IoType>(self, location.offset())?;
-
-        let value: T = Self::Backend::io_read(view).into();
+        let value: T = Self::Backend::io_try_read(view)?.into();
         let io_value = f(value).into();
-        Self::Backend::io_write(view, io_value);
-
-        Ok(())
+        Self::Backend::io_try_write(view, io_value)
     }
 
     /// Generic infallible read with compile-time bounds check.
-- 
2.50.0


  reply	other threads:[~2026-08-22  6:27 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-22  6:26 [RFC PATCH v5 0/3] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
2026-08-22  6:26 ` Muchamad Coirul Anwar [this message]
2026-08-23 23:41   ` [RFC PATCH v5 1/3] i2c: rust: implement SMBus access via IoBackend and FallibleIoCapable Jonathan Cameron
2026-08-22  6:26 ` [RFC PATCH v5 2/3] rust: add minimal IIO subsystem abstractions Muchamad Coirul Anwar
2026-08-24  0:07   ` Jonathan Cameron
2026-08-22  6:26 ` [RFC PATCH v5 3/3] iio: position: add Rust driver for ams AS5600 Muchamad Coirul Anwar
2026-08-24  0:17   ` Jonathan Cameron

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=20260822062725.60519-2-muchamadcoirulanwar@gmail.com \
    --to=muchamadcoirulanwar@gmail.com \
    --cc=andi.shyti@kernel.org \
    --cc=branstj@gmail.com \
    --cc=brucer42@gmail.com \
    --cc=dakr@kernel.org \
    --cc=igor.korotin@linux.dev \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --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