esb_ng/
buffer.rs

1use crate::{
2    app::{Addresses, EsbApp},
3    irq::{Disabled, EsbIrq, IrqTimer},
4    peripherals::{EsbRadio, EsbTimer},
5    Config, Error,
6};
7use core::{
8    marker::PhantomData,
9    sync::atomic::{AtomicBool, Ordering},
10};
11use bbq2::{nicknames::Texas, traits::notifier::maitake::MaiNotSpsc};
12use nrf_pac::radio::Radio;
13
14/// This is the backing structure for the ESB interface
15///
16/// It is intended to live at `'static` scope, and provides
17/// storage for the `EsbApp` and `EsbIrq` interfaces
18///
19/// ## Creating at static scope
20///
21/// Currently due to lacking const generics, the UX for this
22/// isn't great. You'll probably want something like this:
23///
24/// ## NOTE
25///
26/// Although the members of this struct are public, due to const
27/// generic limitations, they are not intended to be used directly,
28/// outside of `static` creation.
29///
30/// This could cause unintended, though not undefined, behavior.
31///
32/// TL;DR: It's not unsafe, but it's also not going to work correctly.
33///
34/// ```rust
35/// // This creates an ESB storage structure with room for
36/// // 512 bytes of outgoing packets (including headers),
37/// // and 256 bytes of incoming packets (including
38/// // headers).
39/// # use esb::{BBBuffer, consts::*, ConstBBBuffer, EsbBuffer};
40/// # use core::sync::atomic::AtomicBool;
41/// static BUFFER: EsbBuffer<U512, U256> = EsbBuffer {
42///     app_to_radio_buf: BBBuffer( ConstBBBuffer::new() ),
43///     radio_to_app_buf: BBBuffer( ConstBBBuffer::new() ),
44///     timer_flag: AtomicBool::new(false),
45/// };
46/// ```
47pub struct EsbBuffer<const OUT: usize, const IN: usize>
48{
49    pub app_to_radio_buf: Texas<OUT, MaiNotSpsc>,
50    pub radio_to_app_buf: Texas<IN, MaiNotSpsc>,
51    pub timer_flag: AtomicBool,
52}
53
54impl<const OUT: usize, const IN: usize> EsbBuffer<OUT, IN>
55{
56    /// Attempt to split the `static` buffer into handles for Interrupt and App context
57    ///
58    /// This function will only succeed once. If the underlying buffers have also
59    /// been split directly, this function will also fail.
60    ///
61    /// Upon splitting, the Radio will be initialized and set to
62    /// [IdleTx](enum.State.html#variant.IdleTx).
63    #[allow(clippy::type_complexity)]
64    pub fn try_split<T: EsbTimer>(
65        &'static self,
66        timer: T,
67        radio: Radio,
68        addresses: Addresses,
69        config: Config,
70    ) -> Result<
71        (
72            EsbApp<OUT, IN>,
73            EsbIrq<OUT, IN, T, Disabled>,
74            IrqTimer<T>,
75        ),
76        Error,
77    > {
78        let (atr_prod, atr_cons) = (
79            self.app_to_radio_buf.framed_producer(),
80            self.app_to_radio_buf.framed_consumer(),
81        );
82        let (rta_prod, rta_cons) = (
83            self.radio_to_app_buf.framed_producer(),
84            self.radio_to_app_buf.framed_consumer(),
85        );
86
87        // Clear the timer flag
88        self.timer_flag.store(false, Ordering::Release);
89
90        let app = EsbApp {
91            prod_to_radio: atr_prod,
92            cons_from_radio: rta_cons,
93            maximum_payload: config.maximum_payload_size,
94        };
95
96        let mut irq = EsbIrq {
97            prod_to_app: rta_prod,
98            cons_from_app: atr_cons,
99            timer,
100            radio: EsbRadio::new(radio),
101            state: Disabled,
102            addresses,
103            attempts: 0,
104            timer_flag: &self.timer_flag,
105            config,
106        };
107
108        let irq_timer = IrqTimer {
109            timer_flag: &self.timer_flag,
110            _timer: PhantomData,
111        };
112
113        irq.radio.init(
114            irq.config.maximum_payload_size,
115            irq.config.tx_power,
116            &irq.addresses,
117        );
118        irq.timer.init();
119
120        Ok((app, irq, irq_timer))
121    }
122}