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// --- Typestate Access Modes ---
15// These markers are used to enforce register access rules
16// (Read-Only, Write-Only, Read-Write) at compile time using
17// the typestate pattern.
18
19/// Marker struct representing Read-Only register access.
20pub struct ReadOnly;
21
22/// Marker struct representing Write-Only register access.
23pub struct WriteOnly;
24
25/// Marker struct representing Read-Write register access.
26pub struct ReadWrite;
27
28/// Trait implemented by access modes that allow reading.
29///
30/// This is used as a generic bound on `Register::read` to prevent compile-time
31/// errors when attempting to read from write-only registers.
32pub trait Readable {}
33impl Readable for ReadOnly {}
34impl Readable for ReadWrite {}
35
36/// Trait implemented by access modes that allow writing.
37///
38/// This is used as a generic bound on `Register::write` to prevent compile-time
39/// errors when attempting to write to read-only registers.
40pub trait Writable {}
41impl Writable for WriteOnly {}
42impl Writable for ReadWrite {}
43
44/// A generic register accessor.
45///
46/// This struct provides type-safe access to a specific hardware register.
47/// It is parameterized by:
48/// * `T` - The typed `RegisterValue` (usually the generated `Value` struct).
49/// * `M` - The access mode (`ReadOnly`, `WriteOnly`, or `ReadWrite`).
50/// * `D` - The underlying device implementation (implements `SpmiDevice`).
51/// * `ADDR` - The constant hardware address of the register.
52pub struct _Register<'a, T, M, D, const ADDR: u16> {
53    /// The underlying SPMI device.
54    pub spmi: &'a D,
55    _marker: std::marker::PhantomData<(T, M)>,
56}
57
58impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR> {
59    /// Creates a new register accessor.
60    pub fn new(spmi: &'a D) -> Self {
61        Self { spmi, _marker: std::marker::PhantomData }
62    }
63}
64
65// Inherent read method available ONLY if M is Readable and D is SpmiDevice
66impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
67where
68    M: Readable,
69    T: RegisterValue,
70    D: SpmiDevice,
71{
72    /// Reads the register value from the hardware.
73    ///
74    /// This method performs an asynchronous read operation on the underlying
75    /// SPMI device and deserializes the raw bytes into the typed value `T`.
76    pub async fn read(&self) -> Result<T, crate::Error> {
77        let size = std::mem::size_of::<T>();
78        let bytes = self.spmi.read_reg(ADDR, size as u32).await?;
79        T::read_from_bytes(&bytes).map_err(|_| crate::Error::SizeMismatch)
80    }
81}
82
83// Inherent write method available ONLY if M is Writable and D is SpmiDevice
84impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
85where
86    M: Writable,
87    T: RegisterValue,
88    D: SpmiDevice,
89{
90    /// Writes the register value to the hardware.
91    ///
92    /// This method serializes the typed value `T` into raw bytes and performs
93    /// an asynchronous write operation on the underlying SPMI device.
94    pub async fn write(&self, val: T) -> Result<(), crate::Error> {
95        let bytes = val.as_bytes();
96        self.spmi.write_reg(ADDR, bytes).await?;
97        Ok(())
98    }
99}
100
101/// Defines a module for accessing a single SPMI hardware register.
102///
103/// This macro generates a public module named `$name` containing:
104/// -   `ADDRESS`: A `u16` constant for the register address.
105/// -   `Value`: A struct wrapping the raw register value (`$value_type`)
106///     and providing const-fn accessors for defined fields.
107/// -   `Register<'a>`: A struct with an asynchronous `read` and
108///     potentially `write` method to interact with the SPMI device.
109///
110/// # Arguments
111///
112/// 1.  `$name`: The identifier for the generated module.
113/// 2.  `$value_type:ty`: The Rust integer type representing the register's
114///     width (e.g., `u8`, `u16`, `u32`).
115/// 3.  `$addr:expr`: The base address of the register as a `u16`.
116/// 4.  `$mode:ident`: The access mode, one of `RO` (Read Only), `WO` (Write
117///     Only), or `RW` (Read/Write).
118/// 5.  `$endianness:ident`: The endianness of the register. Required for `u16`
119///     and larger. Can be `LE` (Little Endian) or `BE` (Big Endian). For `u8`
120///     registers, this argument is omitted because endianness is irrelevant
121///     for `u8` registers.
122/// 6.  `{ ... }`: A block defining the fields within the register. Each
123///     field definition ends with a semicolon. The following formats are
124///     supported within the block:
125///     *   **Single-bit field:** `$vis $field_name $(, $setter_name)? : $bit;`
126///         -   `$vis`: Visibility (e.g., `pub`).
127///         -   `$field_name`: The name of the getter method (returns `bool`).
128///         -   `$setter_name`: (Optional) The name of the setter method
129///             (takes `bool`, returns `Self`).
130///         -   `$bit_index`: The index of the single bit (e.g., `7`).
131///         Example: `pub enable, set_enable: 7;`
132///
133///     *   **Multi-bit field:** `$vis $field_name $(, $setter_name)? \
134///         : $msb, $lsb;`
135///         -   `$vis`: Visibility.
136///         -   `$field_name`: The getter method (returns `$value_type`).
137///         -   `$setter_name`: (Optional) The setter method (takes
138///             `$value_type`, returns `Self`).
139///         -   `$msb`: The Most Significant Bit index.
140///         -   `$lsb`: The Least Significant Bit index.
141///         Example: `pub field_val, set_field_val: 5, 2;`
142///
143///     *   **Enum field (external type):** `$vis enum $enum_type, $field_name \
144///         $(, $setter_name)? : $msb, $lsb;`
145///         -   `$vis`: Visibility.
146///         -   `$enum_type`: The path to an existing enum type (e.g.,
147///             `super::PowerMode`). This enum must have a `pub const fn \
148///             from_val(val: $value_type) -> Self` associated function.
149///         -   `$field_name`: The getter method (returns `$enum_type`).
150///         -   `$setter_name`: (Optional) The setter method (takes
151///             `$enum_type`, returns `Self`).
152///         -   `$msb`, `$lsb`: The bit range.
153///         Example: `pub enum super::PowerMode, mode, set_mode: 3, 2;`
154///
155///     *   **In-line Enum field:** `$vis enum $enum_name { ... }, $field_name \
156///         $(, $setter_name)? : $msb, $lsb;`
157///         -   `$vis`: Visibility.
158///         -   `$enum_name`: The name for the enum type, defined within
159///             the generated module.
160///         -   `{ ... }`: The enum variants and their `$value_type` values.
161///         -   `$field_name`: The getter method (returns `Result<$enum_name, \
162///             $value_type>`).
163///         -   `$setter_name`: (Optional) The setter method (takes
164///             `$enum_name`, returns `Self`).
165///         -   `$msb`, `$lsb`: The bit range.
166///         Example: `pub enum InlineMode { A = 0, B = 1 }, \
167///             mode, set_mode: 1, 0;`
168///
169///     *   **Custom constant:** `pub const $const_name : $type = $val;`
170///         -   Allows defining constants within the generated register module.
171///         Example: `pub const MAX_VALUE: u8 = 0xFF;`
172///
173/// # Examples
174///
175/// ```
176/// // Define a register at address 0x10, 8-bit, Read/Write,
177/// // Little Endian (default)
178/// spmi_register! {
179///     my_reg, u8, 0x10, RW, {
180///         // Single bit flag
181///         pub enable, set_enable: 7;
182///         // 4-bit field
183///         pub value, set_value: 3, 0;
184///         // Inline enum field
185///         pub enum Status {
186///             Idle = 0,
187///             Active = 1,
188///             Error = 2,
189///         }, status, set_status: 6, 5;
190///     }
191/// }
192///
193/// // Define a register at address 0x20, 16-bit, Read Only, Big Endian
194/// spmi_register! {
195///     status_be_reg, u16, 0x20, RO, BE, {
196///         pub error_code: 15, 8;
197///         pub ready: 0;
198///     }
199/// }
200/// ```
201#[macro_export]
202macro_rules! spmi_register {
203    // Helper to map mode and endianness to register type
204    (@map_reg RO, $val:ty, $addr:expr, BE) => {
205        $crate::Register<'a, $val, $crate::ReadOnly, $addr>
206    };
207    (@map_reg RO, $val:ty, $addr:expr, LE) => {
208        $crate::Register<'a, $val, $crate::ReadOnly, $addr>
209    };
210    (@map_reg WO, $val:ty, $addr:expr, BE) => {
211        $crate::Register<'a, $val, $crate::WriteOnly, $addr>
212    };
213    (@map_reg WO, $val:ty, $addr:expr, LE) => {
214        $crate::Register<'a, $val, $crate::WriteOnly, $addr>
215    };
216    (@map_reg RW, $val:ty, $addr:expr, BE) => {
217        $crate::Register<'a, $val, $crate::ReadWrite, $addr>
218    };
219    (@map_reg RW, $val:ty, $addr:expr, LE) => {
220        $crate::Register<'a, $val, $crate::ReadWrite, $addr>
221    };
222
223    // Explicit-endian byteorder helpers inside macro
224    (@byteorder_type u8, $endianness:ident) => { u8 };
225    (@byteorder_type u16, LE) => { $crate::U16<$crate::LittleEndian> };
226    (@byteorder_type u16, BE) => { $crate::U16<$crate::BigEndian> };
227    (@byteorder_type u32, LE) => { $crate::U32<$crate::LittleEndian> };
228    (@byteorder_type u32, BE) => { $crate::U32<$crate::BigEndian> };
229
230    (@new u8, $endianness:ident, $val:expr) => { Self($val) };
231    (@new u16, $endianness:ident, $val:expr) => {
232        Self($crate::U16::new($val))
233    };
234    (@new u32, $endianness:ident, $val:expr) => {
235        Self($crate::U32::new($val))
236    };
237
238    (@get u8, $endianness:ident, $val:expr) => { $val };
239    (@get u16, $endianness:ident, $val:expr) => { $val.get() };
240    (@get u32, $endianness:ident, $val:expr) => { $val.get() };
241
242    // Explicit endianness provided via single arm
243    (
244        $name:ident,
245        $value_type:ident,
246        $addr:expr,
247        $mode:ident,
248        $endianness:ident,
249        {
250            $($tail:tt)*
251        }
252    ) => {
253        #[allow(unused_imports)]
254        #[allow(dead_code)]
255        pub mod $name {
256            use super::*;
257            use $crate::zerocopy_reexport as zerocopy;
258
259            /// The address of the register.
260            pub const ADDRESS: u16 = $addr;
261
262            $crate::spmi_register_extract_enums!($value_type, $($tail)*);
263
264            /// Represents the value of the register.
265            #[derive(
266                Copy,
267                Clone,
268                Debug,
269                PartialEq,
270                Eq,
271                zerocopy::FromBytes,
272                zerocopy::IntoBytes,
273                zerocopy::Immutable,
274            )]
275            #[repr(transparent)]
276            pub struct Value(
277                pub spmi_register!(@byteorder_type $value_type, $endianness),
278            );
279
280            impl Value {
281                pub const fn new(val: $value_type) -> Self {
282                    spmi_register!(@new $value_type, $endianness, val)
283                }
284                pub const fn reg_value(&self) -> $value_type {
285                    spmi_register!(@get $value_type, $endianness, self.0)
286                }
287
288                /// Reconstructs a typed `Value` from a slice of raw bytes.
289                pub fn from_bytes(bytes: &[u8]) -> Result<Self, $crate::Error> {
290                    zerocopy::FromBytes::read_from_bytes(bytes)
291                        .map_err(|_| $crate::Error::SizeMismatch)
292                }
293
294                /// Converts the `Value` into its raw byte representation.
295                pub fn to_bytes(
296                    &self,
297                ) -> [u8; std::mem::size_of::<$value_type>()] {
298                    let mut arr = [0u8; std::mem::size_of::<$value_type>()];
299                    arr.copy_from_slice($crate::IntoBytes::as_bytes(self));
300                    arr
301                }
302
303                $crate::spmi_register_fields!($value_type, $($tail)*);
304            }
305
306            impl $crate::RegisterValue for Value {}
307
308            impl Default for Value {
309                fn default() -> Self {
310                    Self::new(0 as $value_type)
311                }
312            }
313
314            /// The register accessor type.
315            pub type Register<'a> =
316                spmi_register!(@map_reg $mode, Value, $addr, $endianness);
317        }
318    };
319    // Specialized arm for u8 where endianness is irrelevant
320    (
321        $name:ident, u8, $addr:expr, $mode:ident, {
322            $($tail:tt)*
323        }
324    ) => {
325        spmi_register!($name, u8, $addr, $mode, LE, { $($tail)* });
326    };
327    (@is_big BE) => { true };
328    (@is_big LE) => { false };
329}
330
331/// Helper macro for `spmi_register!` to extract inline enums.
332#[macro_export]
333#[doc(hidden)]
334macro_rules! spmi_register_extract_enums {
335    // In-line Enum
336    (
337        $value_type:ty,
338        $(#[$attr:meta])*
339        $vis:vis enum $enum_name:ident {
340            $( $variant_name:ident = $variant_val:expr ),* $(,)?
341        },
342        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
343        $($tail:tt)*
344    ) => {
345        #[derive(Debug, PartialEq, Eq, Copy, Clone)]
346        #[repr($value_type)]
347        $vis enum $enum_name {
348            $( $variant_name = $variant_val ),*
349        }
350        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
351    };
352
353    // Forwarding other cases
354    (
355        $value_type:ty,
356        $(#[$attr:meta])*
357        $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
358        $($tail:tt)*
359    ) => {
360        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
361    };
362    (
363        $value_type:ty,
364        $(#[$attr:meta])*
365        $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
366        $($tail:tt)*
367    ) => {
368        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
369    };
370    (
371        $value_type:ty,
372        $(#[$attr:meta])*
373        $vis:vis enum $enum_type:ty,
374        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
375        $($tail:tt)*
376    ) => {
377        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
378    };
379    (
380        $value_type:ty,
381        pub const $name:ident : $type:ty = $val:expr;
382        $($tail:tt)*
383    ) => {
384        $crate::spmi_register_extract_enums!($value_type, $($tail)*);
385    };
386    ($value_type:ty) => {};
387    ($value_type:ty,) => {};
388}
389
390/// Helper macro for `spmi_register!` to generate field accessors.
391#[macro_export]
392#[doc(hidden)]
393macro_rules! spmi_register_fields {
394    // Terminating cases
395    ($value_type:ty) => {};
396    ($value_type:ty,) => {};
397
398    // 1. Single-bit field
399    (
400        $value_type:ty,
401        $(#[$attr:meta])*
402        $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
403        $($tail:tt)*
404    ) => {
405        $(#[$attr])*
406        #[allow(non_snake_case)]
407        #[allow(dead_code)]
408        $vis const fn $field(&self) -> bool {
409            const _: () = assert!(
410                $bit < <$value_type>::BITS as u8,
411                "Bit index out of bounds"
412            );
413            let bit_mask = (1 as $value_type) << $bit;
414            (self.reg_value() & bit_mask) != 0
415        }
416        $(
417            #[allow(non_snake_case)]
418            #[allow(dead_code)]
419            $vis const fn $setter(self, val: bool) -> Self {
420                const _: () = assert!(
421                    $bit < <$value_type>::BITS as u8,
422                    "Bit index out of bounds"
423                );
424                let bit_mask = (1 as $value_type) << $bit;
425                let raw = (self.reg_value() & !bit_mask)
426                    | ((val as $value_type) << $bit);
427                Self::new(raw)
428            }
429        )?
430        $crate::spmi_register_fields!($value_type, $($tail)*);
431    };
432
433    // 2. Multi-bit field
434    (
435        $value_type:ty,
436        $(#[$attr:meta])*
437        $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
438        $($tail:tt)*
439    ) => {
440        $(#[$attr])*
441        #[allow(non_snake_case)]
442        #[allow(dead_code)]
443        $vis const fn $field(&self) -> $value_type {
444            const _: () = assert!(
445                $msb < <$value_type>::BITS as u8,
446                "MSB index out of bounds"
447            );
448            const _: () = assert!(
449                $lsb < $msb,
450                "LSB must be strictly less than MSB. \
451                 Use single-bit syntax (e.g., 'field: bit;') for 1-bit fields."
452            );
453            let bit_count = $msb - $lsb + 1;
454            let bit_mask = ((!0 as $value_type)
455                >> (<$value_type>::BITS as u8 - bit_count))
456                << $lsb;
457            (self.reg_value() & bit_mask) >> $lsb
458        }
459        $(
460            #[allow(non_snake_case)]
461            #[allow(dead_code)]
462            $vis const fn $setter(self, val: $value_type) -> Self {
463                const _: () = assert!(
464                    $msb < <$value_type>::BITS as u8,
465                    "MSB index out of bounds"
466                );
467                const _: () = assert!(
468                    $lsb < $msb,
469                    "LSB must be strictly less than MSB. \
470                     Use single-bit syntax (e.g., 'field: bit;') \
471                     for 1-bit fields."
472                );
473                let bit_count = $msb - $lsb + 1;
474                let bit_mask = ((!0 as $value_type)
475                    >> (<$value_type>::BITS as u8 - bit_count))
476                    << $lsb;
477                let raw = (self.reg_value() & !bit_mask)
478                    | ((val << $lsb) & bit_mask);
479                Self::new(raw)
480            }
481        )?
482        $crate::spmi_register_fields!($value_type, $($tail)*);
483    };
484
485    // 3. Enum field
486    (
487        $value_type:ty,
488        $(#[$attr:meta])*
489        $vis:vis enum $enum_type:ty,
490        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
491        $($tail:tt)*
492    ) => {
493        $(#[$attr])*
494        #[allow(non_snake_case)]
495        #[allow(dead_code)]
496        $vis const fn $field(&self) -> $enum_type {
497            const _: () = assert!(
498                $msb < <$value_type>::BITS as u8,
499                "MSB index out of bounds"
500            );
501            const _: () = assert!(
502                $lsb <= $msb,
503                "LSB must be less than or equal to MSB"
504            );
505            let bit_count = $msb - $lsb + 1;
506            let bit_mask = ((!0 as $value_type)
507                >> (<$value_type>::BITS as u8 - bit_count))
508                << $lsb;
509            <$enum_type>::from_val((self.reg_value() & bit_mask) >> $lsb)
510        }
511        $(
512            #[allow(non_snake_case)]
513            #[allow(dead_code)]
514            $vis const fn $setter(self, val: $enum_type) -> Self {
515                const _: () = assert!(
516                    $msb < <$value_type>::BITS as u8,
517                    "MSB index out of bounds"
518                );
519                const _: () = assert!(
520                    $lsb <= $msb,
521                    "LSB must be less than or equal to MSB"
522                );
523                let bit_count = $msb - $lsb + 1;
524                let bit_mask = ((!0 as $value_type)
525                    >> (<$value_type>::BITS as u8 - bit_count))
526                    << $lsb;
527                let raw = (self.reg_value() & !bit_mask)
528                    | (((val as $value_type) << $lsb) & bit_mask);
529                Self::new(raw)
530            }
531        )?
532        $crate::spmi_register_fields!($value_type, $($tail)*);
533    };
534
535    // 4. In-line Enum field
536    (
537        $value_type:ty,
538        $(#[$attr:meta])*
539        $vis:vis enum $enum_name:ident {
540            $( $variant_name:ident = $variant_val:expr ),* $(,)?
541        },
542        $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
543        $($tail:tt)*
544    ) => {
545        $(#[$attr])*
546        #[allow(non_snake_case)]
547        #[allow(dead_code)]
548        $vis const fn $field(&self) -> Result<$enum_name, $value_type> {
549            const _: () = assert!(
550                $msb < <$value_type>::BITS as u8,
551                "MSB index out of bounds"
552            );
553            const _: () = assert!(
554                $lsb <= $msb,
555                "LSB must be less than or equal to MSB"
556            );
557            let bit_count = $msb - $lsb + 1;
558            let bit_mask = ((!0 as $value_type)
559                >> (<$value_type>::BITS as u8 - bit_count))
560                << $lsb;
561            let val = (self.reg_value() & bit_mask) >> $lsb;
562            $(
563                if val == $enum_name::$variant_name as $value_type {
564                    return Ok($enum_name::$variant_name);
565                }
566            )*
567            Err(val)
568        }
569        $(
570            #[allow(non_snake_case)]
571            #[allow(dead_code)]
572            $vis const fn $setter(self, val: $enum_name) -> Self {
573                const _: () = assert!(
574                    $msb < <$value_type>::BITS as u8,
575                    "MSB index out of bounds"
576                );
577                const _: () = assert!(
578                    $lsb <= $msb,
579                    "LSB must be less than or equal to MSB"
580                );
581                let bit_count = $msb - $lsb + 1;
582                let bit_mask = ((!0 as $value_type)
583                    >> (<$value_type>::BITS as u8 - bit_count))
584                    << $lsb;
585                let raw = (self.reg_value() & !bit_mask)
586                    | (((val as $value_type) << $lsb) & bit_mask);
587                Self::new(raw)
588            }
589        )?
590        $crate::spmi_register_fields!($value_type, $($tail)*);
591    };
592
593    // 5. Custom constant
594    (
595        $value_type:ty,
596        pub const $name:ident : $type:ty = $val:expr;
597        $($tail:tt)*
598    ) => {
599        pub const $name: $type = $val;
600        $crate::spmi_register_fields!($value_type, $($tail)*);
601    };
602}
603
604/// Verifies at compile-time that a list of registers is contiguous.
605#[macro_export]
606#[doc(hidden)]
607macro_rules! assert_contiguous {
608    ($prev:ident, $curr:ident $(, $rest:ident)*) => {
609        const _: () = assert!(
610            $curr::ADDRESS == $prev::ADDRESS
611                + std::mem::size_of::<$prev::Value>() as u16,
612            concat!(
613                "Registers ",
614                stringify!($prev),
615                " and ",
616                stringify!($curr),
617                " are not contiguous"
618            )
619        );
620        $crate::assert_contiguous!($curr $(, $rest)*);
621    };
622    ($last:ident) => {};
623}
624
625/// Defines a struct to group multiple SPMI registers.
626///
627/// This macro generates a public struct named `$name` that holds a
628/// device client and provides methods to access individual registers
629/// defined by `spmi_register!`, as well as `read_bulk` and `write_bulk`
630/// methods for contiguous accesses.
631///
632/// # Arguments
633///
634/// 1.  `$name`: The identifier for the generated register block struct.
635/// 2.  `{ ... }`: A block defining the registers contained within this block.
636///     The format of each register definition is:
637///     `$vis $field_name => $reg_mod,`
638///     -   `$vis`: Visibility of the register accessor method (e.g., `pub`).
639///     -   `$field_name`: The name of the method generated on the block
640///         struct to access the register.
641///     -   `$reg_mod`: The module identifier of the register defined
642///         via `spmi_register!`.
643///
644/// # Examples
645///
646/// ```
647/// spmi_register! {
648///     my_reg, u8, 0x10, RW, {
649///         pub enable, set_enable: 7;
650///     }
651/// }
652///
653/// spmi_register! {
654///     status_reg, u16, 0x12, RO, LE, {
655///         pub ready: 0;
656///     }
657/// }
658///
659/// spmi_register_block! {
660///     pub struct MyDeviceRegisters {
661///         pub control => my_reg,
662///         pub status => status_reg,
663///     }
664/// }
665///
666/// // Usage:
667/// // let regs = MyDeviceRegisters::new(spmi_device);
668/// // let ctrl_val = regs.control().read().await?;
669/// // let is_ready = regs.status().read().await?.ready();
670/// ```
671#[macro_export]
672macro_rules! spmi_register_block {
673    (
674        pub struct $name:ident {
675            $($tail:tt)*
676        }
677    ) => {
678        pub struct $name {
679            pub spmi: $crate::DeviceType,
680        }
681
682        #[allow(dead_code)]
683        impl $name {
684            pub fn new(spmi: $crate::DeviceType) -> Self {
685                Self { spmi }
686            }
687
688            /// Reads a raw byte slice from the contiguous register range.
689            ///
690            /// # Note
691            /// This method is public but hidden because it is required by the
692            /// `spmi_read_contiguous!` macro. Direct use is discouraged.
693            #[doc(hidden)]
694            #[allow(dead_code)]
695            pub async fn read_bulk(
696                &self,
697                address: u16,
698                size: u32,
699            ) -> Result<Vec<u8>, $crate::Error> {
700                let data = $crate::SpmiDevice::read_reg(
701                    &self.spmi,
702                    address,
703                    size,
704                ).await?;
705                if data.len() == size as usize {
706                    Ok(data)
707                } else {
708                    Err($crate::Error::SizeMismatch)
709                }
710            }
711
712            /// Reads a raw byte slice from the contiguous register range into a
713            /// mutable buffer.
714            ///
715            /// # Note
716            /// This method is public but hidden to discourage direct use,
717            /// keeping the bulk API consistent with `read_bulk`.
718            #[doc(hidden)]
719            #[allow(dead_code)]
720            pub async fn read_bulk_into(
721                &self,
722                address: u16,
723                out: &mut [u8],
724            ) -> Result<(), $crate::Error> {
725                let data = $crate::SpmiDevice::read_reg(
726                    &self.spmi,
727                    address,
728                    out.len() as u32,
729                ).await?;
730                if data.len() == out.len() {
731                    out.copy_from_slice(&data);
732                    Ok(())
733                } else {
734                    Err($crate::Error::SizeMismatch)
735                }
736            }
737
738            /// Writes the specified data to the contiguous register range.
739            ///
740            /// # Note
741            /// This method is public but hidden because it is required by the
742            /// `spmi_write_contiguous!` macro. Direct use is discouraged.
743            #[doc(hidden)]
744            #[allow(dead_code)]
745            pub async fn write_bulk(
746                &self,
747                address: u16,
748                data: &[u8],
749            ) -> Result<(), $crate::Error> {
750                $crate::SpmiDevice::write_reg(&self.spmi, address, data).await?;
751                Ok(())
752            }
753
754            spmi_register_block!(@fields $($tail)*);
755        }
756    };
757
758    (@fields) => {};
759
760    // Case 1: Individual register with trailing fields
761    (@fields $vis:vis $field:ident => $reg_mod:ident, $($tail:tt)*) => {
762        #[allow(dead_code)]
763        $vis fn $field(&self) -> $reg_mod::Register<'_> {
764            $reg_mod::Register::new(&self.spmi)
765        }
766        spmi_register_block!(@fields $($tail)*);
767    };
768
769    // Case 2: Individual register at end of token stream
770    (@fields $vis:vis $field:ident => $reg_mod:ident) => {
771        #[allow(dead_code)]
772        $vis fn $field(&self) -> $reg_mod::Register<'_> {
773            $reg_mod::Register::new(&self.spmi)
774        }
775    };
776}
777
778/// Reads multiple contiguous registers in a single async call to the hardware.
779///
780/// This macro accepts an SPMI device proxy and a list of register
781/// modules, calculates the base address and combined size, and performs
782/// a single async contiguous read.
783///
784/// # Arguments
785///
786/// 1.  `$spmi:expr`: The SPMI device proxy.
787/// 2.  `$( $reg:ident ),*`: A comma-separated list of already-declared
788///     register modules.
789///
790/// # Examples
791///
792/// ```
793/// // Read both 'general' and 'status' registers together, update,
794/// // and write back:
795/// // let regs = MySpmiRegisters::new(spmi_proxy);
796/// let (mut general_val, status_val) = spmi_read_contiguous!(
797///     &regs,
798///     my_reg,
799///     status_be_reg
800/// ).await?;
801///
802/// general_val = general_val.set_field1(true);
803///
804/// spmi_write_contiguous!(
805///     &regs,
806///     my_reg => general_val,
807///     status_be_reg => status_val
808/// ).await?;
809/// ```
810#[macro_export]
811macro_rules! spmi_read_contiguous {
812    (
813        $regs:expr,
814        $head:ident $(, $tail:ident )* $(,)?
815    ) => {
816        async {
817            $crate::assert_contiguous!($head $(, $tail)*);
818            let res: Result<
819                ($head::Value, $( $tail::Value ),*),
820                $crate::Error
821            > = async {
822                let base_addr = $head::ADDRESS;
823
824                let total_size = std::mem::size_of::<$head::Value>()
825                    $( + std::mem::size_of::<$tail::Value>() )*;
826
827                let data = $regs.read_bulk(
828                    base_addr,
829                    total_size as u32,
830                ).await?;
831
832                let mut cursor = 0;
833                let $head = $head::Value::from_bytes(
834                    &data[cursor..std::mem::size_of::<$head::Value>()],
835                )?;
836                cursor = std::mem::size_of::<$head::Value>();
837                $(
838                    let end = cursor + std::mem::size_of::<$tail::Value>();
839                    let $tail = $tail::Value::from_bytes(
840                        &data[cursor..end],
841                    )?;
842                    cursor = end;
843                )*
844                let _ = cursor;
845
846                Ok(($head, $( $tail ),*))
847            }.await;
848            res
849        }
850    };
851}
852
853/// Writes multiple contiguous registers in a single async call to the hardware.
854///
855/// This macro accepts an SPMI device proxy and a mapping of register modules
856/// to their values, serializes the values using their correct endianness,
857/// and performs a single async contiguous write.
858///
859/// # Arguments
860///
861/// 1.  `$regs:expr`: The block struct instance.
862/// 2.  `$( $reg:ident => $val:expr ),*`: A comma-separated mapping from
863///     register modules to their values.
864///
865/// # Examples
866///
867/// ```
868/// // Write both 'general' and 'status' registers together:
869/// // let regs = MySpmiRegisters::new(spmi_proxy);
870/// spmi_write_contiguous!(
871///     &regs,
872///     my_reg => val_a,
873///     status_be_reg => val_b
874/// ).await?;
875/// ```
876#[macro_export]
877macro_rules! spmi_write_contiguous {
878    (
879        $regs:expr,
880        $head:ident => $head_val:expr $(, $tail:ident => $tail_val:expr )* $(,)?
881    ) => {
882        async {
883            $crate::assert_contiguous!($head $(, $tail)*);
884            let regs_ref = $regs;
885            let res: Result<(), $crate::Error> = async move {
886                let base_addr = $head::ADDRESS;
887
888                let mut bytes = Vec::new();
889                bytes.extend_from_slice(&$head_val.to_bytes());
890                $(
891                    bytes.extend_from_slice(&$tail_val.to_bytes());
892                )*
893
894                regs_ref.write_bulk(base_addr, &bytes).await?;
895
896                Ok(())
897            }.await;
898            res
899        }
900    };
901}
902
903/// Trait for SPMI devices to abstract register read/write operations.
904///
905/// This trait allows the `Register` and `spmi_register_block!` macros to
906/// interact with any underlying hardware interface or mock device that
907/// implements these basic SPMI read and write operations.
908///
909/// Implementations must handle the low-level transport (e.g., FIDL calls
910/// to a Fuchsia SPMI driver) and handle error mapping.
911#[allow(async_fn_in_trait)]
912pub trait SpmiDevice {
913    /// Reads a contiguous sequence of bytes from the device.
914    ///
915    /// # Arguments
916    ///
917    /// * `address` - The 16-bit base register address to read from.
918    /// * `size` - The number of bytes to read.
919    ///
920    /// # Returns
921    ///
922    /// Returns a vector containing the read bytes on success, or a
923    /// `crate::Error` on failure.
924    async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error>;
925
926    /// Writes a contiguous sequence of bytes to the device.
927    ///
928    /// # Arguments
929    ///
930    /// * `address` - The 16-bit base register address to write to.
931    /// * `data` - The byte slice to write to the device.
932    ///
933    /// # Returns
934    ///
935    /// Returns `Ok(())` on success, or a `crate::Error` on failure.
936    async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error>;
937}
938
939#[cfg(test)]
940/// A mock SPMI device that stores register values in memory.
941/// Used for testing register access without a real device or FIDL connection.
942pub struct TestSpmiDevice {
943    registers: std::sync::Mutex<std::collections::HashMap<u16, u8>>,
944}
945
946#[cfg(test)]
947impl TestSpmiDevice {
948    /// Creates a new empty `TestSpmiDevice`.
949    pub fn new() -> Self {
950        Self { registers: std::sync::Mutex::new(std::collections::HashMap::new()) }
951    }
952}
953
954#[cfg(test)]
955impl SpmiDevice for TestSpmiDevice {
956    async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
957        let regs = self.registers.lock().unwrap();
958        let mut data = Vec::new();
959        for i in 0..size {
960            let addr = address + i as u16;
961            let val = regs.get(&addr).copied().unwrap_or(0);
962            data.push(val);
963        }
964        Ok(data)
965    }
966
967    async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
968        let mut regs = self.registers.lock().unwrap();
969        for (i, &val) in data.iter().enumerate() {
970            let addr = address + i as u16;
971            regs.insert(addr, val);
972        }
973        Ok(())
974    }
975}
976
977#[cfg(test)]
978mod tests {
979    use super::*;
980
981    // Note: For single-byte width registers (u8), explicit endianness is
982    // not required.
983    spmi_register! {
984        test_u8_reg, u8, 0xCD, RW, {
985            pub flag, set_flag: 4;
986            pub field, set_field: 3, 0;
987        }
988    }
989
990    spmi_register_block! {
991        pub struct MockU8Regs {
992            pub test => test_u8_reg,
993        }
994    }
995
996    spmi_register! {
997        test_u16_from_bytes_reg, u16, 0x99, RW, LE, {
998            pub field, set_field: 15, 0;
999        }
1000    }
1001
1002    spmi_register_block! {
1003        pub struct MockU16FromBytesRegs {
1004            pub test => test_u16_from_bytes_reg,
1005        }
1006    }
1007
1008    spmi_register! {
1009        test_u16_contig_reg, u16, 0xCE, RW, LE, {
1010            pub field, set_field: 15, 0;
1011        }
1012    }
1013
1014    #[fuchsia::test]
1015    async fn test_u16_from_bytes() {
1016        let device = TestSpmiDevice::new();
1017        device.write_reg(0x99, &[0x34, 0x12]).await.unwrap();
1018
1019        let regs = MockU16FromBytesRegs::new(device);
1020        let val = regs.test().read().await.unwrap();
1021        assert_eq!(val.reg_value(), 0x1234);
1022    }
1023
1024    #[fuchsia::test]
1025    async fn test_u8_register() {
1026        let device = TestSpmiDevice::new();
1027        device.write_reg(0xCD, &[0x1A]).await.unwrap();
1028
1029        let regs = MockU8Regs::new(device);
1030        let val = regs.test().read().await.unwrap();
1031        assert_eq!(val.reg_value(), 0x1A);
1032        assert_eq!(val.flag(), true);
1033        assert_eq!(val.field(), 0x0A);
1034    }
1035
1036    spmi_register! {
1037        test_reg, u16, 0xAB, RW, LE, {
1038            pub test_bit, set_test_bit: 7;
1039            pub test_field, set_test_field: 3, 0;
1040        }
1041    }
1042
1043    spmi_register_block! {
1044        pub struct MockRegs {
1045            pub test => test_reg,
1046        }
1047    }
1048
1049    #[fuchsia::test]
1050    async fn test_read() {
1051        let device = TestSpmiDevice::new();
1052        device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1053
1054        let regs = MockRegs::new(device);
1055        let val = regs.test().read().await.unwrap();
1056        assert_eq!(val.reg_value(), 0x008A);
1057        assert_eq!(val.test_bit(), true);
1058        assert_eq!(val.test_field(), 0x0A);
1059    }
1060
1061    #[fuchsia::test]
1062    async fn test_write() {
1063        let device = TestSpmiDevice::new();
1064
1065        let regs = MockRegs::new(device);
1066        let v = test_reg::Value::new(0x008A);
1067        regs.test().write(v).await.unwrap();
1068
1069        let data = regs.spmi.read_reg(0xAB, 2).await.unwrap();
1070        assert_eq!(data, &[0x8A, 0x00]);
1071    }
1072
1073    spmi_register! {
1074        test_be_reg, u16, 0xEF, RW, BE, {
1075            pub flag, set_flag: 4;
1076            pub field, set_field: 3, 0;
1077        }
1078    }
1079
1080    spmi_register_block! {
1081        pub struct MockBERegs {
1082            pub test => test_be_reg,
1083        }
1084    }
1085
1086    #[fuchsia::test]
1087    async fn test_be_register() {
1088        let device = TestSpmiDevice::new();
1089        device.write_reg(0xEF, &[0x00, 0x8A]).await.unwrap();
1090
1091        let regs = MockBERegs::new(device);
1092        let val = regs.test().read().await.unwrap();
1093        assert_eq!(val.reg_value(), 0x8A);
1094    }
1095
1096    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
1097    #[repr(u16)]
1098    pub enum PowerMode {
1099        Normal = 0,
1100        Hibernate = 1,
1101        LowPower = 2,
1102        Unknown = 0xFFFF,
1103    }
1104
1105    impl PowerMode {
1106        pub const fn from_val(val: u16) -> Self {
1107            match val {
1108                0 => PowerMode::Normal,
1109                1 => PowerMode::Hibernate,
1110                2 => PowerMode::LowPower,
1111                _ => PowerMode::Unknown,
1112            }
1113        }
1114    }
1115
1116    spmi_register! {
1117        test_enum_reg, u16, 0x44, RW, LE, {
1118            pub enum PowerMode, mode, set_mode: 3, 2;
1119        }
1120    }
1121
1122    spmi_register_block! {
1123        pub struct MockEnumRegs {
1124            pub test => test_enum_reg,
1125        }
1126    }
1127
1128    #[fuchsia::test]
1129    async fn test_enum_register() {
1130        let device = TestSpmiDevice::new();
1131
1132        let regs = MockEnumRegs::new(device);
1133        let v = test_enum_reg::Value::new(0).set_mode(PowerMode::Hibernate);
1134        regs.test().write(v).await.unwrap();
1135
1136        let data = regs.spmi.read_reg(0x44, 2).await.unwrap();
1137        assert_eq!(data, &[0x04, 0x00]);
1138    }
1139
1140    spmi_register! {
1141        test_inline_enum_reg, u16, 0x55, RW, LE, {
1142            pub enum InlineMode {
1143                A = 0,
1144                B = 1,
1145            }, mode, set_mode: 1, 0;
1146        }
1147    }
1148
1149    spmi_register_block! {
1150        pub struct MockInlineEnumRegs {
1151            pub test => test_inline_enum_reg,
1152        }
1153    }
1154
1155    #[fuchsia::test]
1156    async fn test_inline_enum() {
1157        let device = TestSpmiDevice::new();
1158
1159        let regs = MockInlineEnumRegs::new(device);
1160        let v = test_inline_enum_reg::Value::new(1).set_mode(test_inline_enum_reg::InlineMode::B);
1161        assert_eq!(v.mode(), Ok(test_inline_enum_reg::InlineMode::B));
1162        regs.test().write(v).await.unwrap();
1163
1164        let data = regs.spmi.read_reg(0x55, 2).await.unwrap();
1165        assert_eq!(data, &[0x01, 0x00]);
1166    }
1167
1168    #[fuchsia::test]
1169    async fn test_contiguous_read_write() {
1170        let device = TestSpmiDevice::new();
1171        device.write_reg(0xCD, &[0x1A, 0x34, 0x12]).await.unwrap();
1172
1173        spmi_register_block! {
1174            pub struct ContiguousRegs {
1175                pub r1 => test_u8_reg,
1176                pub r2 => test_u16_contig_reg,
1177            }
1178        }
1179        let regs = ContiguousRegs::new(device);
1180
1181        let (val_1, val_2) =
1182            spmi_read_contiguous!(&regs, test_u8_reg, test_u16_contig_reg,).await.unwrap();
1183
1184        assert_eq!(val_1.reg_value(), 0x1A);
1185        assert_eq!(val_2.reg_value(), 0x1234);
1186
1187        spmi_write_contiguous!(
1188            &regs,
1189            test_u8_reg => val_1,
1190            test_u16_contig_reg => val_2
1191        )
1192        .await
1193        .unwrap();
1194
1195        let data = regs.spmi.read_reg(0xCD, 3).await.unwrap();
1196        assert_eq!(data, &[0x1A, 0x34, 0x12]);
1197    }
1198
1199    #[fuchsia::test]
1200    async fn test_read_write_bulk() {
1201        let device = TestSpmiDevice::new();
1202        device.write_reg(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1203
1204        let regs = MockU8Regs::new(device);
1205        let bytes = regs.read_bulk(0x88, 3).await.unwrap();
1206        assert_eq!(bytes, vec![0x1A, 0x2B, 0x3C]);
1207
1208        let mut buf = [0u8; 3];
1209        regs.read_bulk_into(0x88, &mut buf).await.unwrap();
1210        assert_eq!(buf, [0x1A, 0x2B, 0x3C]);
1211
1212        regs.write_bulk(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1213        let data = regs.spmi.read_reg(0x88, 3).await.unwrap();
1214        assert_eq!(data, &[0x1A, 0x2B, 0x3C]);
1215    }
1216}