[PATCH v2 03/16] rust: io: support register projections
Gary Guo <[email protected]> Wed, 05 Aug 2026 17:35:46 +0100
| Newsgroups | org.kernel.vger.rust-for-linux,dev.linux.lists.driver-core,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
`IoLoc`s themselves just describe a projection from a region to a concrete register. Thus, support it in `io_project` macro too. Also, update methods that operate on `IoLoc` to use I/O projection. Documentation of `io_project!` is not expanded yet as the example works better when `register!` type can specify base type. `io_read!` and `io_write!` gains the ability to operate on registers as corollary of the capability of `io_project!`. Examples are not added because `read` and `write` is still preferrably used instead. Signed-off-by: Gary Guo <[email protected]> --- rust/kernel/io.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 71c6180ed745..ae18890866b6 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -295,7 +295,7 @@ fn transmute_neo<Src: IntoBytes, Dst: FromBytes>(val: Src) -> Dst { /// /// The layout of the type and the underlying primitive must match; this is enforced via const /// assertions when I/O methods are used, as the type system cannot represent this. -/// [`IoRepr::from_repr`] and [`IoRepr::into_expr`] can be overridden for conversions, however it +/// [`IoRepr::from_repr`] and [`IoRepr::into_repr`] can be overridden for conversions, however it /// should be noted that they are only invoked on value read/write operations and are not invoked /// on byte operations such as [`Io::copy_read`]. /// @@ -970,8 +970,7 @@ fn try_read<T, L>(self, location: L) -> Result<T> L: IoLoc<Self::Target, T>, Self::Backend: IoCapable<<T as IoRepr>::Repr>, { - let view = io_view::<Self, T>(self, location.offset())?; - Ok(view.read_val()) + Ok(io_read!(self, try: location)) } /// Generic fallible write with runtime bounds check. @@ -1004,8 +1003,7 @@ fn try_write<T, L>(self, location: L, value: T) -> Result L: IoLoc<Self::Target, T>, Self::Backend: IoCapable<<T as IoRepr>::Repr>, { - let view = io_view::<Self, T>(self, location.offset())?; - view.write_val(value); + io_write!(self, try: location, value); Ok(()) } @@ -1084,7 +1082,7 @@ fn try_update<T, L, F>(self, location: L, f: F) -> Result Self::Backend: IoCapable<<T as IoRepr>::Repr>, F: FnOnce(T) -> T, { - let view = io_view::<Self, T>(self, location.offset())?; + let view = io_project!(self, try: location); view.write_val(f(view.read_val())); Ok(()) } @@ -1117,8 +1115,7 @@ fn read<T, L>(self, location: L) -> T L: IoLoc<Self::Target, T>, Self::Backend: IoCapable<<T as IoRepr>::Repr>, { - let view = io_view_assert::<Self, T>(self, location.offset()); - view.read_val() + io_read!(self, build: location) } /// Generic infallible write with compile-time bounds check. @@ -1149,8 +1146,7 @@ fn write<T, L>(self, location: L, value: T) L: IoLoc<Self::Target, T>, Self::Backend: IoCapable<<T as IoRepr>::Repr>, { - let view = io_view_assert::<Self, T>(self, location.offset()); - view.write_val(value) + io_write!(self, build: location, value); } /// Generic infallible write of a fully-located register value. @@ -1227,7 +1223,7 @@ fn update<T, L, F>(self, location: L, f: F) Self::Backend: IoCapable<<T as IoRepr>::Repr>, F: FnOnce(T) -> T, { - let view = io_view_assert::<Self, T>(self, location.offset()); + let view = io_project!(self, build: location); view.write_val(f(view.read_val())); } } @@ -1772,6 +1768,25 @@ pub unsafe fn project_view<U: ?Sized + KnownSize>( // SAFETY: Per safety requirement. unsafe { T::Backend::project_view::<T::Target, _>(self.0, ptr) } } + + #[inline(always)] + pub fn try_project_loc<U, L>( + self, + location: L, + ) -> Result<<T::Backend as IoBackend>::View<'a, U>> + where + L: IoLoc<T::Target, U>, + { + io_view::<_, U>(self.0, location.offset()) + } + + #[inline(always)] + pub fn project_loc<U, L>(self, location: L) -> <T::Backend as IoBackend>::View<'a, U> + where + L: IoLoc<T::Target, U>, + { + io_view_assert::<_, U>(self.0, location.offset()) + } } /// Project an I/O type to a subview of it. @@ -1799,6 +1814,21 @@ pub unsafe fn project_view<U: ?Sized + KnownSize>( #[macro_export] #[doc(hidden)] macro_rules! io_project { + // Register projection + ($io:expr, try: $ioloc:expr) => {{ + #[allow(unused)] + use $crate::io::IoBase as _; + let view = $crate::io::ProjectHelper($io.as_view()); + view.try_project_loc($ioloc)? + }}; + ($io:expr, build: $ioloc:expr) => {{ + #[allow(unused)] + use $crate::io::IoBase as _; + let view = $crate::io::ProjectHelper($io.as_view()); + view.project_loc($ioloc) + }}; + + // Field or index projection ($io:expr, $($proj:tt)*) => {{ #[allow(unused)] use $crate::io::IoBase as _; @@ -1869,6 +1899,12 @@ macro_rules! io_write { (@parse [$io:expr] [$($proj:tt)*] [[$flavor:ident: $index:expr] $($rest:tt)*]) => { $crate::io_write!(@parse [$io] [$($proj)* [$flavor: $index]] [$($rest)*]) }; + (@parse [$io:expr] [] [try: $ioloc:expr, $($rest:tt)*]) => { + $crate::io_write!(@parse [$io] [try: $ioloc] [, $($rest)*]) + }; + (@parse [$io:expr] [] [build: $ioloc:expr, $($rest:tt)*]) => { + $crate::io_write!(@parse [$io] [build: $ioloc] [, $($rest)*]) + }; ($io:expr, $($rest:tt)*) => { $crate::io_write!(@parse [$io] [] [$($rest)*]) }; -- 2.54.0