Skip to main content

fake_mmio/
device.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 crate::atomic::AtomicMmio;
6use crate::operand::MmioOperand;
7use mmio::{IndexedRegister, Mmio, MmioExt, Register};
8
9/// Provides a wrapper around `Mmio` and `AtomicMmio` implementations for
10/// register access in tests from the perspective of a device.
11///
12/// This struct effectively sidesteps the `WritableRegister` and
13/// `ReadableRegister` traits, given it's meant to be used by a faked peripheral
14/// Mmio.
15pub struct FakeMmioDevice<M>(M);
16
17impl<M> FakeMmioDevice<M> {
18    /// Creates a new [`FakeMmioDevice`] wrapping the MMIO implementation `m`.
19    pub fn new(m: M) -> Self {
20        Self(m)
21    }
22}
23
24impl<M: Mmio> FakeMmioDevice<M> {
25    /// Reads a register `R`.
26    pub fn read<R: Register>(&self) -> R {
27        R::from_raw(self.0.load::<R::Value>(R::OFFSET))
28    }
29
30    /// Writes a register `R` with value `v`.
31    pub fn write<R: Register>(&mut self, v: R) {
32        self.0.store::<R::Value>(R::OFFSET, v.to_raw())
33    }
34
35    /// Clears the specified bits in `v` for register `R`.
36    pub fn clear_bits<R: Register>(&mut self, v: R::Value) {
37        self.0.masked_modify(R::OFFSET, v, !v)
38    }
39
40    /// Sets the specified bits in `v` for register `R`.
41    pub fn set_bits<R: Register>(&mut self, v: R::Value) {
42        self.0.masked_modify(R::OFFSET, v, v)
43    }
44
45    /// Reads an indexed register `R` at `index`.
46    pub fn read_indexed<R: IndexedRegister>(&self, index: usize) -> R {
47        R::from_raw(self.0.load::<R::Value>(R::BASE_OFFSET + index * R::STRIDE))
48    }
49
50    /// Writes an indexed register `R` with value `v` at `index`.
51    pub fn write_indexed<R: IndexedRegister>(&mut self, index: usize, v: R) {
52        self.0.store::<R::Value>(R::BASE_OFFSET + index * R::STRIDE, v.to_raw())
53    }
54}
55
56impl<M: AtomicMmio> FakeMmioDevice<M> {
57    /// Atomically swaps register `R` with value `v` and returns the previous
58    /// value.
59    pub fn swap<R: Register<Value: MmioOperand>>(&self, v: R) -> R {
60        R::from_raw(self.0.swap(R::OFFSET, v.to_raw()))
61    }
62
63    /// Atomically takes register `R`, replacing it with `R::default()`, and
64    /// returns the previous value.
65    pub fn take<R: Register<Value: MmioOperand> + Default>(&self) -> R {
66        self.swap(R::default())
67    }
68
69    /// Atomically performs bitwise OR on register `R` with `v` and returns the
70    /// previous value.
71    pub fn fetch_or<R: Register<Value: MmioOperand>>(&self, v: R) -> R {
72        R::from_raw(self.0.fetch_or(R::OFFSET, v.to_raw()))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::CachedVmoMemory;
80
81    const VMO_SIZE: usize = 4096;
82
83    mmio::register! {
84        #[register(offset = 0x0, mode = RW)]
85        pub struct TestReg32(u32);
86
87        #[register(offset = 0x8, mode = RW)]
88        pub struct TestReg8(u8);
89
90        #[indexed_register(offset = 0x10, stride = 4, count = 4, mode = RW)]
91        pub struct TestIndexedReg32(u32);
92    }
93
94    #[test]
95    fn test_read_write() {
96        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
97        let mut dev = FakeMmioDevice::new(mmio);
98
99        let reg32 = TestReg32::from_raw(0x1234_5678);
100        dev.write(reg32);
101        assert_eq!(dev.read::<TestReg32>(), reg32);
102
103        let reg8 = TestReg8::from_raw(0xab);
104        dev.write(reg8);
105        assert_eq!(dev.read::<TestReg8>(), reg8);
106    }
107
108    #[test]
109    fn test_set_and_clear_bits() {
110        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
111        let mut dev = FakeMmioDevice::new(mmio);
112
113        dev.write(TestReg32::from_raw(0xf0f0_f0f0));
114
115        dev.clear_bits::<TestReg32>(0xa0a0_a0a0);
116        assert_eq!(dev.read::<TestReg32>(), TestReg32::from_raw(0x5050_5050));
117
118        dev.set_bits::<TestReg32>(0x0505_0505);
119        assert_eq!(dev.read::<TestReg32>(), TestReg32::from_raw(0x5555_5555));
120    }
121
122    #[test]
123    fn test_indexed_read_write() {
124        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
125        let mut dev = FakeMmioDevice::new(mmio);
126
127        let reg0 = TestIndexedReg32::from_raw(0x1111_1111);
128        let reg1 = TestIndexedReg32::from_raw(0x2222_2222);
129        let reg2 = TestIndexedReg32::from_raw(0x3333_3333);
130
131        dev.write_indexed(0, reg0);
132        dev.write_indexed(1, reg1);
133        dev.write_indexed(2, reg2);
134
135        assert_eq!(dev.read_indexed::<TestIndexedReg32>(0), reg0);
136        assert_eq!(dev.read_indexed::<TestIndexedReg32>(1), reg1);
137        assert_eq!(dev.read_indexed::<TestIndexedReg32>(2), reg2);
138    }
139
140    #[test]
141    fn test_atomic_swap() {
142        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
143        let dev = FakeMmioDevice::new(mmio);
144
145        let new_val = TestReg32::from_raw(0xdead_beef);
146        let old = dev.swap(new_val);
147        assert_eq!(old, TestReg32::from_raw(0));
148        assert_eq!(dev.read::<TestReg32>(), new_val);
149    }
150
151    #[test]
152    fn test_atomic_take() {
153        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
154        let dev = FakeMmioDevice::new(mmio);
155
156        let val = TestReg32::from_raw(0xcafe_babe);
157        let _ = dev.swap(val);
158        let taken = dev.take::<TestReg32>();
159        assert_eq!(taken, val);
160        assert_eq!(dev.read::<TestReg32>(), TestReg32::default());
161    }
162
163    #[test]
164    fn test_atomic_fetch_or() {
165        let mmio = CachedVmoMemory::new_mapped(VMO_SIZE).expect("Failed to map VMO.");
166        let dev = FakeMmioDevice::new(mmio);
167
168        let initial = TestReg32::from_raw(0x0000_ffff);
169        let _ = dev.swap(initial);
170        let old = dev.fetch_or(TestReg32::from_raw(0xffff_0000));
171        assert_eq!(old, initial);
172        assert_eq!(dev.read::<TestReg32>(), TestReg32::from_raw(0xffff_ffff));
173    }
174}