Skip to main content

spmi_hwreg/
lib.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
5//! # `spmi-hwreg`
6//! Rust SPMI register access library matching the MMIO hwreg paradigm.
7
8#![deny(missing_docs)]
9
10use thiserror::Error as ThisError;
11
12/// The device type used by this crate for hardware communication.
13///
14/// In production, this resolves to the FIDL `DeviceProxy` representing
15/// the SPMI device.
16#[cfg(not(test))]
17pub type DeviceType = fidl_fuchsia_hardware_spmi::DeviceProxy;
18
19/// The device type used by this crate during tests.
20///
21/// In test environments, this resolves to `TestSpmiDevice` to allow
22/// in-memory mocking of register state.
23#[cfg(test)]
24pub type DeviceType = crate::common::TestSpmiDevice;
25
26/// Common definitions and macros for SPMI registers.
27///
28/// This module contains the underlying traits, generic structs, and macros
29/// used to construct register layouts and blocks.
30#[macro_use]
31pub mod common;
32pub use common::*;
33pub use zerocopy as zerocopy_reexport;
34pub use zerocopy::byteorder::{BigEndian, LittleEndian, U16, U32};
35pub use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
36
37impl SpmiDevice for fidl_fuchsia_hardware_spmi::DeviceProxy {
38    async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
39        self.register_read(address, size)
40            .await
41            .map_err(|e| Error::Fidl(e.into()))?
42            .map_err(|e| Error::Spmi(e))
43    }
44
45    async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
46        self.register_write(address, data)
47            .await
48            .map_err(|e| Error::Fidl(e.into()))?
49            .map_err(|e| Error::Spmi(e))
50    }
51}
52
53/// A generic register accessor parameterized by value type, mode, and address.
54///
55/// This is a convenience alias that binds `Register` to the
56/// platform-specific `DeviceType`.
57///
58/// # Generics
59/// * `T` - The typed register value (e.g., generated by `spmi_register!`).
60/// * `M` - The access mode (`ReadOnly`, `WriteOnly`, `ReadWrite`).
61/// * `ADDR` - The register address.
62pub type Register<'a, T, M, const ADDR: u16> = _Register<'a, T, M, crate::DeviceType, ADDR>;
63
64/// Error type for the `spmi-hwreg` crate.
65#[derive(Debug, ThisError)]
66pub enum Error {
67    /// A transport or framework error occurred during FIDL communication.
68    #[error("FIDL error: {0}")]
69    Fidl(fidl::Error),
70    /// The SPMI driver returned a protocol-level error (e.g.,
71    /// transaction timeout or NACK).
72    #[error("SPMI error: {0:?}")]
73    Spmi(fidl_fuchsia_hardware_spmi::DriverError),
74    /// The data returned from a read operation did not match the
75    /// expected size of the register.
76    #[error("Size mismatch in read operation")]
77    SizeMismatch,
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use fidl::endpoints::create_proxy_and_stream;
84    use fidl_fuchsia_hardware_spmi as fspmi;
85    use futures::StreamExt;
86
87    #[fuchsia::test]
88    async fn test_spmi_device_read_write() {
89        let (proxy, mut stream) = create_proxy_and_stream::<fspmi::DeviceMarker>();
90
91        fuchsia_async::Task::local(async move {
92            while let Some(Ok(req)) = stream.next().await {
93                match req {
94                    fspmi::DeviceRequest::RegisterRead { address, size_bytes, responder } => {
95                        assert_eq!(address, 0x12);
96                        assert_eq!(size_bytes, 2);
97                        responder.send(Ok(&[0xAA, 0x55])).unwrap();
98                    }
99                    fspmi::DeviceRequest::RegisterWrite { address, data, responder } => {
100                        assert_eq!(address, 0x34);
101                        assert_eq!(data, &[0xDE, 0xAD]);
102                        responder.send(Ok(())).unwrap();
103                    }
104                    _ => panic!("Unexpected request"),
105                }
106            }
107        })
108        .detach();
109
110        // Test read_reg
111        let read_result = proxy.read_reg(0x12, 2).await.unwrap();
112        assert_eq!(read_result, vec![0xAA, 0x55]);
113
114        // Test write_reg
115        let write_result = proxy.write_reg(0x34, &[0xDE, 0xAD]).await;
116        assert!(write_result.is_ok());
117    }
118}