[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DG0CVJN57L5F.2I6J4RXMJOGQ@garyguo.net>
Date: Wed, 28 Jan 2026 16:16:50 +0000
From: "Gary Guo" <gary@...yguo.net>
To: "Alexandre Courbot" <acourbot@...dia.com>, "Danilo Krummrich"
<dakr@...nel.org>, "Alice Ryhl" <aliceryhl@...gle.com>, "Daniel Almeida"
<daniel.almeida@...labora.com>, "Miguel Ojeda" <ojeda@...nel.org>, "Boqun
Feng" <boqun.feng@...il.com>, "Gary Guo" <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>, "Benno Lossin"
<lossin@...nel.org>, "Andreas Hindborg" <a.hindborg@...nel.org>, "Trevor
Gross" <tmgross@...ch.edu>
Cc: "Yury Norov" <yury.norov@...il.com>, "John Hubbard"
<jhubbard@...dia.com>, "Alistair Popple" <apopple@...dia.com>, "Joel
Fernandes" <joelagnelf@...dia.com>, "Timur Tabi" <ttabi@...dia.com>, "Edwin
Peer" <epeer@...dia.com>, "Eliot Courtney" <ecourtney@...dia.com>, "Dirk
Behme" <dirk.behme@...bosch.com>, "Steven Price" <steven.price@....com>,
<rust-for-linux@...r.kernel.org>, <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v4 5/7] rust: io: add `register!` macro
On Wed Jan 28, 2026 at 2:37 AM GMT, Alexandre Courbot wrote:
> Add a macro for defining hardware register types with I/O accessors.
>
> Each register field is represented as a `Bounded` of the appropriate bit
> width, ensuring field values are never silently truncated.
>
> Fields can optionally be converted to/from custom types, either fallibly
> or infallibly.
>
> The address of registers can be direct, relative, or indexed, supporting
> most of the patterns in which registers are arranged.
>
> Tested-by: Dirk Behme <dirk.behme@...bosch.com>
> Signed-off-by: Alexandre Courbot <acourbot@...dia.com>
> ---
> rust/kernel/io.rs | 1 +
> rust/kernel/io/register.rs | 1287 ++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 1288 insertions(+)
>
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> index 056a3ec71647..112f43ecbf88 100644
> --- a/rust/kernel/io.rs
> +++ b/rust/kernel/io.rs
> @@ -11,6 +11,7 @@
>
> pub mod mem;
> pub mod poll;
> +pub mod register;
> pub mod resource;
>
> pub use resource::Resource;
> diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
> new file mode 100644
> index 000000000000..fc85dcd1f09a
> --- /dev/null
> +++ b/rust/kernel/io/register.rs
> @@ -0,0 +1,1287 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! A macro to define register layout and accessors.
> +//!
> +//! A single register typically includes several fields, which are accessed through a combination
> +//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
> +//! not all possible field values are necessarily valid.
> +//!
> +//! The [`register!`] macro in this module provides an intuitive and readable syntax for defining a
> +//! dedicated type for each register. Each such type comes with its own field accessors that can
> +//! return an error if a field's value is invalid.
> +//!
> +//! [`register!`]: kernel::register!
> +
> +use core::ops::Deref;
> +
> +use crate::io::{
> + IoCapable,
> + IoKnownSize, //
> +};
> +
> +/// Trait providing a base address to be added to the offset of a relative register to obtain
> +/// its actual offset.
> +///
> +/// The `T` generic argument is used to distinguish which base to use, in case a type provides
> +/// several bases. It is given to the `register!` macro to restrict the use of the register to
> +/// implementors of this particular variant.
> +pub trait RegisterBase<T> {
> + /// Base address to which register offsets are added.
> + const BASE: usize;
> +}
> +
> +/// Trait providing I/O read/write operations for register storage types.
> +///
> +/// This trait is implemented for all integer types on which I/O can be performed, allowing the
> +/// `register!` macro to generate appropriate I/O accessor methods based on the register's storage
> +/// type.
> +pub trait RegisterIo: Sized {
Is this trait intended for public usage or just internal detail of `register!()`
macro?
If it's the former, then we should probably just put the method into `IoCapable`
and allow generic-read in Io. If it's the latter, let's `#[doc(hidden)]` this so
it won't get abused.
> + /// Read a value from the given offset in the I/O region.
> + fn read<T, I>(io: &T, offset: usize) -> Self
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>;
I think generally `Deref` bound shouldn't be exposed to user. What smart
pointers are involved here, and can we implement forwarding impls of `Io`?
> +
> + /// Write a value to the given offset in the I/O region.
> + fn write<T, I>(self, io: &T, offset: usize)
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>;
> +}
> +
> +impl RegisterIo for u8 {
> + #[inline(always)]
> + fn read<T, I>(io: &T, offset: usize) -> Self
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.read8(offset)
> + }
> +
> + #[inline(always)]
> + fn write<T, I>(self, io: &T, offset: usize)
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.write8(self, offset)
> + }
> +}
> +
> +impl RegisterIo for u16 {
> + #[inline(always)]
> + fn read<T, I>(io: &T, offset: usize) -> Self
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.read16(offset)
> + }
> +
> + #[inline(always)]
> + fn write<T, I>(self, io: &T, offset: usize)
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.write16(self, offset)
> + }
> +}
> +
> +impl RegisterIo for u32 {
> + #[inline(always)]
> + fn read<T, I>(io: &T, offset: usize) -> Self
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.read32(offset)
> + }
> +
> + #[inline(always)]
> + fn write<T, I>(self, io: &T, offset: usize)
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.write32(self, offset)
> + }
> +}
> +
> +#[cfg(CONFIG_64BIT)]
This cfg should be removed.
> +impl RegisterIo for u64 {
> + #[inline(always)]
> + fn read<T, I>(io: &T, offset: usize) -> Self
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.read64(offset)
> + }
> +
> + #[inline(always)]
> + fn write<T, I>(self, io: &T, offset: usize)
> + where
> + T: Deref<Target = I>,
> + I: IoKnownSize + IoCapable<Self>,
> + {
> + io.write64(self, offset)
> + }
> +}
> +
> +/// Defines a dedicated type for a register, including getter and setter methods for its fields and
> +/// methods to read and write it from an [`Io`] region.
> +///
> +/// Example:
> +///
> +/// ```
> +/// use kernel::register;
> +///
> +/// register! {
> +/// /// Basic information about the chip.
> +/// pub BOOT_0(u32) @ 0x00000100 {
> +/// /// Vendor ID.
> +/// 15:8 vendor_id;
> +/// /// Major revision of the chip.
> +/// 7:4 major_revision;
> +/// /// Minor revision of the chip.
> +/// 3:0 minor_revision;
> +/// }
> +/// }
> +/// ```
> +///
> +/// This defines a `BOOT_0` type which can be read from or written to offset `0x100` of an `Io`
> +/// region. For instance, `minor_revision` consists of the 4 least significant bits of the
> +/// register.
> +///
> +/// Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
> +/// getter method, which is named after them. They also have setter methods prefixed with `set_`
> +/// for runtime values and `with_` for constant values. All setters return the updated register
> +/// value.
> +///
> +/// ```no_run
> +/// use kernel::register;
> +/// use kernel::num::Bounded;
> +///
> +/// # register! {
> +/// # pub BOOT_0(u32) @ 0x00000100 {
> +/// # 15:8 vendor_id;
> +/// # 7:4 major_revision;
> +/// # 3:0 minor_revision;
> +/// # }
> +/// # }
> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T) {
> +/// # fn obtain_vendor_id() -> u8 { 0xff }
> +/// // Read from the register's defined offset (0x100).
> +/// let boot0 = BOOT_0::read(&bar);
> +/// pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
> +///
> +/// // Update some fields and write the new value back.
> +/// boot0
> +/// // Constant values.
> +/// .with_major_revision::<3>()
> +/// .with_minor_revision::<10>()
> +/// // Run-time value.
> +/// .set_vendor_id(obtain_vendor_id())
> +/// .write(&bar);
> +///
> +/// // Or, just read and update the register in a single step.
> +/// BOOT_0::update(&bar, |r| r
> +/// .with_major_revision::<3>()
> +/// .with_minor_revision::<10>()
> +/// .set_vendor_id(obtain_vendor_id())
> +/// );
> +///
> +/// // Constant values can also be built using the const setters.
> +/// const V: BOOT_0 = BOOT_0::zeroed()
> +/// .with_major_revision::<3>()
> +/// .with_minor_revision::<10>();
> +/// # }
> +/// ```
> +///
> +/// Fields can also be transparently converted from/to an arbitrary type by using the bitfield `=>`
> +/// and `?=>` syntaxes.
> +///
> +/// If present, doccomments above register or fields definitions are added to the relevant item
> +/// they document (the register type itself, or the field's setter and getter methods).
> +///
> +/// Note that multiple registers can be defined in a single `register!` invocation. This can be
> +/// useful to group related registers together.
> +///
> +/// ```
> +/// use kernel::register;
> +///
> +/// register! {
> +/// pub BOOT_0(u8) @ 0x00000100 {
> +/// 7:4 major_revision;
> +/// 3:0 minor_revision;
> +/// }
> +///
> +/// pub BOOT_1(u8) @ 0x00000101 {
> +/// 7:5 num_threads;
> +/// 4:0 num_cores;
> +/// }
> +/// };
> +/// ```
> +///
> +/// It is possible to create an alias of an existing register with new field definitions by using
> +/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
> +/// the context:
> +///
> +/// ```
> +/// use kernel::register;
> +///
> +/// register! {
> +/// /// Scratch register.
> +/// pub SCRATCH(u32) @ 0x00000200 {
> +/// /// Raw value.
> +/// 31:0 value;
> +/// }
> +///
> +/// /// Boot status of the firmware.
> +/// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
> +/// /// Whether the firmware has completed booting.
> +/// 0:0 completed;
> +/// }
> +/// }
> +/// ```
> +///
> +/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
> +/// providing its own `completed` field.
> +///
> +/// ## Relative registers
> +///
> +/// A register can be defined as being accessible from a fixed offset of a provided base. For
> +/// instance, imagine the following I/O space:
> +///
> +/// ```text
> +/// +-----------------------------+
> +/// | ... |
> +/// | |
> +/// 0x100--->+------------CPU0-------------+
> +/// | |
> +/// 0x110--->+-----------------------------+
> +/// | CPU_CTL |
> +/// +-----------------------------+
> +/// | ... |
> +/// | |
> +/// | |
> +/// 0x200--->+------------CPU1-------------+
> +/// | |
> +/// 0x210--->+-----------------------------+
> +/// | CPU_CTL |
> +/// +-----------------------------+
> +/// | ... |
> +/// +-----------------------------+
> +/// ```
> +///
> +/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
> +/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
> +/// them twice and would prefer a way to select which one to use from a single definition
> +///
> +/// This can be done using the `Base + Offset` syntax when specifying the register's address.
> +///
> +/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
> +/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
> +/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
> +/// into code:
> +///
> +/// ```no_run
> +/// use kernel::register;
> +/// use kernel::io::register::RegisterBase;
> +///
> +/// // Type used to identify the base.
> +/// pub struct CpuCtlBase;
> +///
> +/// // ZST describing `CPU0`.
> +/// struct Cpu0;
> +/// impl RegisterBase<CpuCtlBase> for Cpu0 {
> +/// const BASE: usize = 0x100;
> +/// }
> +/// // Singleton of `CPU0` used to identify it.
> +/// const CPU0: Cpu0 = Cpu0;
> +///
> +/// // ZST describing `CPU1`.
> +/// struct Cpu1;
> +/// impl RegisterBase<CpuCtlBase> for Cpu1 {
> +/// const BASE: usize = 0x200;
> +/// }
> +/// // Singleton of `CPU1` used to identify it.
> +/// const CPU1: Cpu1 = Cpu1;
> +///
> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T) {
> +/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
> +/// register! {
> +/// /// CPU core control.
> +/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
> +/// /// Start the CPU core.
> +/// 0:0 start;
> +/// }
> +/// }
> +///
> +/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
> +/// // that is used to resolve its final address by adding its `BASE` to the offset of the
> +/// // register.
> +///
> +/// // Start `CPU0`.
> +/// CPU_CTL::update(&bar, &CPU0, |r| r.set_start(true));
> +///
> +/// // Start `CPU1`.
> +/// CPU_CTL::update(&bar, &CPU1, |r| r.set_start(true));
> +///
> +/// // Aliases can also be defined for relative register.
> +/// register! {
> +/// /// Alias to CPU core control.
> +/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
> +/// /// Start the aliased CPU core.
> +/// 1:1 alias_start;
> +/// }
> +/// }
> +///
> +/// // Start the aliased `CPU0`.
> +/// CPU_CTL_ALIAS::update(&bar, &CPU0, |r| r.set_alias_start(true));
> +/// # }
> +/// ```
> +///
> +/// ## Arrays of registers
> +///
> +/// Some I/O areas contain consecutive registers that can be interpreted in the same way. These
> +/// areas can be defined as an array of identical registers, allowing them to be accessed by index
> +/// with compile-time or runtime bound checking. Simply specify their size inside `[` and `]`
> +/// brackets, and add an `idx` parameter to their `read`, `write` and `update` methods:
> +///
> +/// ```no_run
> +/// use kernel::register;
> +///
> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T)
> +/// # -> Result<(), Error>{
> +/// # fn get_scratch_idx() -> usize {
> +/// # 0x15
> +/// # }
> +/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
> +/// register! {
> +/// /// Scratch registers.
> +/// pub SCRATCH(u32)[64] @ 0x00000080 {
> +/// 31:0 value;
> +/// }
> +/// }
> +///
> +/// // Read scratch register 0, i.e. I/O address `0x80`.
> +/// let scratch_0 = SCRATCH::read(&bar, 0).value();
> +/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
> +/// let scratch_15 = SCRATCH::read(&bar, 15).value();
> +///
> +/// // This is out of bounds and won't build.
> +/// // let scratch_128 = SCRATCH::read(&bar, 128).value();
> +///
> +/// // Runtime-obtained array index.
> +/// let scratch_idx = get_scratch_idx();
> +/// // Access on a runtime index returns an error if it is out-of-bounds.
> +/// let some_scratch = SCRATCH::try_read(&bar, scratch_idx)?.value();
> +///
> +/// // Alias to a particular register in an array.
> +/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
> +/// register! {
> +/// /// Firmware exit status code.
> +/// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
> +/// 7:0 status;
> +/// }
> +/// }
> +/// let status = FIRMWARE_STATUS::read(&bar).status();
> +///
> +/// // Non-contiguous register arrays can be defined by adding a stride parameter.
> +/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
> +/// // registers of the two declarations below are interleaved.
> +/// register! {
> +/// /// Scratch registers bank 0.
> +/// pub SCRATCH_INTERLEAVED_0(u32)[16 ; 8] @ 0x000000c0 {
> +/// 31:0 value;
> +/// }
> +///
> +/// /// Scratch registers bank 1.
> +/// pub SCRATCH_INTERLEAVED_1(u32)[16 ; 8] @ 0x000000c4 {
> +/// 31:0 value;
> +/// }
> +/// }
> +/// # Ok(())
> +/// # }
> +/// ```
> +///
> +/// ## Relative arrays of registers
> +///
> +/// Combining the two features described in the sections above, arrays of registers accessible from
> +/// a base can also be defined:
> +///
> +/// ```no_run
> +/// use kernel::register;
> +/// use kernel::io::register::RegisterBase;
> +///
> +/// # fn test<T: kernel::io::IoKnownSize + kernel::io::IoCapable<u32>>(bar: &T)
> +/// # -> Result<(), Error>{
> +/// # fn get_scratch_idx() -> usize {
> +/// # 0x15
> +/// # }
> +/// // Type used as parameter of `RegisterBase` to specify the base.
> +/// pub struct CpuCtlBase;
> +///
> +/// // ZST describing `CPU0`.
> +/// struct Cpu0;
> +/// impl RegisterBase<CpuCtlBase> for Cpu0 {
> +/// const BASE: usize = 0x100;
> +/// }
> +/// // Singleton of `CPU0` used to identify it.
> +/// const CPU0: Cpu0 = Cpu0;
> +///
> +/// // ZST describing `CPU1`.
> +/// struct Cpu1;
> +/// impl RegisterBase<CpuCtlBase> for Cpu1 {
> +/// const BASE: usize = 0x200;
> +/// }
> +/// // Singleton of `CPU1` used to identify it.
> +/// const CPU1: Cpu1 = Cpu1;
> +///
> +/// // 64 per-cpu scratch registers, arranged as a contiguous array.
> +/// register! {
> +/// /// Per-CPU scratch registers.
> +/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
> +/// 31:0 value;
> +/// }
> +/// }
> +///
> +/// let cpu0_scratch_0 = CPU_SCRATCH::read(&bar, &Cpu0, 0).value();
> +/// let cpu1_scratch_15 = CPU_SCRATCH::read(&bar, &Cpu1, 15).value();
> +///
> +/// // This won't build.
> +/// // let cpu0_scratch_128 = CPU_SCRATCH::read(&bar, &Cpu0, 128).value();
> +///
> +/// // Runtime-obtained array index.
> +/// let scratch_idx = get_scratch_idx();
> +/// // Access on a runtime value returns an error if it is out-of-bounds.
> +/// let cpu0_some_scratch = CPU_SCRATCH::try_read(&bar, &Cpu0, scratch_idx)?.value();
> +///
> +/// // `SCRATCH[8]` is used to convey the firmware exit code.
> +/// register! {
> +/// /// Per-CPU firmware exit status code.
> +/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
> +/// 7:0 status;
> +/// }
> +/// }
> +/// let cpu0_status = CPU_FIRMWARE_STATUS::read(&bar, &Cpu0).status();
> +///
> +/// // Non-contiguous register arrays can be defined by adding a stride parameter.
> +/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
> +/// // registers of the two declarations below are interleaved.
> +/// register! {
> +/// /// Scratch registers bank 0.
> +/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16 ; 8] @ CpuCtlBase + 0x00000d00 {
I discussed this with Alex off-list and we agree that this'll better be
pub CPU_SCRATCH_INTERLEAVED_0(u32)[u16, stride = 8] @ CpuCtlBase + 0x00000d00
spelling the full intention out rather than have a special syntax, as this isn't
the common case.
> +/// 31:0 value;
> +/// }
> +///
> +/// /// Scratch registers bank 1.
> +/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16 ; 8] @ CpuCtlBase + 0x00000d04 {
> +/// 31:0 value;
> +/// }
> +/// }
> +/// # Ok(())
> +/// # }
> +/// ```
> +/// [`Io`]: kernel::io::Io
> +#[macro_export]
> +macro_rules! register {
> + // Entry point for the macro, allowing multiple registers to be defined in one call.
> + // It matches all possible register declaration patterns to dispatch them to corresponding
> + // `@...` rule that defines a single register.
> + (
> + $(
> + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
> + $([ $size:expr $(; $stride:expr)? ])? $(@ $offset:literal)?
> + $(@ $base:ident + $base_offset:literal)?
Does `$(@ $($base:ident +)? $offset:literal)?` not work?
> + $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
> + { $($fields:tt)* }
> + )*
> + ) => {
> + $(
> + ::kernel::register!(
$crate::register!()
> + @reg $(#[$attr])* $vis $name ($storage) $([$size $(; $stride)?])?
> + $(@ $offset)?
> + $(@ $base + $base_offset)?
> + $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
> + { $($fields)* }
> + );
> + )*
> + };
> +
> + // All the rules below are private helpers.
> +
> + // Creates a register at a fixed offset of the MMIO space.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
> + { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_fixed $name($storage) @ $offset);
> + };
> +
> + // Creates an alias register of fixed offset register `alias` with its own fields.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
> + { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET);
> + };
> +
> + // Creates a register at a relative offset from a base address provider.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
> + { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_relative $name($storage) @ $base + $offset );
> + };
> +
> + // Creates an alias register of relative offset register `alias` with its own fields.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
> + { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_relative $name($storage) @ $base + $alias::OFFSET );
Would this generate error messages if $name and $alias are of different base, or
would such case be a bug but silently compiles?
> + };
> +
> + // Creates an array of registers at a fixed offset of the MMIO space.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
> + [ $size:expr ; $stride:expr ] @ $offset:literal { $($fields:tt)* }
> + ) => {
> + static_assert!(::core::mem::size_of::<$storage>() <= $stride);
> +
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_array $name($storage) [ $size ; $stride ] @ $offset);
> + };
> +
> + // Shortcut for contiguous array of registers (stride == size of element).
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
> + { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + $(#[$attr])* $vis $name($storage) [ $size ; ::core::mem::size_of::<$storage>() ]
> + @ $offset { $($fields)* }
> + );
> + };
> +
> + // Creates an alias of register `idx` of array of registers `alias` with its own fields.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
> + { $($fields:tt)* }
> + ) => {
> + static_assert!($idx < $alias::SIZE);
> +
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET + $idx * $alias::STRIDE);
> + };
> +
> + // Creates an array of registers at a relative offset from a base address provider.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ; $stride:expr ]
> + @ $base:ident + $offset:literal { $($fields:tt)* }
> + ) => {
> + static_assert!(::core::mem::size_of::<$storage>() <= $stride);
> +
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(
> + @io_relative_array $name($storage) [ $size ; $stride ] @ $base + $offset
> + );
> + };
> +
> + // Shortcut for contiguous array of relative registers (stride == size of element).
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
> + @ $base:ident + $offset:literal { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(
> + $(#[$attr])* $vis $name($storage) [ $size ; ::core::mem::size_of::<$storage>() ]
> + @ $base + $offset { $($fields)* }
> + );
> + };
> +
> + // Creates an alias of register `idx` of relative array of registers `alias` with its own
> + // fields.
> + (
> + @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
> + => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
> + ) => {
> + static_assert!($idx < $alias::SIZE);
> +
> + ::kernel::register!(
> + @bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }
> + );
> + ::kernel::register!(
> + @io_relative $name($storage) @ $base + $alias::OFFSET + $idx * $alias::STRIDE
> + );
> + };
> +
> + // Generates the bitfield for the register.
> + //
> + // `#[allow(non_camel_case_types)]` is added since register names typically use SCREAMING_CASE.
> + (
> + @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
> + ) => {
> + ::kernel::register!(@bitfield_core
> + #[allow(non_camel_case_types)]
> + $(#[$attr])* $vis $name $storage
> + );
> + ::kernel::register!(@bitfield_fields $vis $name $storage { $($fields)* });
> + };
> +
> + // Generates the IO accessors for a fixed offset register.
> + (@io_fixed $name:ident ($storage:ty) @ $offset:expr) => {
> + #[allow(dead_code)]
> + impl $name {
> + /// Absolute offset of the register.
> + pub const OFFSET: usize = $offset;
> +
> + /// Read the register from its address in `io`.
> + #[inline(always)]
> + pub fn read<T, I>(io: &T) -> Self where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + Self(<$storage as $crate::io::register::RegisterIo>::read(io, Self::OFFSET))
> + }
> +
> + /// Write the value contained in `self` to the register address in `io`.
> + #[inline(always)]
> + pub fn write<T, I>(self, io: &T) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + <$storage as $crate::io::register::RegisterIo>::write(self.0, io, Self::OFFSET)
> + }
> +
> + /// Read the register from its address in `io` and run `f` on its value to obtain a new
> + /// value to write back.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
Given the non-atomicity, how much value does it provide compared to having the
user write read and write themselves? I feel that people reading the code may
assume the atomicity without reading docs if they see `FOO::update`, while it's
less likely that they do so if they read
`FOO::read(io).with_bar(baz).write(io)`.
> + #[inline(always)]
> + pub fn update<T, I, F>(
> + io: &T,
> + f: F,
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + let reg = f(Self::read(io));
> + reg.write(io);
> + }
> + }
> + };
> +
> + // Generates the IO accessors for a relative offset register.
> + (@io_relative $name:ident ($storage:ty) @ $base:ident + $offset:expr ) => {
> + #[allow(dead_code)]
> + impl $name {
> + /// Relative offset of the register.
> + pub const OFFSET: usize = $offset;
> +
> + /// Read the register from `io`, using the base address provided by `base` and adding
> + /// the register's offset to it.
> + #[inline(always)]
> + pub fn read<T, I, B>(
> + io: &T,
> + #[allow(unused_variables)]
> + base: &B,
> + ) -> Self where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + Self::OFFSET;
> +
> + Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
> + }
> +
> + /// Write the value contained in `self` to `io`, using the base address provided by
> + /// `base` and adding the register's offset to it.
> + #[inline(always)]
> + pub fn write<T, I, B>(
> + self,
> + io: &T,
> + #[allow(unused_variables)]
> + base: &B,
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE + Self::OFFSET;
> +
> + <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
> + }
> +
> + /// Read the register from `io`, using the base address provided by `base` and adding
> + /// the register's offset to it, then run `f` on its value to obtain a new value to
> + /// write back.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
> + #[inline(always)]
> + pub fn update<T, I, B, F>(
> + io: &T,
> + base: &B,
> + f: F,
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + let reg = f(Self::read(io, base));
> + reg.write(io, base);
> + }
> + }
> + };
> +
> + // Generates the IO accessors for an array of registers.
> + (@io_array $name:ident ($storage:ty) [ $size:expr ; $stride:expr ] @ $offset:literal) => {
> + #[allow(dead_code)]
> + impl $name {
> + /// Absolute offset of the register array.
> + pub const OFFSET: usize = $offset;
> + /// Number of elements in the array of registers.
> + pub const SIZE: usize = $size;
> + /// Number of bytes separating each element of the array of registers.
> + pub const STRIDE: usize = $stride;
> +
> + /// Read the array register at index `idx` from its address in `io`.
> + #[inline(always)]
> + pub fn read<T, I>(
> + io: &T,
> + idx: usize,
> + ) -> Self where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + build_assert!(idx < Self::SIZE);
> +
> + let offset = Self::OFFSET + (idx * Self::STRIDE);
> +
> + Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
> + }
> +
> + /// Write the value contained in `self` to the array register with index `idx` in `io`.
> + #[inline(always)]
> + pub fn write<T, I>(
> + self,
> + io: &T,
> + idx: usize
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + build_assert!(idx < Self::SIZE);
> +
> + let offset = Self::OFFSET + (idx * Self::STRIDE);
> +
> + <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
> + }
> +
> + /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
> + /// new value to write back.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
> + #[inline(always)]
> + pub fn update<T, I, F>(
> + io: &T,
> + idx: usize,
> + f: F,
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + let reg = f(Self::read(io, idx));
> + reg.write(io, idx);
> + }
> +
> + /// Read the array register at index `idx` from its address in `io`.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + #[inline(always)]
> + pub fn try_read<T, I>(
> + io: &T,
> + idx: usize,
> + ) -> ::kernel::error::Result<Self> where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + if idx < Self::SIZE {
> + Ok(Self::read(io, idx))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> +
> + /// Write the value contained in `self` to the array register with index `idx` in `io`.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + #[inline(always)]
> + pub fn try_write<T, I>(
> + self,
> + io: &T,
> + idx: usize,
> + ) -> ::kernel::error::Result where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + {
> + if idx < Self::SIZE {
> + Ok(self.write(io, idx))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> +
> + /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
> + /// new value to write back.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
> + #[inline(always)]
> + pub fn try_update<T, I, F>(
> + io: &T,
> + idx: usize,
> + f: F,
> + ) -> ::kernel::error::Result where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + if idx < Self::SIZE {
> + Ok(Self::update(io, idx, f))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> + }
> + };
> +
> + // Generates the IO accessors for an array of relative registers.
> + (
> + @io_relative_array $name:ident ($storage:ty) [ $size:expr ; $stride:expr ]
> + @ $base:ident + $offset:literal
> + ) => {
> + #[allow(dead_code)]
> + impl $name {
> + /// Relative offset of the register array.
> + pub const OFFSET: usize = $offset;
> + /// Number of elements in the array of registers.
> + pub const SIZE: usize = $size;
> + /// Number of bytes separating each element of the array of registers.
> + pub const STRIDE: usize = $stride;
> +
> + /// Read the array register at index `idx` from `io`, using the base address provided
> + /// by `base` and adding the register's offset to it.
> + #[inline(always)]
> + pub fn read<T, I, B>(
> + io: &T,
> + #[allow(unused_variables)]
> + base: &B,
> + idx: usize,
> + ) -> Self where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + build_assert!(idx < Self::SIZE);
> +
> + let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
> + Self::OFFSET + (idx * Self::STRIDE);
> +
> + Self(<$storage as $crate::io::register::RegisterIo>::read(io, offset))
> + }
> +
> + /// Write the value contained in `self` to `io`, using the base address provided by
> + /// `base` and adding the offset of array register `idx` to it.
> + #[inline(always)]
> + pub fn write<T, I, B>(
> + self,
> + io: &T,
> + #[allow(unused_variables)]
> + base: &B,
> + idx: usize
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + build_assert!(idx < Self::SIZE);
> +
> + let offset = <B as $crate::io::register::RegisterBase<$base>>::BASE +
> + Self::OFFSET + (idx * Self::STRIDE);
> +
> + <$storage as $crate::io::register::RegisterIo>::write(self.0, io, offset)
> + }
> +
> + /// Read the array register at index `idx` from `io`, using the base address provided
> + /// by `base` and adding the register's offset to it, then run `f` on its value to
> + /// obtain a new value to write back.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
> + #[inline(always)]
> + pub fn update<T, I, B, F>(
> + io: &T,
> + base: &B,
> + idx: usize,
> + f: F,
> + ) where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + let reg = f(Self::read(io, base, idx));
> + reg.write(io, base, idx);
> + }
> +
> + /// Read the array register at index `idx` from `io`, using the base address provided
> + /// by `base` and adding the register's offset to it.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + #[inline(always)]
> + pub fn try_read<T, I, B>(
> + io: &T,
> + base: &B,
> + idx: usize,
> + ) -> ::kernel::error::Result<Self> where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + if idx < Self::SIZE {
> + Ok(Self::read(io, base, idx))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> +
> + /// Write the value contained in `self` to `io`, using the base address provided by
> + /// `base` and adding the offset of array register `idx` to it.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + #[inline(always)]
> + pub fn try_write<T, I, B>(
> + self,
> + io: &T,
> + base: &B,
> + idx: usize,
> + ) -> ::kernel::error::Result where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + {
> + if idx < Self::SIZE {
> + Ok(self.write(io, base, idx))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> +
> + /// Read the array register at index `idx` from `io`, using the base address provided
> + /// by `base` and adding the register's offset to it, then run `f` on its value to
> + /// obtain a new value to write back.
> + ///
> + /// The validity of `idx` is checked at run-time, and `EINVAL` is returned if the
> + /// access was out-of-bounds.
> + ///
> + /// Note that this operation is not atomic. In concurrent contexts, external
> + /// synchronization may be required to prevent race conditions.
> + #[inline(always)]
> + pub fn try_update<T, I, B, F>(
> + io: &T,
> + base: &B,
> + idx: usize,
> + f: F,
> + ) -> ::kernel::error::Result where
> + T: ::core::ops::Deref<Target = I>,
> + I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<$storage>,
> + B: $crate::io::register::RegisterBase<$base>,
> + F: ::core::ops::FnOnce(Self) -> Self,
> + {
> + if idx < Self::SIZE {
> + Ok(Self::update(io, base, idx, f))
> + } else {
> + Err(::kernel::error::code::EINVAL)
> + }
> + }
> + }
> + };
> +
> + // Defines the wrapper `$name` type and its conversions from/to the storage type.
> + (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
> + $(#[$attr])*
> + #[repr(transparent)]
> + #[derive(Clone, Copy, PartialEq, Eq)]
You can derive `Zeroable` and save you an unsafe impl.
> + $vis struct $name($storage);
> +
> + #[allow(dead_code)]
> + impl $name {
> + /// Creates a bitfield from a raw value.
> + $vis const fn from_raw(value: $storage) -> Self {
> + Self(value)
> + }
> +
> + /// Creates a zeroed bitfield value.
> + ///
> + /// This is a const alternative to the `Zeroable::zeroed()` trait method.
> + $vis const fn zeroed() -> Self {
> + Self(0)
> + }
All types that impl `Zeroable` automatically have the `::zeroed()` fn provided
via the trait.
> +
> + /// Returns the raw value of this bitfield.
> + ///
> + /// This is similar to the [`From`] implementation, but is shorter to invoke in
> + /// most cases.
> + $vis const fn as_raw(self) -> $storage {
> + self.0
> + }
> + }
> +
> + // SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
> + unsafe impl ::pin_init::Zeroable for $name {}
> +
> + impl ::core::convert::From<$name> for $storage {
> + fn from(val: $name) -> $storage {
> + val.as_raw()
> + }
> + }
> +
> + impl ::core::convert::From<$storage> for $name {
> + fn from(val: $storage) -> $name {
> + Self::from_raw(val)
> + }
> + }
> + };
> +
> + // Definitions requiring knowledge of individual fields: private and public field accessors,
> + // and `Debug` implementation.
> + (@bitfield_fields $vis:vis $name:ident $storage:ty {
> + $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
> + $(?=> $try_into_type:ty)?
> + $(=> $into_type:ty)?
> + ;
> + )*
> + }
> + ) => {
> + #[allow(dead_code)]
> + impl $name {
> + $(
> + ::kernel::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
> + ::kernel::register!(
> + @public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
> + $(?=> $try_into_type)?
> + $(=> $into_type)?
> + );
> + )*
> + }
> +
> + ::kernel::register!(@debug $name { $($field;)* });
> + };
> +
> + // Private field accessors working with the correct `Bounded` type for the field.
> + (
> + @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
> + ) => {
> + ::kernel::macros::paste!(
> + $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
Is this used by anything?
Best,
Gary
> + $vis const [<$field:upper _MASK>]: $storage =
> + ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
> + $vis const [<$field:upper _SHIFT>]: u32 = $lo;
> + );
> +
> + ::kernel::macros::paste!(
> + fn [<__ $field>](self) ->
> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
> + // Left shift to align the field's MSB with the storage MSB.
> + const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
> + // Right shift to move the top-aligned field to bit 0 of the storage.
> + const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
> +
> + // Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
> + // output type.
> + let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
> + self.0 << ALIGN_TOP
> + );
> + val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
> + }
> +
> + const fn [<__set_ $field>](
> + mut self,
> + value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
> + ) -> Self
> + {
> + const MASK: $storage = $name::[<$field:upper _MASK>];
> + const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
> +
> + let value = value.into_inner() << SHIFT;
> + self.0 = (self.0 & !MASK) | value;
> +
> + self
> + }
> + );
> + };
> +
> + // Public accessors for fields infallibly (`=>`) converted to a type.
> + (
> + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
> + $hi:literal:$lo:literal $field:ident => $into_type:ty
> + ) => {
> + ::kernel::macros::paste!(
> +
> + $(#[doc = $doc])*
> + #[doc = "Returns the value of this field."]
> + #[inline(always)]
> + $vis fn $field(self) -> $into_type
> + {
> + self.[<__ $field>]().into()
> + }
> +
> + $(#[doc = $doc])*
> + #[doc = "Sets this field to the given `value`."]
> + #[inline(always)]
> + $vis fn [<set_ $field>](self, value: $into_type) -> Self
> + {
> + self.[<__set_ $field>](value.into())
> + }
> +
> + );
> + };
> +
> + // Public accessors for fields fallibly (`?=>`) converted to a type.
> + (
> + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
> + $hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
> + ) => {
> + ::kernel::macros::paste!(
> +
> + $(#[doc = $doc])*
> + #[doc = "Returns the value of this field."]
> + #[inline(always)]
> + $vis fn $field(self) ->
> + Result<
> + $try_into_type,
> + <$try_into_type as ::core::convert::TryFrom<
> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
> + >>::Error
> + >
> + {
> + self.[<__ $field>]().try_into()
> + }
> +
> + $(#[doc = $doc])*
> + #[doc = "Sets this field to the given `value`."]
> + #[inline(always)]
> + $vis fn [<set_ $field>](self, value: $try_into_type) -> Self
> + {
> + self.[<__set_ $field>](value.into())
> + }
> +
> + );
> + };
> +
> + // Public accessors for fields not converted to a type.
> + (
> + @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
> + $hi:tt:$lo:tt $field:ident
> + ) => {
> + ::kernel::macros::paste!(
> +
> + $(#[doc = $doc])*
> + #[doc = "Returns the value of this field."]
> + #[inline(always)]
> + $vis fn $field(self) ->
> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
> + {
> + self.[<__ $field>]()
> + }
> +
> + $(#[doc = $doc])*
> + #[doc = "Sets this field to the compile-time constant `VALUE`."]
> + #[inline(always)]
> + $vis const fn [<with_ $field>]<const VALUE: $storage>(self) -> Self {
> + self.[<__set_ $field>](
> + ::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
> + )
> + }
> +
> + $(#[doc = $doc])*
> + #[doc = "Sets this field to the given `value`."]
> + #[inline(always)]
> + $vis fn [<set_ $field>]<T>(
> + self,
> + value: T,
> + ) -> Self
> + where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
> + {
> + self.[<__set_ $field>](value.into())
> + }
> +
> + $(#[doc = $doc])*
> + #[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
> + #[inline(always)]
> + $vis fn [<try_set_ $field>]<T>(
> + self,
> + value: T,
> + ) -> ::kernel::error::Result<Self>
> + where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
> + {
> + Ok(
> + self.[<__set_ $field>](
> + value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
> + )
> + )
> + }
> +
> + );
> + };
> +
> + // `Debug` implementation.
> + (@debug $name:ident { $($field:ident;)* }) => {
> + impl ::kernel::fmt::Debug for $name {
> + fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
> + f.debug_struct(stringify!($name))
> + .field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.0))
> + $(
> + .field(stringify!($field), &self.$field())
> + )*
> + .finish()
> + }
> + }
> + };
> +}
Powered by blists - more mailing lists