[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DG0WY7S2QPQM.37S6PROC464QI@nvidia.com>
Date: Thu, 29 Jan 2026 17:00:41 +0900
From: "Alexandre Courbot" <acourbot@...dia.com>
To: "Gary Guo" <gary@...yguo.net>
Cc: "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>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>, "Benno Lossin"
<lossin@...nel.org>, "Andreas Hindborg" <a.hindborg@...nel.org>, "Trevor
Gross" <tmgross@...ch.edu>, "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 Thu Jan 29, 2026 at 1:16 AM JST, Gary Guo wrote:
> 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.
It is an internal detail of `register!()` and not supposed to be used by anyone
else.
>
>> + /// 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`?
Removing the requirement for `Deref` in the `RegisterIo` trait is simple - we
can just call `Deref` in the register IO accessors.
But I suspect you mean that we should also remove it from the register
accessors themselves? The problem if I remove it there is that things like
Nova's `Bar0` do not implement `IoKnownSize` and `IoCapable` (but rather deref
to something that does), which would require all callers to do the deref op
itself as deref coercion doesn't seem to be usable here.
>
>> +
>> + /// 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.
Done.
>
>> +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.
Implemented it and it indeed looks much better.
>
>> +/// 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?
It does! I was trying to match the `+` in the right-side expression and
obviously couldn't, but it didn't occur to me to do it the other way. :)
>
>> + $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
>> + { $($fields:tt)* }
>> + )*
>> + ) => {
>> + $(
>> + ::kernel::register!(
>
> $crate::register!()
Updated throughout the file (I am not sure to understand the difference though?).
>
>> + @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?
You would get an build error when trying to use the alias as the bases
are dedicated types and the types would be incompatible.
>
>> + };
>> +
>> + // 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)`.
It is just a convenience method. I cannot pretend it is widely used, but
it allows to turn multiple-step ops into one-liners.
>
>> + #[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.
Unfortunately this happens if I try to do it:
error: no rules expected `(`
--> ../samples/rust/rust_driver_pci.rs:74:9
|
74 | / ::kernel::register! {
75 | | VENDOR_ID(u16) @ 0x0 {
76 | | 15:0 vendor_id;
... |
86 | | }
| |_________^ no rules expected this token in macro call
Not quite sure why to be honest.
>
>> + $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.
Yes, but that method from the trait cannot be used in const context, and
`zeroed` is the starting point for building register values from scratch (and
thus constant values).
>
>> +
>> + /// 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?
Not yet. This is intended for user code that wants to know which bits are used
by a specific field. I don't mind removing it though.
Thanks for the review and ideas!
Powered by blists - more mailing lists