Skip to main content

spmi_hwreg/
common.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
6
7/// Trait for types that can represent a register's value.
8///
9/// It is implemented by the `Value` structs generated by the
10/// `spmi_register!` macro, bounding it by Sized, FromBytes, IntoBytes,
11/// FromZeros, and Immutable for safe memory casting.
12pub trait RegisterValue: Sized + FromBytes + IntoBytes + FromZeros + Immutable {}
13
14/// Trait implemented by register blocks to provide metadata like address unit size.
15pub trait RegisterBlock {
16    /// The size in bytes of an address unit for this register block.
17    /// Defaults to 1 (byte-addressed).
18    const ADDRESS_UNIT_BYTES: u16 = 1;
19}
20
21/// Helper function to retrieve `ADDRESS_UNIT_BYTES` for a register block as a `const fn`.
22#[doc(hidden)]
23#[inline(always)]
24pub const fn block_address_unit_bytes<B: RegisterBlock>(_: &B) -> u16 {
25    B::ADDRESS_UNIT_BYTES
26}
27
28/// Base trait implemented by register definitions providing device-independent metadata.
29pub trait SpmiRegisterDef {
30    /// The type representing the value of this register.
31    type Value: RegisterValue;
32
33    /// The typestate access mode marker for this register.
34    type Mode;
35
36    /// The address of this register.
37    const ADDRESS: u16;
38}
39
40/// Trait implemented by register definitions to provide type-safe access.
41///
42/// `D` is the device type (e.g., `DeviceType` or custom wrapper implementing [`SpmiDevice`]).
43pub trait SpmiRegister<D>: SpmiRegisterDef {
44    /// The type representing the accessor for this register.
45    type Accessor<'a>
46    where
47        Self: 'a,
48        D: 'a;
49
50    /// Constructs the accessor for this register.
51    fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a>;
52}
53
54/// Helper function that triggers the compile-time contiguity assertion.
55#[doc(hidden)]
56#[inline(always)]
57pub const fn assert_contiguous<B, R1, R2>(_: &B)
58where
59    B: RegisterBlock,
60    R1: SpmiRegisterDef,
61    R2: SpmiRegisterDef,
62{
63    struct ContiguityCheck<B, R1, R2>(std::marker::PhantomData<(B, R1, R2)>);
64
65    impl<B, R1, R2> ContiguityCheck<B, R1, R2>
66    where
67        B: RegisterBlock,
68        R1: SpmiRegisterDef,
69        R2: SpmiRegisterDef,
70    {
71        const ASSERT: () = {
72            let unit = B::ADDRESS_UNIT_BYTES as u32;
73            let val_size = std::mem::size_of::<R1::Value>() as u32;
74            let r1_addr = R1::ADDRESS as u32;
75            let r2_addr = R2::ADDRESS as u32;
76            let step = (val_size + unit - 1) / unit;
77            assert!(r2_addr == r1_addr + step, "Registers are not contiguous");
78        };
79    }
80
81    let _ = ContiguityCheck::<B, R1, R2>::ASSERT;
82}
83
84// --- Typestate Access Modes ---
85// These markers are used to enforce register access rules
86// (Read-Only, Write-Only, Read-Write) at compile time using
87// the typestate pattern.
88
89/// Marker struct representing Read-Only register access.
90pub struct ReadOnly;
91
92/// Marker struct representing Write-Only register access.
93pub struct WriteOnly;
94
95/// Marker struct representing Read-Write register access.
96pub struct ReadWrite;
97
98/// Trait implemented by access modes that allow reading.
99///
100/// This is used as a generic bound on `Register::read` to prevent compile-time
101/// errors when attempting to read from write-only registers.
102pub trait Readable {}
103impl Readable for ReadOnly {}
104impl Readable for ReadWrite {}
105
106/// Trait implemented by access modes that allow writing.
107///
108/// This is used as a generic bound on `Register::write` to prevent compile-time
109/// errors when attempting to write to read-only registers.
110pub trait Writable {}
111impl Writable for WriteOnly {}
112impl Writable for ReadWrite {}
113
114/// A generic register accessor.
115///
116/// This struct provides type-safe access to a specific hardware register.
117/// It is parameterized by:
118/// * `T` - The typed `RegisterValue` (usually the generated `Value` struct).
119/// * `M` - The access mode (`ReadOnly`, `WriteOnly`, or `ReadWrite`).
120/// * `D` - The underlying device implementation (implements `SpmiDevice`).
121/// * `ADDR` - The constant hardware address of the register.
122pub struct _Register<'a, T, M, D, const ADDR: u16> {
123    /// The underlying SPMI device.
124    pub spmi: &'a D,
125    _marker: std::marker::PhantomData<(T, M)>,
126}
127
128impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR> {
129    /// Creates a new register accessor.
130    pub fn new(spmi: &'a D) -> Self {
131        Self { spmi, _marker: std::marker::PhantomData }
132    }
133}
134
135// Inherent read method available ONLY if M is Readable and D is SpmiDevice
136impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
137where
138    M: Readable,
139    T: RegisterValue,
140    D: SpmiDevice,
141{
142    /// Reads the register value from the hardware.
143    ///
144    /// This method performs an asynchronous read operation on the underlying
145    /// SPMI device and deserializes the raw bytes into the typed value `T`.
146    pub async fn read(&self) -> Result<T, crate::Error> {
147        let size = std::mem::size_of::<T>();
148        let bytes = self.spmi.read_reg(ADDR, size as u32).await?;
149        T::read_from_bytes(&bytes).map_err(|_| crate::Error::SizeMismatch)
150    }
151}
152
153// Inherent write method available ONLY if M is Writable and D is SpmiDevice
154impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
155where
156    M: Writable,
157    T: RegisterValue,
158    D: SpmiDevice,
159{
160    /// Writes the register value to the hardware.
161    ///
162    /// This method serializes the typed value `T` into raw bytes and performs
163    /// an asynchronous write operation on the underlying SPMI device.
164    pub async fn write(&self, val: T) -> Result<(), crate::Error> {
165        let bytes = val.as_bytes();
166        self.spmi.write_reg(ADDR, bytes).await?;
167        Ok(())
168    }
169}
170
171/// Defines a module for accessing a single SPMI hardware register.
172///
173/// This macro generates a public module named `$name` containing:
174/// -   `ADDRESS`: A `u16` constant for the register address.
175/// -   `Value`: A struct wrapping the raw register value (`$value_type`)
176///     and providing const-fn accessors for defined fields.
177/// -   `Register<'a>`: A struct with an asynchronous `read` and
178///     potentially `write` method to interact with the SPMI device.
179///
180/// # Arguments
181///
182/// 1.  `$name`: The identifier for the generated module.
183/// 2.  `$value_type:ty`: The Rust integer type representing the register's
184///     width (e.g., `u8`, `u16`, `u32`).
185/// 3.  `$addr:expr`: The base address of the register as a `u16`.
186/// 4.  `$mode:ident`: The access mode, one of `RO` (Read Only), `WO` (Write
187///     Only), or `RW` (Read/Write).
188/// 5.  `$endianness:ident`: The endianness of the register. Required for `u16`
189///     and larger. Can be `LE` (Little Endian) or `BE` (Big Endian). For `u8`
190///     registers, this argument is omitted because endianness is irrelevant
191///     for `u8` registers.
192/// 6.  `{ ... }`: A block defining the fields within the register. Each
193///     field definition ends with a semicolon. The following formats are
194///     supported within the block:
195///     *   **Single-bit field:** `$vis $field_name $(, $setter_name)? : $bit;`
196///         -   `$vis`: Visibility (e.g., `pub`).
197///         -   `$field_name`: The name of the getter method (returns `bool`).
198///         -   `$setter_name`: (Optional) The name of the setter method
199///             (takes `bool`, returns `Self`).
200///         -   `$bit_index`: The index of the single bit (e.g., `7`).
201///         Example: `pub enable, set_enable: 7;`
202///
203///     *   **Multi-bit field:** `$vis $field_name $(, $setter_name)? \
204///         : $msb, $lsb;`
205///         -   `$vis`: Visibility.
206///         -   `$field_name`: The getter method (returns `$value_type`).
207///         -   `$setter_name`: (Optional) The setter method (takes
208///             `$value_type`, returns `Self`).
209///         -   `$msb`: The Most Significant Bit index.
210///         -   `$lsb`: The Least Significant Bit index.
211///         Example: `pub field_val, set_field_val: 5, 2;`
212///
213///     *   **Enum field (external type):** `$vis enum $enum_type, $field_name \
214///         $(, $setter_name)? : $msb, $lsb;`
215///         -   `$vis`: Visibility.
216///         -   `$enum_type`: The path to an existing enum type (e.g.,
217///             `super::PowerMode`). This enum must have a `pub const fn \
218///             from_val(val: $value_type) -> Self` associated function.
219///         -   `$field_name`: The getter method (returns `$enum_type`).
220///         -   `$setter_name`: (Optional) The setter method (takes
221///             `$enum_type`, returns `Self`).
222///         -   `$msb`, `$lsb`: The bit range.
223///         Example: `pub enum super::PowerMode, mode, set_mode: 3, 2;`
224///
225///     *   **In-line Enum field:** `$vis enum $enum_name { ... }, $field_name \
226///         $(, $setter_name)? : $msb, $lsb;`
227///         -   `$vis`: Visibility.
228///         -   `$enum_name`: The name for the enum type, defined within
229///             the generated module.
230///         -   `{ ... }`: The enum variants and their `$value_type` values.
231///         -   `$field_name`: The getter method (returns `Result<$enum_name, \
232///             $value_type>`).
233///         -   `$setter_name`: (Optional) The setter method (takes
234///             `$enum_name`, returns `Self`).
235///         -   `$msb`, `$lsb`: The bit range.
236///         Example: `pub enum InlineMode { A = 0, B = 1 }, \
237///             mode, set_mode: 1, 0;`
238///
239///     *   **Custom constant:** `pub const $const_name : $type = $val;`
240///         -   Allows defining constants within the generated register module.
241///         Example: `pub const MAX_VALUE: u8 = 0xFF;`
242///
243/// # Examples
244///
245/// ```
246/// // Define a register at address 0x10, 8-bit, Read/Write,
247/// // Little Endian (default)
248/// spmi_register! {
249///     my_reg, u8, 0x10, RW, {
250///         // Single bit flag
251///         pub enable, set_enable: 7;
252///         // 4-bit field
253///         pub value, set_value: 3, 0;
254///         // Inline enum field
255///         pub enum Status {
256///             Idle = 0,
257///             Active = 1,
258///             Error = 2,
259///         }, status, set_status: 6, 5;
260///     }
261/// }
262///
263/// // Define a register at address 0x20, 16-bit, Read Only, Big Endian
264/// spmi_register! {
265///     status_be_reg, u16, 0x20, RO, BE, {
266///         pub error_code: 15, 8;
267///         pub ready: 0;
268///     }
269/// }
270/// ```
271#[macro_export]
272macro_rules! spmi_register {
273    // Helper to map mode to marker types
274    (@mode_type RO) => { $crate::ReadOnly };
275    (@mode_type WO) => { $crate::WriteOnly };
276    (@mode_type RW) => { $crate::ReadWrite };
277
278    // Explicit-endian byteorder helpers inside macro
279    (@byteorder_type u8, $endianness:ident) => { u8 };
280    (@byteorder_type u16, LE) => { $crate::U16<$crate::LittleEndian> };
281    (@byteorder_type u16, BE) => { $crate::U16<$crate::BigEndian> };
282    (@byteorder_type u32, LE) => { $crate::U32<$crate::LittleEndian> };
283    (@byteorder_type u32, BE) => { $crate::U32<$crate::BigEndian> };
284
285    (@new u8, $endianness:ident, $val:expr) => { Self($val) };
286    (@new u16, $endianness:ident, $val:expr) => {
287        Self($crate::U16::new($val))
288    };
289    (@new u32, $endianness:ident, $val:expr) => {
290        Self($crate::U32::new($val))
291    };
292
293    (@get u8, $endianness:ident, $val:expr) => { $val };
294    (@get u16, $endianness:ident, $val:expr) => { $val.get() };
295    (@get u32, $endianness:ident, $val:expr) => { $val.get() };
296
297    // Explicit endianness provided via single arm
298    (
299        $name:ident,
300        $value_type:ident,
301        $addr:expr,
302        $mode:ident,
303        $endianness:ident,
304        {
305            $($tail:tt)*
306        }
307    ) => {
308        #[allow(unused_imports)]
309        #[allow(dead_code)]
310        pub mod $name {
311            use super::*;
312            use $crate::zerocopy_reexport as zerocopy;
313
314            /// The address of the register.
315            pub const ADDRESS: u16 = $addr;
316
317            $crate::spmi_register_extract_enums!($value_type, $($tail)*);
318
319            /// Represents the value of the register.
320            #[derive(
321                Copy,
322                Clone,
323                Debug,
324                PartialEq,
325                Eq,
326                zerocopy::FromBytes,
327                zerocopy::IntoBytes,
328                zerocopy::Immutable,
329            )]
330            #[repr(transparent)]
331            pub struct Value(
332                pub spmi_register!(@byteorder_type $value_type, $endianness),
333            );
334
335            impl Value {
336                pub const fn new(val: $value_type) -> Self {
337                    spmi_register!(@new $value_type, $endianness, val)
338                }
339                pub const fn reg_value(&self) -> $value_type {
340                    spmi_register!(@get $value_type, $endianness, self.0)
341                }
342
343                /// Reconstructs a typed `Value` from a slice of raw bytes.
344                pub fn from_bytes(bytes: &[u8]) -> Result<Self, $crate::Error> {
345                    zerocopy::FromBytes::read_from_bytes(bytes)
346                        .map_err(|_| $crate::Error::SizeMismatch)
347                }
348
349                /// Converts the `Value` into its raw byte representation.
350                pub fn to_bytes(
351                    &self,
352                ) -> [u8; std::mem::size_of::<$value_type>()] {
353                    let mut arr = [0u8; std::mem::size_of::<$value_type>()];
354                    arr.copy_from_slice($crate::IntoBytes::as_bytes(self));
355                    arr
356                }
357
358                $crate::spmi_register_fields!($value_type, $($tail)*);
359            }
360
361            impl $crate::RegisterValue for Value {}
362
363            impl Default for Value {
364                fn default() -> Self {
365                    Self::new(0 as $value_type)
366                }
367            }
368
369            impl $crate::SpmiRegisterDef for Value {
370                type Value = Self;
371                type Mode = spmi_register!(@mode_type $mode);
372                const ADDRESS: u16 = $addr;
373            }
374
375            impl<D> $crate::SpmiRegister<D> for Value {
376                type Accessor<'a> =
377                    $crate::_Register<'a, Self, spmi_register!(@mode_type $mode), D, $addr>
378                where
379                    Self: 'a,
380                    D: 'a;
381
382                fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
383                    $crate::_Register::new(spmi)
384                }
385            }
386
387            /// The register accessor type.
388            pub type Register<'a, D = $crate::DeviceType> =
389                $crate::_Register<'a, Value, spmi_register!(@mode_type $mode), D, $addr>;
390        }
391    };
392    // Specialized arm for u8 where endianness is irrelevant
393    (
394        $name:ident, u8, $addr:expr, $mode:ident, {
395            $($tail:tt)*
396        }
397    ) => {
398        spmi_register!($name, u8, $addr, $mode, LE, { $($tail)* });
399    };
400    (@is_big BE) => { true };
401    (@is_big LE) => { false };
402}
403
404/// Helper macro for `spmi_register!` to extract inline enums.
405#[macro_export]
406#[doc(hidden)]
407macro_rules! spmi_register_extract_enums {
408    // In-line Enum
409    (
410        $value_type:ty,
411        $(#[$attr:meta])*
412        $vis:vis enum $enum_name:ident {
413            $( $variant_name:ident = $variant_val:expr ),* $(,)?
414        },
415        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
416        $($tail:tt)*
417    ) => {
418        #[derive(Debug, PartialEq, Eq, Copy, Clone)]
419        #[repr($value_type)]
420        $vis enum $enum_name {
421            $( $variant_name = $variant_val ),*
422        }
423        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
424    };
425
426    // Forwarding other cases
427    (
428        $value_type:ty,
429        $(#[$attr:meta])*
430        $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
431        $($tail:tt)*
432    ) => {
433        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
434    };
435    (
436        $value_type:ty,
437        $(#[$attr:meta])*
438        $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
439        $($tail:tt)*
440    ) => {
441        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
442    };
443    (
444        $value_type:ty,
445        $(#[$attr:meta])*
446        $vis:vis enum $enum_type:ty,
447        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
448        $($tail:tt)*
449    ) => {
450        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
451    };
452    (
453        $value_type:ty,
454        pub const $name:ident : $type:ty = $val:expr;
455        $($tail:tt)*
456    ) => {
457        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
458    };
459    ($value_type:ty) => {};
460    ($value_type:ty,) => {};
461}
462
463/// Helper macro for `spmi_register!` to generate field accessors.
464#[macro_export]
465#[doc(hidden)]
466macro_rules! spmi_register_fields {
467    // Terminating cases
468    ($value_type:ty) => {};
469    ($value_type:ty,) => {};
470
471    // 1. Single-bit field
472    (
473        $value_type:ty,
474        $(#[$attr:meta])*
475        $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
476        $($tail:tt)*
477    ) => {
478        $(#[$attr])*
479        #[allow(non_snake_case)]
480        #[allow(dead_code)]
481        $vis const fn $field(&self) -> bool {
482            const _: () = assert!(
483                $bit < <$value_type>::BITS as u8,
484                "Bit index out of bounds"
485            );
486            let bit_mask = (1 as $value_type) << $bit;
487            (self.reg_value() & bit_mask) != 0
488        }
489        $(
490            #[allow(non_snake_case)]
491            #[allow(dead_code)]
492            $vis const fn $setter(self, val: bool) -> Self {
493                const _: () = assert!(
494                    $bit < <$value_type>::BITS as u8,
495                    "Bit index out of bounds"
496                );
497                let bit_mask = (1 as $value_type) << $bit;
498                let raw = (self.reg_value() & !bit_mask)
499                    | ((val as $value_type) << $bit);
500                Self::new(raw)
501            }
502        )?
503        $crate::spmi_register_fields!($value_type, $($tail)*);
504    };
505
506    // 2. Multi-bit field
507    (
508        $value_type:ty,
509        $(#[$attr:meta])*
510        $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
511        $($tail:tt)*
512    ) => {
513        $(#[$attr])*
514        #[allow(non_snake_case)]
515        #[allow(dead_code)]
516        $vis const fn $field(&self) -> $value_type {
517            const _: () = assert!(
518                $msb < <$value_type>::BITS as u8,
519                "MSB index out of bounds"
520            );
521            const _: () = assert!(
522                $lsb < $msb,
523                "LSB must be strictly less than MSB. \
524                 Use single-bit syntax (e.g., 'field: bit;') for 1-bit fields."
525            );
526            let bit_count = $msb - $lsb + 1;
527            let bit_mask = ((!0 as $value_type)
528                >> (<$value_type>::BITS as u8 - bit_count))
529                << $lsb;
530            (self.reg_value() & bit_mask) >> $lsb
531        }
532        $(
533            #[allow(non_snake_case)]
534            #[allow(dead_code)]
535            $vis const fn $setter(self, val: $value_type) -> Self {
536                const _: () = assert!(
537                    $msb < <$value_type>::BITS as u8,
538                    "MSB index out of bounds"
539                );
540                const _: () = assert!(
541                    $lsb < $msb,
542                    "LSB must be strictly less than MSB. \
543                     Use single-bit syntax (e.g., 'field: bit;') \
544                     for 1-bit fields."
545                );
546                let bit_count = $msb - $lsb + 1;
547                let bit_mask = ((!0 as $value_type)
548                    >> (<$value_type>::BITS as u8 - bit_count))
549                    << $lsb;
550                let raw = (self.reg_value() & !bit_mask)
551                    | ((val << $lsb) & bit_mask);
552                Self::new(raw)
553            }
554        )?
555        $crate::spmi_register_fields!($value_type, $($tail)*);
556    };
557
558    // 3. Enum field
559    (
560        $value_type:ty,
561        $(#[$attr:meta])*
562        $vis:vis enum $enum_type:ty,
563        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
564        $($tail:tt)*
565    ) => {
566        $(#[$attr])*
567        #[allow(non_snake_case)]
568        #[allow(dead_code)]
569        $vis const fn $field(&self) -> $enum_type {
570            const _: () = assert!(
571                $msb < <$value_type>::BITS as u8,
572                "MSB index out of bounds"
573            );
574            const _: () = assert!(
575                $lsb <= $msb,
576                "LSB must be less than or equal to MSB"
577            );
578            let bit_count = $msb - $lsb + 1;
579            let bit_mask = ((!0 as $value_type)
580                >> (<$value_type>::BITS as u8 - bit_count))
581                << $lsb;
582            <$enum_type>::from_val((self.reg_value() & bit_mask) >> $lsb)
583        }
584        $(
585            #[allow(non_snake_case)]
586            #[allow(dead_code)]
587            $vis const fn $setter(self, val: $enum_type) -> Self {
588                const _: () = assert!(
589                    $msb < <$value_type>::BITS as u8,
590                    "MSB index out of bounds"
591                );
592                const _: () = assert!(
593                    $lsb <= $msb,
594                    "LSB must be less than or equal to MSB"
595                );
596                let bit_count = $msb - $lsb + 1;
597                let bit_mask = ((!0 as $value_type)
598                    >> (<$value_type>::BITS as u8 - bit_count))
599                    << $lsb;
600                let raw = (self.reg_value() & !bit_mask)
601                    | (((val as $value_type) << $lsb) & bit_mask);
602                Self::new(raw)
603            }
604        )?
605        $crate::spmi_register_fields!($value_type, $($tail)*);
606    };
607
608    // 4. In-line Enum field
609    (
610        $value_type:ty,
611        $(#[$attr:meta])*
612        $vis:vis enum $enum_name:ident {
613            $( $variant_name:ident = $variant_val:expr ),* $(,)?
614        },
615        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
616        $($tail:tt)*
617    ) => {
618        $(#[$attr])*
619        #[allow(non_snake_case)]
620        #[allow(dead_code)]
621        $vis const fn $field(&self) -> Result<$enum_name, $value_type> {
622            const _: () = assert!(
623                $msb < <$value_type>::BITS as u8,
624                "MSB index out of bounds"
625            );
626            const _: () = assert!(
627                $lsb <= $msb,
628                "LSB must be less than or equal to MSB"
629            );
630            let bit_count = $msb - $lsb + 1;
631            let bit_mask = ((!0 as $value_type)
632                >> (<$value_type>::BITS as u8 - bit_count))
633                << $lsb;
634            let val = (self.reg_value() & bit_mask) >> $lsb;
635            $(
636                if val == $enum_name::$variant_name as $value_type {
637                    return Ok($enum_name::$variant_name);
638                }
639            )*
640            Err(val)
641        }
642        $(
643            #[allow(non_snake_case)]
644            #[allow(dead_code)]
645            $vis const fn $setter(self, val: $enum_name) -> Self {
646                const _: () = assert!(
647                    $msb < <$value_type>::BITS as u8,
648                    "MSB index out of bounds"
649                );
650                const _: () = assert!(
651                    $lsb <= $msb,
652                    "LSB must be less than or equal to MSB"
653                );
654                let bit_count = $msb - $lsb + 1;
655                let bit_mask = ((!0 as $value_type)
656                    >> (<$value_type>::BITS as u8 - bit_count))
657                    << $lsb;
658                let raw = (self.reg_value() & !bit_mask)
659                    | (((val as $value_type) << $lsb) & bit_mask);
660                Self::new(raw)
661            }
662        )?
663        $crate::spmi_register_fields!($value_type, $($tail)*);
664    };
665
666    // 5. Custom constant
667    (
668        $value_type:ty,
669        pub const $name:ident : $type:ty = $val:expr;
670        $($tail:tt)*
671    ) => {
672        pub const $name: $type = $val;
673        $crate::spmi_register_fields!($value_type, $($tail)*);
674    };
675}
676
677/// Verifies at compile-time that a list of registers is contiguous.
678#[macro_export]
679#[doc(hidden)]
680macro_rules! assert_contiguous {
681    ($regs:expr, $prev:ident, $curr:ident $(, $rest:ident)*) => {
682        $crate::assert_contiguous::<_, $prev::Value, $curr::Value>($regs);
683        $crate::assert_contiguous!($regs, $curr $(, $rest)*);
684    };
685    ($regs:expr, $last:ident) => {};
686}
687
688/// Defines a struct to group multiple SPMI registers.
689///
690/// This macro generates a public struct named `$name` that holds a
691/// device client and provides methods to access individual registers
692/// defined by `spmi_register!`, as well as `read_bulk` and `write_bulk`
693/// methods for contiguous accesses.
694///
695/// # Arguments
696///
697/// 1.  `address_unit: $unit_type:ident` (optional): Specifies the type representing an address unit
698///     for this block (e.g. `u8` for byte-addressed [default], or `u16` for word-addressed).
699/// 2.  `$name`: The identifier for the generated register block struct.
700/// 3.  `{ ... }`: A block defining the registers contained within this block.
701///     The format of each register definition is:
702///     `$vis $field_name => $reg_mod,`
703///     -   `$vis`: Visibility of the register accessor method (e.g., `pub`).
704///     -   `$field_name`: The name of the method generated on the block
705///         struct to access the register.
706///     -   `$reg_mod`: The module identifier of the register defined
707///         via `spmi_register!`.
708///
709/// # Examples
710///
711/// ```
712/// spmi_register! {
713///     my_reg, u8, 0x10, RW, {
714///         pub enable, set_enable: 7;
715///     }
716/// }
717///
718/// spmi_register! {
719///     status_reg, u16, 0x12, RO, LE, {
720///         pub ready: 0;
721///     }
722/// }
723///
724/// spmi_register_block! {
725///     pub struct MyDeviceRegisters {
726///         pub control => my_reg,
727///         pub status => status_reg,
728///     }
729/// }
730///
731/// // Usage:
732/// // let regs = MyDeviceRegisters::new(spmi_device);
733/// // let ctrl_val = regs.control().read().await?;
734/// // let is_ready = regs.status().read().await?.ready();
735/// ```
736#[macro_export]
737macro_rules! spmi_register_block {
738    // Arm 1: with address_unit override
739    (
740        address_unit: $unit_type:ty,
741        $struct_vis:vis struct $name:ident {
742            $($tail:tt)*
743        }
744    ) => {
745        $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
746
747        impl<D> $crate::RegisterBlock for $name<D> {
748            const ADDRESS_UNIT_BYTES: u16 = std::mem::size_of::<$unit_type>() as u16;
749        }
750    };
751
752    // Arm 2: default (byte-addressed)
753    (
754        $struct_vis:vis struct $name:ident {
755            $($tail:tt)*
756        }
757    ) => {
758        $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
759
760        impl<D> $crate::RegisterBlock for $name<D> {
761            const ADDRESS_UNIT_BYTES: u16 = 1;
762        }
763    };
764
765    // Helper to generate the struct and impl block
766    (
767        @struct $struct_vis:vis, $name:ident,
768        $($tail:tt)*
769    ) => {
770        #[derive(Clone)]
771        $struct_vis struct $name<D = $crate::DeviceType> {
772            pub spmi: D,
773        }
774
775        #[allow(dead_code)]
776        impl<D: $crate::SpmiDevice> $name<D> {
777            $struct_vis fn new(spmi: D) -> Self {
778                Self { spmi }
779            }
780
781            /// Reads a raw byte slice from the contiguous register range.
782            ///
783            /// # Note
784            /// This method is public but hidden because it is required by the
785            /// `spmi_read_contiguous!` macro. Direct use is discouraged.
786            #[doc(hidden)]
787            #[allow(dead_code)]
788            pub async fn read_bulk(
789                &self,
790                address: u16,
791                size: u32,
792            ) -> Result<Vec<u8>, $crate::Error> {
793                let data = $crate::SpmiDevice::read_reg(
794                    &self.spmi,
795                    address,
796                    size,
797                ).await?;
798                if data.len() == size as usize {
799                    Ok(data)
800                } else {
801                    Err($crate::Error::SizeMismatch)
802                }
803            }
804
805            /// Reads a raw byte slice from the contiguous register range into a
806            /// mutable buffer.
807            ///
808            /// # Note
809            /// This method is public but hidden to discourage direct use,
810            /// keeping the bulk API consistent with `read_bulk`.
811            #[doc(hidden)]
812            #[allow(dead_code)]
813            pub async fn read_bulk_into(
814                &self,
815                address: u16,
816                out: &mut [u8],
817            ) -> Result<(), $crate::Error> {
818                let data = $crate::SpmiDevice::read_reg(
819                    &self.spmi,
820                    address,
821                    out.len() as u32,
822                ).await?;
823                if data.len() == out.len() {
824                    out.copy_from_slice(&data);
825                    Ok(())
826                } else {
827                    Err($crate::Error::SizeMismatch)
828                }
829            }
830
831            /// Writes the specified data to the contiguous register range.
832            ///
833            /// # Note
834            /// This method is public but hidden because it is required by the
835            /// `spmi_write_contiguous!` macro. Direct use is discouraged.
836            #[doc(hidden)]
837            #[allow(dead_code)]
838            pub async fn write_bulk(
839                &self,
840                address: u16,
841                data: &[u8],
842            ) -> Result<(), $crate::Error> {
843                $crate::SpmiDevice::write_reg(&self.spmi, address, data).await?;
844                Ok(())
845            }
846
847            $crate::spmi_register_block!(@fields D, $($tail)*);
848        }
849    };
850
851    (@fields $d:ident) => {};
852    (@fields $d:ident,) => {};
853
854    // Case 1: Individual register with trailing fields
855    (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident, $($tail:tt)*) => {
856        $(#[$meta])*
857        #[allow(dead_code)]
858        $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
859            <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
860        }
861        $crate::spmi_register_block!(@fields $d, $($tail)*);
862    };
863
864    // Case 2: Individual register at end of token stream
865    (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident) => {
866        $(#[$meta])*
867        #[allow(dead_code)]
868        $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
869            <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
870        }
871    };
872}
873
874/// Reads multiple contiguous registers in a single async call to the hardware.
875///
876/// This macro accepts an SPMI device proxy and a list of register
877/// modules, calculates the base address and combined size, and performs
878/// a single async contiguous read.
879///
880/// # Arguments
881///
882/// 1.  `$regs:expr`: The block struct instance.
883/// 2.  `$( $reg:ident ),*`: A comma-separated list of already-declared
884///     register modules.
885///
886/// # Examples
887///
888/// ```
889/// // Read both 'general' and 'status' registers together, update,
890/// // and write back:
891/// // let regs = MySpmiRegisters::new(spmi_proxy);
892/// let (mut general_val, status_val) = spmi_read_contiguous!(
893///     &regs,
894///     my_reg,
895///     status_be_reg
896/// ).await?;
897///
898/// general_val = general_val.set_field1(true);
899///
900/// spmi_write_contiguous!(
901///     &regs,
902///     my_reg => general_val,
903///     status_be_reg => status_val
904/// ).await?;
905/// ```
906/// Helper macro that calculates contiguous block layout (unit size, base address, and total bytes).
907#[doc(hidden)]
908#[macro_export]
909macro_rules! spmi_contiguous_layout {
910    (@last $head:ident) => {
911        ($head::ADDRESS, std::mem::size_of::<$head::Value>())
912    };
913
914    (@last $head:ident, $( $tail:ident ),+) => {
915        $crate::spmi_contiguous_layout!(@last $( $tail ),+)
916    };
917
918    (@last_val $head:ident) => {
919        $head::Value
920    };
921
922    (@last_val $head:ident, $( $tail:ident ),+) => {
923        $crate::spmi_contiguous_layout!(@last_val $( $tail ),+)
924    };
925
926    ($regs_ref:expr, $head:ident $(, $tail:ident )*) => {{
927        let unit = $crate::block_address_unit_bytes($regs_ref) as usize;
928        let base_addr = $head::ADDRESS;
929        let (last_addr, last_size) = $crate::spmi_contiguous_layout!(@last $head $(, $tail)*);
930        let last_step = (last_size + unit - 1) / unit;
931        let total_units = (last_addr - base_addr) as usize + last_step;
932        let total_bytes = total_units * unit;
933        (unit, base_addr, total_bytes)
934    }};
935}
936
937/// Reads multiple contiguous registers in a single async call to the hardware.
938///
939/// # Module Names and Paths
940/// The macro expects register module names available directly in scope (e.g. `my_reg`).
941/// Module paths (e.g. `submod::my_reg`) are not supported as variable binding identifiers.
942///
943/// # Arguments
944///
945/// 1.  `$regs:expr`: The block struct instance.
946/// 2.  `$head:ident $(, $tail:ident )*`: The register modules to read.
947///
948/// # Examples
949///
950/// ```
951/// // Read both 'general' and 'status' registers together:
952/// // let regs = MySpmiRegisters::new(spmi_proxy);
953/// let (general_val, status_val) = spmi_read_contiguous!(
954///     &regs,
955///     my_reg,
956///     status_be_reg
957/// ).await?;
958/// ```
959#[macro_export]
960macro_rules! spmi_read_contiguous {
961    (
962        $regs:expr,
963        $head:ident $(, $tail:ident )* $(,)?
964    ) => {
965        async {
966            #[allow(dead_code, non_camel_case_types)]
967            struct ReadAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
968                std::marker::PhantomData<($head, $( $tail ),*)>,
969            )
970            where
971                $head::Mode: $crate::Readable,
972                $( $tail::Mode: $crate::Readable, )*;
973
974            let _ = ReadAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
975
976            let regs_ref = $regs;
977            $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
978            let res: Result<
979                ($head::Value, $( $tail::Value ),*),
980                $crate::Error
981            > = async move {
982                let (unit, base_addr, total_bytes) =
983                    $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
984
985                let data = regs_ref.read_bulk(
986                    base_addr,
987                    total_bytes as u32,
988                ).await?;
989
990                let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
991                let $head = $head::Value::from_bytes(
992                    &data[head_offset..head_offset + std::mem::size_of::<$head::Value>()],
993                )?;
994                $(
995                    let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
996                    let $tail = $tail::Value::from_bytes(
997                        &data[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()],
998                    )?;
999                )*
1000
1001                Ok(($head, $( $tail ),*))
1002            }.await;
1003            res
1004        }
1005    };
1006}
1007
1008/// Writes multiple contiguous registers in a single async call to the hardware.
1009///
1010/// # Sub-Word Padding
1011/// When writing to word-addressed blocks (`address_unit: u16`) containing sub-word registers (e.g. `u8`),
1012/// unwritten padding byte positions within a word unit are zero-filled before issuing the bulk write.
1013///
1014/// # Module Names and Paths
1015/// The macro expects register module names available directly in scope (e.g. `my_reg`).
1016/// Module paths (e.g. `submod::my_reg`) are not supported as variable binding identifiers.
1017///
1018/// # Arguments
1019///
1020/// 1.  `$regs:expr`: The block struct instance.
1021/// 2.  `$( $reg:ident => $val:expr ),*`: A comma-separated mapping from
1022///     register modules to their values.
1023///
1024/// # Examples
1025///
1026/// ```
1027/// // Write both 'general' and 'status' registers together:
1028/// // let regs = MySpmiRegisters::new(spmi_proxy);
1029/// spmi_write_contiguous!(
1030///     &regs,
1031///     my_reg => val_a,
1032///     status_be_reg => val_b
1033/// ).await?;
1034/// ```
1035#[macro_export]
1036macro_rules! spmi_write_contiguous {
1037    (
1038        $regs:expr,
1039        $head:ident => $head_val:expr $(, $tail:ident => $tail_val:expr )* $(,)?
1040    ) => {
1041        async {
1042            #[allow(dead_code, non_camel_case_types)]
1043            struct WriteAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
1044                std::marker::PhantomData<($head, $( $tail ),*)>,
1045            )
1046            where
1047                $head::Mode: $crate::Writable,
1048                $( $tail::Mode: $crate::Writable, )*;
1049
1050            let _ = WriteAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
1051
1052            let regs_ref = $regs;
1053            $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
1054            let res: Result<(), $crate::Error> = async move {
1055                let (unit, base_addr, total_bytes) =
1056                    $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
1057
1058                let mut bytes = vec![0u8; total_bytes];
1059
1060                let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
1061                bytes[head_offset..head_offset + std::mem::size_of::<$head::Value>()]
1062                    .copy_from_slice(&$head_val.to_bytes());
1063                $(
1064                    let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
1065                    bytes[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()]
1066                        .copy_from_slice(&$tail_val.to_bytes());
1067                )*
1068
1069                regs_ref.write_bulk(base_addr, &bytes).await?;
1070
1071                Ok(())
1072            }.await;
1073            res
1074        }
1075    };
1076}
1077
1078/// Trait for SPMI devices to abstract register read/write operations.
1079///
1080/// This trait allows the `Register` and `spmi_register_block!` macros to
1081/// interact with any underlying hardware interface or mock device that
1082/// implements these basic SPMI read and write operations.
1083///
1084/// Implementations must handle the low-level transport (e.g., FIDL calls
1085/// to a Fuchsia SPMI driver) and handle error mapping.
1086#[allow(async_fn_in_trait)]
1087pub trait SpmiDevice {
1088    /// Reads a contiguous sequence of bytes from the device.
1089    ///
1090    /// # Arguments
1091    ///
1092    /// * `address` - The 16-bit base register address to read from.
1093    /// * `size` - The number of bytes to read.
1094    ///
1095    /// # Returns
1096    ///
1097    /// Returns a vector containing the read bytes on success, or a
1098    /// `crate::Error` on failure.
1099    async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error>;
1100
1101    /// Writes a contiguous sequence of bytes to the device.
1102    ///
1103    /// # Arguments
1104    ///
1105    /// * `address` - The 16-bit base register address to write to.
1106    /// * `data` - The byte slice to write to the device.
1107    ///
1108    /// # Returns
1109    ///
1110    /// Returns `Ok(())` on success, or a `crate::Error` on failure.
1111    async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error>;
1112}
1113
1114#[cfg(test)]
1115/// A mock SPMI device that stores register values in memory.
1116/// Used for testing register access without a real device or FIDL connection.
1117pub struct TestSpmiDevice {
1118    registers: std::sync::Mutex<std::collections::HashMap<u16, u8>>,
1119}
1120
1121#[cfg(test)]
1122impl TestSpmiDevice {
1123    /// Creates a new empty `TestSpmiDevice`.
1124    pub fn new() -> Self {
1125        Self { registers: std::sync::Mutex::new(std::collections::HashMap::new()) }
1126    }
1127}
1128
1129#[cfg(test)]
1130impl SpmiDevice for TestSpmiDevice {
1131    async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1132        let regs = self.registers.lock().unwrap();
1133        let mut data = Vec::new();
1134        for i in 0..size {
1135            let addr = address + i as u16;
1136            let val = regs.get(&addr).copied().unwrap_or(0);
1137            data.push(val);
1138        }
1139        Ok(data)
1140    }
1141
1142    async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1143        let mut regs = self.registers.lock().unwrap();
1144        for (i, &val) in data.iter().enumerate() {
1145            let addr = address + i as u16;
1146            regs.insert(addr, val);
1147        }
1148        Ok(())
1149    }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154    use super::*;
1155
1156    // Note: For single-byte width registers (u8), explicit endianness is
1157    // not required.
1158    spmi_register! {
1159        test_u8_reg, u8, 0xCD, RW, {
1160            pub flag, set_flag: 4;
1161            pub field, set_field: 3, 0;
1162        }
1163    }
1164
1165    spmi_register_block! {
1166        pub struct MockU8Regs {
1167            pub test => test_u8_reg,
1168        }
1169    }
1170
1171    spmi_register! {
1172        test_u16_from_bytes_reg, u16, 0x99, RW, LE, {
1173            pub field, set_field: 15, 0;
1174        }
1175    }
1176
1177    spmi_register_block! {
1178        pub struct MockU16FromBytesRegs {
1179            pub test => test_u16_from_bytes_reg,
1180        }
1181    }
1182
1183    spmi_register! {
1184        test_u16_contig_reg, u16, 0xCE, RW, LE, {
1185            pub field, set_field: 15, 0;
1186        }
1187    }
1188
1189    #[fuchsia::test]
1190    async fn test_u16_from_bytes() {
1191        let device = TestSpmiDevice::new();
1192        device.write_reg(0x99, &[0x34, 0x12]).await.unwrap();
1193
1194        let regs = MockU16FromBytesRegs::new(device);
1195        let val = regs.test().read().await.unwrap();
1196        assert_eq!(val.reg_value(), 0x1234);
1197    }
1198
1199    #[fuchsia::test]
1200    async fn test_u8_register() {
1201        let device = TestSpmiDevice::new();
1202        device.write_reg(0xCD, &[0x1A]).await.unwrap();
1203
1204        let regs = MockU8Regs::new(device);
1205        let val = regs.test().read().await.unwrap();
1206        assert_eq!(val.reg_value(), 0x1A);
1207        assert_eq!(val.flag(), true);
1208        assert_eq!(val.field(), 0x0A);
1209    }
1210
1211    spmi_register! {
1212        test_reg, u16, 0xAB, RW, LE, {
1213            pub test_bit, set_test_bit: 7;
1214            pub test_field, set_test_field: 3, 0;
1215        }
1216    }
1217
1218    spmi_register_block! {
1219        pub struct MockRegs {
1220            pub test => test_reg,
1221        }
1222    }
1223
1224    #[fuchsia::test]
1225    async fn test_read() {
1226        let device = TestSpmiDevice::new();
1227        device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1228
1229        let regs = MockRegs::new(device);
1230        let val = regs.test().read().await.unwrap();
1231        assert_eq!(val.reg_value(), 0x008A);
1232        assert_eq!(val.test_bit(), true);
1233        assert_eq!(val.test_field(), 0x0A);
1234    }
1235
1236    #[fuchsia::test]
1237    async fn test_write() {
1238        let device = TestSpmiDevice::new();
1239
1240        let regs = MockRegs::new(device);
1241        let v = test_reg::Value::new(0x008A);
1242        regs.test().write(v).await.unwrap();
1243
1244        let data = regs.spmi.read_reg(0xAB, 2).await.unwrap();
1245        assert_eq!(data, &[0x8A, 0x00]);
1246    }
1247
1248    struct MockWrapper {
1249        device: TestSpmiDevice,
1250    }
1251
1252    impl SpmiDevice for MockWrapper {
1253        async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1254            self.device.read_reg(address, size).await
1255        }
1256        async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1257            self.device.write_reg(address, data).await
1258        }
1259    }
1260
1261    spmi_register_block! {
1262        pub struct MockWrappedRegs {
1263            pub test => test_reg,
1264        }
1265    }
1266
1267    #[fuchsia::test]
1268    async fn test_wrapped_read() {
1269        let device = TestSpmiDevice::new();
1270        device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1271
1272        let wrapper = MockWrapper { device };
1273        let regs = MockWrappedRegs::new(wrapper);
1274        let val = regs.test().read().await.unwrap();
1275        assert_eq!(val.reg_value(), 0x008A);
1276        assert_eq!(val.test_bit(), true);
1277    }
1278
1279    spmi_register! {
1280        test_be_reg, u16, 0xEF, RW, BE, {
1281            pub flag, set_flag: 4;
1282            pub field, set_field: 3, 0;
1283        }
1284    }
1285
1286    spmi_register_block! {
1287        pub struct MockBERegs {
1288            pub test => test_be_reg,
1289        }
1290    }
1291
1292    #[fuchsia::test]
1293    async fn test_be_register() {
1294        let device = TestSpmiDevice::new();
1295        device.write_reg(0xEF, &[0x00, 0x8A]).await.unwrap();
1296
1297        let regs = MockBERegs::new(device);
1298        let val = regs.test().read().await.unwrap();
1299        assert_eq!(val.reg_value(), 0x8A);
1300    }
1301
1302    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
1303    #[repr(u16)]
1304    pub enum PowerMode {
1305        Normal = 0,
1306        Hibernate = 1,
1307        LowPower = 2,
1308        Unknown = 0xFFFF,
1309    }
1310
1311    impl PowerMode {
1312        pub const fn from_val(val: u16) -> Self {
1313            match val {
1314                0 => PowerMode::Normal,
1315                1 => PowerMode::Hibernate,
1316                2 => PowerMode::LowPower,
1317                _ => PowerMode::Unknown,
1318            }
1319        }
1320    }
1321
1322    spmi_register! {
1323        test_enum_reg, u16, 0x44, RW, LE, {
1324            pub enum PowerMode, mode, set_mode: 3, 2;
1325        }
1326    }
1327
1328    spmi_register_block! {
1329        pub struct MockEnumRegs {
1330            pub test => test_enum_reg,
1331        }
1332    }
1333
1334    #[fuchsia::test]
1335    async fn test_enum_register() {
1336        let device = TestSpmiDevice::new();
1337
1338        let regs = MockEnumRegs::new(device);
1339        let v = test_enum_reg::Value::new(0).set_mode(PowerMode::Hibernate);
1340        regs.test().write(v).await.unwrap();
1341
1342        let data = regs.spmi.read_reg(0x44, 2).await.unwrap();
1343        assert_eq!(data, &[0x04, 0x00]);
1344    }
1345
1346    spmi_register! {
1347        test_inline_enum_reg, u16, 0x55, RW, LE, {
1348            pub enum InlineMode {
1349                A = 0,
1350                B = 1,
1351            }, mode, set_mode: 1, 0;
1352        }
1353    }
1354
1355    spmi_register_block! {
1356        pub struct MockInlineEnumRegs {
1357            pub test => test_inline_enum_reg,
1358        }
1359    }
1360
1361    #[fuchsia::test]
1362    async fn test_inline_enum() {
1363        let device = TestSpmiDevice::new();
1364
1365        let regs = MockInlineEnumRegs::new(device);
1366        let v = test_inline_enum_reg::Value::new(1).set_mode(test_inline_enum_reg::InlineMode::B);
1367        assert_eq!(v.mode(), Ok(test_inline_enum_reg::InlineMode::B));
1368        regs.test().write(v).await.unwrap();
1369
1370        let data = regs.spmi.read_reg(0x55, 2).await.unwrap();
1371        assert_eq!(data, &[0x01, 0x00]);
1372    }
1373
1374    #[fuchsia::test]
1375    async fn test_contiguous_read_write() {
1376        let device = TestSpmiDevice::new();
1377        device.write_reg(0xCD, &[0x1A, 0x34, 0x12]).await.unwrap();
1378
1379        spmi_register_block! {
1380            pub struct ContiguousRegs {
1381                pub r1 => test_u8_reg,
1382                pub r2 => test_u16_contig_reg,
1383            }
1384        }
1385        let regs = ContiguousRegs::new(device);
1386
1387        let (val_1, val_2) =
1388            spmi_read_contiguous!(&regs, test_u8_reg, test_u16_contig_reg,).await.unwrap();
1389
1390        assert_eq!(val_1.reg_value(), 0x1A);
1391        assert_eq!(val_2.reg_value(), 0x1234);
1392
1393        spmi_write_contiguous!(
1394            &regs,
1395            test_u8_reg => val_1,
1396            test_u16_contig_reg => val_2
1397        )
1398        .await
1399        .unwrap();
1400
1401        let data = regs.spmi.read_reg(0xCD, 3).await.unwrap();
1402        assert_eq!(data, &[0x1A, 0x34, 0x12]);
1403    }
1404
1405    spmi_register! {
1406        test_word_reg_1, u16, 0x10, RW, LE, {}
1407    }
1408    spmi_register! {
1409        test_word_reg_2, u16, 0x11, RW, LE, {}
1410    }
1411    spmi_register! {
1412        test_word_reg_3, u16, 0x12, RW, LE, {}
1413    }
1414
1415    spmi_register_block! {
1416        address_unit: u16,
1417        pub struct WordAddressedRegs {
1418            pub r1 => test_word_reg_1,
1419            pub r2 => test_word_reg_2,
1420            pub r3 => test_word_reg_3,
1421        }
1422    }
1423
1424    #[fuchsia::test]
1425    async fn test_word_addressed_contiguous() {
1426        let device = TestSpmiDevice::new();
1427        device.write_reg(0x10, &[0x34, 0x12, 0x78, 0x56]).await.unwrap();
1428
1429        let regs = WordAddressedRegs::new(device);
1430
1431        // Note on SPMI addressing: Standard SPMI register addresses refer to byte offsets (where a
1432        // 16-bit register spans 2 byte addresses, so contiguous u16 registers have an address diff
1433        // of 2). However, some hardware devices use 16-bit word-addressed registers where each
1434        // address offset 0x10, 0x11 represents a 16-bit word (address diff of 1). Specifying
1435        // `address_unit: u16` in `spmi_register_block!` handles these word-addressed hardware
1436        // blocks.
1437        let (val_1, val_2) =
1438            spmi_read_contiguous!(&regs, test_word_reg_1, test_word_reg_2).await.unwrap();
1439        assert_eq!(val_1.reg_value(), 0x1234);
1440        assert_eq!(val_2.reg_value(), 0x5678);
1441
1442        // Write contiguous
1443        spmi_write_contiguous!(
1444            &regs,
1445            test_word_reg_1 => val_1,
1446            test_word_reg_2 => val_2
1447        )
1448        .await
1449        .unwrap();
1450
1451        let data = regs.spmi.read_reg(0x10, 4).await.unwrap();
1452        assert_eq!(data, &[0x34, 0x12, 0x78, 0x56]);
1453    }
1454
1455    #[fuchsia::test]
1456    async fn test_read_write_bulk() {
1457        let device = TestSpmiDevice::new();
1458        device.write_reg(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1459
1460        let regs = MockU8Regs::new(device);
1461        let bytes = regs.read_bulk(0x88, 3).await.unwrap();
1462        assert_eq!(bytes, vec![0x1A, 0x2B, 0x3C]);
1463
1464        let mut buf = [0u8; 3];
1465        regs.read_bulk_into(0x88, &mut buf).await.unwrap();
1466        assert_eq!(buf, [0x1A, 0x2B, 0x3C]);
1467
1468        regs.write_bulk(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1469        let data = regs.spmi.read_reg(0x88, 3).await.unwrap();
1470        assert_eq!(data, &[0x1A, 0x2B, 0x3C]);
1471    }
1472
1473    spmi_register! {
1474        test_mixed_u8, u8, 0x20, RW, {}
1475    }
1476    spmi_register! {
1477        test_mixed_u16, u16, 0x21, RW, LE, {}
1478    }
1479
1480    spmi_register_block! {
1481        address_unit: u16,
1482        pub struct WordAddressedMixedRegs {
1483            pub u8_reg => test_mixed_u8,
1484            pub u16_reg => test_mixed_u16,
1485        }
1486    }
1487
1488    #[fuchsia::test]
1489    async fn test_word_addressed_mixed_sizes_contiguous() {
1490        let device = TestSpmiDevice::new();
1491        // 0x20 is u8 (occupies byte 0, byte 1 padded 0x00). 0x21 is u16 (bytes 2..4).
1492        device.write_reg(0x20, &[0xAB, 0x00, 0x34, 0x12]).await.unwrap();
1493
1494        let regs = WordAddressedMixedRegs::new(device);
1495        let (val_u8, val_u16) =
1496            spmi_read_contiguous!(&regs, test_mixed_u8, test_mixed_u16).await.unwrap();
1497
1498        assert_eq!(val_u8.reg_value(), 0xAB);
1499        assert_eq!(val_u16.reg_value(), 0x1234);
1500
1501        spmi_write_contiguous!(
1502            &regs,
1503            test_mixed_u8 => val_u8,
1504            test_mixed_u16 => val_u16
1505        )
1506        .await
1507        .unwrap();
1508
1509        let data = regs.spmi.read_reg(0x20, 4).await.unwrap();
1510        assert_eq!(data, &[0xAB, 0x00, 0x34, 0x12]);
1511    }
1512
1513    #[derive(
1514        Copy,
1515        Clone,
1516        Debug,
1517        PartialEq,
1518        Eq,
1519        zerocopy::FromBytes,
1520        zerocopy::IntoBytes,
1521        zerocopy::KnownLayout,
1522        zerocopy::Immutable,
1523    )]
1524    #[repr(C)]
1525    pub struct Reg3Byte([u8; 3]);
1526    impl RegisterValue for Reg3Byte {}
1527
1528    impl Reg3Byte {
1529        fn from_bytes(bytes: &[u8]) -> Result<Self, crate::Error> {
1530            let slice: [u8; 3] = bytes.try_into().unwrap();
1531            Ok(Self(slice))
1532        }
1533        fn to_bytes(&self) -> [u8; 3] {
1534            self.0
1535        }
1536        fn reg_value(&self) -> Self {
1537            *self
1538        }
1539    }
1540
1541    mod test_3byte_reg {
1542        use super::*;
1543        pub type Value = Reg3Byte;
1544        pub const ADDRESS: u16 = 0x30;
1545    }
1546
1547    impl SpmiRegisterDef for Reg3Byte {
1548        type Value = Self;
1549        type Mode = crate::ReadWrite;
1550        const ADDRESS: u16 = 0x30;
1551    }
1552
1553    impl<D> SpmiRegister<D> for Reg3Byte {
1554        type Accessor<'a>
1555            = &'a D
1556        where
1557            Self: 'a,
1558            D: 'a;
1559        fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
1560            spmi
1561        }
1562    }
1563    spmi_register! {
1564        test_after_3byte_reg, u16, 0x32, RW, LE, {}
1565    }
1566
1567    spmi_register_block! {
1568        address_unit: u16,
1569        pub struct WordAddressed3ByteRegs {
1570            pub r3b => test_3byte_reg,
1571            pub r_next => test_after_3byte_reg,
1572        }
1573    }
1574
1575    #[fuchsia::test]
1576    async fn test_word_addressed_non_multiple_size_contiguous() {
1577        let device = TestSpmiDevice::new();
1578        // 0x30 is 3 bytes (takes 2 word units = 4 bytes: [0x11, 0x22, 0x33, 0x00]).
1579        // 0x32 is u16 (starts at 0x32 = byte offset 4: [0x56, 0x78]).
1580        device.write_reg(0x30, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]).await.unwrap();
1581
1582        let regs = WordAddressed3ByteRegs::new(device);
1583        let (val_3b, val_next) =
1584            spmi_read_contiguous!(&regs, test_3byte_reg, test_after_3byte_reg).await.unwrap();
1585
1586        assert_eq!(val_3b.reg_value(), Reg3Byte([0x11, 0x22, 0x33]));
1587        assert_eq!(val_next.reg_value(), 0x7856);
1588
1589        spmi_write_contiguous!(
1590            &regs,
1591            test_3byte_reg => val_3b,
1592            test_after_3byte_reg => val_next
1593        )
1594        .await
1595        .unwrap();
1596
1597        let data = regs.spmi.read_reg(0x30, 6).await.unwrap();
1598        assert_eq!(data, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]);
1599    }
1600}