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}