Skip to main content

cu_embedded_registry/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6#[cfg(not(feature = "std"))]
7use alloc::{boxed::Box, format};
8use core::any::{Any, TypeId};
9use cu29::prelude::*;
10use embedded_hal as eh1;
11use embedded_io::{Read, Write};
12use spin::Mutex;
13
14type Entry = Box<dyn Any + Send>;
15
16struct TypedSlot {
17    type_id: TypeId,
18    entry: Entry,
19}
20
21macro_rules! define_registry {
22    ($name:ident) => {
23        struct $name {
24            entries: [Option<TypedSlot>; 8],
25        }
26
27        impl $name {
28            const fn new() -> Self {
29                Self {
30                    entries: [None, None, None, None, None, None, None, None],
31                }
32            }
33        }
34    };
35}
36
37define_registry!(SerialSlots);
38define_registry!(SpiSlots);
39define_registry!(CsSlots);
40define_registry!(DelaySlots);
41
42static SERIAL_SLOTS: Mutex<SerialSlots> = Mutex::new(SerialSlots::new());
43static SPI_SLOTS: Mutex<SpiSlots> = Mutex::new(SpiSlots::new());
44static CS_SLOTS: Mutex<CsSlots> = Mutex::new(CsSlots::new());
45static DELAY_SLOTS: Mutex<DelaySlots> = Mutex::new(DelaySlots::new());
46
47pub const MAX_SERIAL_SLOTS: usize = 8;
48pub const MAX_SPI_SLOTS: usize = 8;
49pub const MAX_CS_SLOTS: usize = 8;
50pub const MAX_DELAY_SLOTS: usize = 8;
51
52fn register_inner<T>(
53    slots: &mut [Option<TypedSlot>],
54    max: usize,
55    slot: usize,
56    value: T,
57) -> CuResult<()>
58where
59    T: Any + Send + 'static,
60{
61    if slot >= max {
62        return Err(CuError::from(format!(
63            "Registry slot {slot} out of range (max {max})"
64        )));
65    }
66    slots[slot] = Some(TypedSlot {
67        type_id: TypeId::of::<T>(),
68        entry: Box::new(value),
69    });
70    Ok(())
71}
72
73fn take_inner<T>(slots: &mut [Option<TypedSlot>], max: usize, slot: usize) -> Option<T>
74where
75    T: Any + Send + 'static,
76{
77    if slot >= max {
78        return None;
79    }
80    let record = slots[slot].take()?;
81    if record.type_id != TypeId::of::<T>() {
82        slots[slot] = Some(record);
83        return None;
84    }
85    record.entry.downcast::<T>().map(|boxed| *boxed).ok()
86}
87
88fn status_inner(slots: &[Option<TypedSlot>], max: usize) -> [bool; 8] {
89    let mut status = [false; 8];
90    let slice = &slots[..max.min(8)];
91    for (i, slot) in slice.iter().enumerate() {
92        status[i] = slot.is_some();
93    }
94    status
95}
96
97/// Register a serial port at the specified slot index.
98///
99/// Returns `CuResult<()>` indicating success or failure
100pub fn register<S, E>(slot: usize, serial: S) -> CuResult<()>
101where
102    S: Write<Error = E> + Read<Error = E> + Send + 'static,
103{
104    let mut slots = SERIAL_SLOTS.lock();
105    register_inner(&mut slots.entries, MAX_SERIAL_SLOTS, slot, serial)
106}
107
108/// Take (consume) a serial port from the specified slot index.
109///
110/// Returns `Some(S)` if the slot contains a serial port of type S, `None` otherwise.
111/// Note: This consumes the serial port from the slot, leaving it empty.
112pub fn take<S, E>(slot: usize) -> Option<S>
113where
114    S: Write<Error = E> + Read<Error = E> + Send + 'static,
115{
116    let mut slots = SERIAL_SLOTS.lock();
117    take_inner(&mut slots.entries, MAX_SERIAL_SLOTS, slot)
118}
119
120/// Get the serial slot status for debugging purposes.
121pub fn slot_status() -> [bool; 8] {
122    let slots = SERIAL_SLOTS.lock();
123    status_inner(&slots.entries, MAX_SERIAL_SLOTS)
124}
125
126/// Register an SPI bus that implements embedded-hal 1.0 `SpiBus`.
127pub fn register_spi<S>(slot: usize, spi: S) -> CuResult<()>
128where
129    S: eh1::spi::SpiBus<u8> + Send + 'static,
130    S::Error: Send + 'static,
131{
132    let mut slots = SPI_SLOTS.lock();
133    register_inner(&mut slots.entries, MAX_SPI_SLOTS, slot, spi)
134}
135
136/// Consume an SPI bus from the specified slot index.
137pub fn take_spi<S>(slot: usize) -> Option<S>
138where
139    S: eh1::spi::SpiBus<u8> + Send + 'static,
140    S::Error: Send + 'static,
141{
142    let mut slots = SPI_SLOTS.lock();
143    take_inner(&mut slots.entries, MAX_SPI_SLOTS, slot)
144}
145
146/// Register a chip-select output pin implementing embedded-hal 1.0 `OutputPin`.
147pub fn register_cs<P>(slot: usize, pin: P) -> CuResult<()>
148where
149    P: eh1::digital::OutputPin + Send + 'static,
150    P::Error: Send + 'static,
151{
152    let mut slots = CS_SLOTS.lock();
153    register_inner(&mut slots.entries, MAX_CS_SLOTS, slot, pin)
154}
155
156/// Consume a chip-select output pin from the specified slot index.
157pub fn take_cs<P>(slot: usize) -> Option<P>
158where
159    P: eh1::digital::OutputPin + Send + 'static,
160    P::Error: Send + 'static,
161{
162    let mut slots = CS_SLOTS.lock();
163    take_inner(&mut slots.entries, MAX_CS_SLOTS, slot)
164}
165
166/// Register a delay provider implementing embedded-hal 1.0 `DelayNs`.
167pub fn register_delay<D>(slot: usize, delay: D) -> CuResult<()>
168where
169    D: eh1::delay::DelayNs + Send + 'static,
170{
171    let mut slots = DELAY_SLOTS.lock();
172    register_inner(&mut slots.entries, MAX_DELAY_SLOTS, slot, delay)
173}
174
175/// Consume a delay provider from the specified slot index.
176pub fn take_delay<D>(slot: usize) -> Option<D>
177where
178    D: eh1::delay::DelayNs + Send + 'static,
179{
180    let mut slots = DELAY_SLOTS.lock();
181    take_inner(&mut slots.entries, MAX_DELAY_SLOTS, slot)
182}
183
184/// Get the SPI slot status for debugging purposes.
185pub fn spi_slot_status() -> [bool; 8] {
186    let slots = SPI_SLOTS.lock();
187    status_inner(&slots.entries, MAX_SPI_SLOTS)
188}
189
190/// Get the chip-select slot status for debugging purposes.
191pub fn cs_slot_status() -> [bool; 8] {
192    let slots = CS_SLOTS.lock();
193    status_inner(&slots.entries, MAX_CS_SLOTS)
194}
195
196/// Get the delay slot status for debugging purposes.
197pub fn delay_slot_status() -> [bool; 8] {
198    let slots = DELAY_SLOTS.lock();
199    status_inner(&slots.entries, MAX_DELAY_SLOTS)
200}
201
202/// Clear all slots. Useful for testing.
203#[cfg(test)]
204pub fn clear_all() {
205    {
206        let mut slots = SERIAL_SLOTS.lock();
207        for slot in &mut slots.entries {
208            *slot = None;
209        }
210    }
211    {
212        let mut slots = SPI_SLOTS.lock();
213        for slot in &mut slots.entries {
214            *slot = None;
215        }
216    }
217    {
218        let mut slots = CS_SLOTS.lock();
219        for slot in &mut slots.entries {
220            *slot = None;
221        }
222    }
223    {
224        let mut slots = DELAY_SLOTS.lock();
225        for slot in &mut slots.entries {
226            *slot = None;
227        }
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use embedded_io::{ErrorKind, ErrorType};
235
236    #[derive(Debug, Clone, Copy)]
237    struct TestError;
238
239    impl embedded_io::Error for TestError {
240        fn kind(&self) -> ErrorKind {
241            ErrorKind::Other
242        }
243    }
244
245    struct MockSerial;
246
247    impl ErrorType for MockSerial {
248        type Error = TestError;
249    }
250
251    impl embedded_io::Write for MockSerial {
252        fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
253            Ok(0)
254        }
255
256        fn flush(&mut self) -> Result<(), Self::Error> {
257            Ok(())
258        }
259    }
260
261    impl embedded_io::Read for MockSerial {
262        fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
263            Ok(0)
264        }
265    }
266
267    #[derive(Debug, Clone, Copy)]
268    struct MockHalError;
269
270    impl eh1::spi::Error for MockHalError {
271        fn kind(&self) -> eh1::spi::ErrorKind {
272            eh1::spi::ErrorKind::Other
273        }
274    }
275
276    impl eh1::digital::Error for MockHalError {
277        fn kind(&self) -> eh1::digital::ErrorKind {
278            eh1::digital::ErrorKind::Other
279        }
280    }
281
282    struct MockSpi;
283
284    impl eh1::spi::ErrorType for MockSpi {
285        type Error = MockHalError;
286    }
287
288    impl eh1::spi::SpiBus<u8> for MockSpi {
289        fn read(&mut self, _words: &mut [u8]) -> Result<(), Self::Error> {
290            Ok(())
291        }
292
293        fn write(&mut self, _words: &[u8]) -> Result<(), Self::Error> {
294            Ok(())
295        }
296
297        fn transfer(&mut self, _read: &mut [u8], _write: &[u8]) -> Result<(), Self::Error> {
298            Ok(())
299        }
300
301        fn transfer_in_place(&mut self, _words: &mut [u8]) -> Result<(), Self::Error> {
302            Ok(())
303        }
304
305        fn flush(&mut self) -> Result<(), Self::Error> {
306            Ok(())
307        }
308    }
309
310    struct OtherSpi;
311
312    impl eh1::spi::ErrorType for OtherSpi {
313        type Error = MockHalError;
314    }
315
316    impl eh1::spi::SpiBus<u8> for OtherSpi {
317        fn read(&mut self, _words: &mut [u8]) -> Result<(), Self::Error> {
318            Ok(())
319        }
320
321        fn write(&mut self, _words: &[u8]) -> Result<(), Self::Error> {
322            Ok(())
323        }
324
325        fn transfer(&mut self, _read: &mut [u8], _write: &[u8]) -> Result<(), Self::Error> {
326            Ok(())
327        }
328
329        fn transfer_in_place(&mut self, _words: &mut [u8]) -> Result<(), Self::Error> {
330            Ok(())
331        }
332
333        fn flush(&mut self) -> Result<(), Self::Error> {
334            Ok(())
335        }
336    }
337
338    struct MockCs;
339
340    impl eh1::digital::ErrorType for MockCs {
341        type Error = MockHalError;
342    }
343
344    impl eh1::digital::OutputPin for MockCs {
345        fn set_low(&mut self) -> Result<(), Self::Error> {
346            Ok(())
347        }
348
349        fn set_high(&mut self) -> Result<(), Self::Error> {
350            Ok(())
351        }
352    }
353
354    struct OtherCs;
355
356    impl eh1::digital::ErrorType for OtherCs {
357        type Error = MockHalError;
358    }
359
360    impl eh1::digital::OutputPin for OtherCs {
361        fn set_low(&mut self) -> Result<(), Self::Error> {
362            Ok(())
363        }
364
365        fn set_high(&mut self) -> Result<(), Self::Error> {
366            Ok(())
367        }
368    }
369
370    struct MockDelay;
371
372    impl eh1::delay::DelayNs for MockDelay {
373        fn delay_ns(&mut self, _ns: u32) {}
374    }
375
376    struct OtherDelay;
377
378    impl eh1::delay::DelayNs for OtherDelay {
379        fn delay_ns(&mut self, _ns: u32) {}
380    }
381
382    #[test]
383    fn test_register_and_take_serial() {
384        clear_all();
385
386        let serial = MockSerial;
387        assert!(register(0, serial).is_ok());
388
389        let status = slot_status();
390        assert!(status[0]);
391        assert!(!status[1]);
392
393        let recovered: Option<MockSerial> = take(0);
394        assert!(recovered.is_some());
395
396        let status = slot_status();
397        assert!(!status[0]);
398    }
399
400    #[test]
401    fn test_register_and_take_spi() {
402        clear_all();
403        assert!(register_spi(0, MockSpi).is_ok());
404        assert!(spi_slot_status()[0]);
405        assert!(take_spi::<MockSpi>(0).is_some());
406        assert!(!spi_slot_status()[0]);
407
408        // Type mismatch should preserve the slot
409        assert!(register_spi(1, MockSpi).is_ok());
410        assert!(take_spi::<OtherSpi>(1).is_none());
411        assert!(spi_slot_status()[1]);
412    }
413
414    #[test]
415    fn test_register_and_take_cs() {
416        clear_all();
417        assert!(register_cs(0, MockCs).is_ok());
418        assert!(cs_slot_status()[0]);
419        assert!(take_cs::<MockCs>(0).is_some());
420        assert!(!cs_slot_status()[0]);
421
422        assert!(register_cs(1, MockCs).is_ok());
423        assert!(take_cs::<OtherCs>(1).is_none());
424        assert!(cs_slot_status()[1]);
425    }
426
427    #[test]
428    fn test_register_and_take_delay() {
429        clear_all();
430        assert!(register_delay(0, MockDelay).is_ok());
431        assert!(delay_slot_status()[0]);
432        assert!(take_delay::<MockDelay>(0).is_some());
433        assert!(!delay_slot_status()[0]);
434
435        assert!(register_delay(1, MockDelay).is_ok());
436        assert!(take_delay::<OtherDelay>(1).is_none());
437        assert!(delay_slot_status()[1]);
438    }
439
440    #[test]
441    fn test_slot_bounds() {
442        clear_all();
443        let serial = MockSerial;
444        assert!(register(MAX_SERIAL_SLOTS, serial).is_err());
445
446        let recovered: Option<MockSerial> = take(MAX_SERIAL_SLOTS);
447        assert!(recovered.is_none());
448
449        assert!(register_spi(MAX_SPI_SLOTS, MockSpi).is_err());
450        assert!(take_spi::<MockSpi>(MAX_SPI_SLOTS).is_none());
451
452        assert!(register_cs(MAX_CS_SLOTS, MockCs).is_err());
453        assert!(take_cs::<MockCs>(MAX_CS_SLOTS).is_none());
454
455        assert!(register_delay(MAX_DELAY_SLOTS, MockDelay).is_err());
456        assert!(take_delay::<MockDelay>(MAX_DELAY_SLOTS).is_none());
457    }
458}