1use crate::{
6 radio::{self, RadioBuffer, RfConfig, RxConfig, RxMode},
7 region, AppSKey, Downlink, NewSKey,
8};
9use heapless::Vec;
10use lorawan::{self, keys::CryptoFactory};
11use lorawan::{maccommands::DownlinkMacCommand, parser::DevAddr};
12
13pub type FcntDown = u32;
14pub type FcntUp = u32;
15
16mod session;
17use rand_core::RngCore;
18pub use session::{Session, SessionKeys};
19
20mod otaa;
21pub use otaa::NetworkCredentials;
22
23use crate::async_device;
24use crate::nb_device;
25
26pub(crate) mod uplink;
27
28#[derive(Copy, Clone, Debug)]
29pub(crate) enum Frame {
30 Join,
31 Data,
32}
33
34#[derive(Copy, Clone, Debug)]
35pub(crate) enum Window {
36 _1,
37 _2,
38}
39
40#[derive(Debug, PartialEq, Clone, Copy)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct Configuration {
44 pub(crate) data_rate: region::DR,
45 rx1_delay: u32,
46 join_accept_delay1: u32,
47 join_accept_delay2: u32,
48}
49
50impl Configuration {
51 fn handle_downlink_macs(
52 &mut self,
53 region: &mut region::Configuration,
54 uplink: &mut uplink::Uplink,
55 cmds: lorawan::maccommands::MacCommandIterator<DownlinkMacCommand>,
56 ) {
57 use uplink::MacAnsTrait;
58 for cmd in cmds {
59 match cmd {
60 DownlinkMacCommand::LinkADRReq(payload) => {
61 region.set_channel_mask(
63 payload.redundancy().channel_mask_control(),
64 payload.channel_mask(),
65 );
66 uplink.adr_ans.add();
67 }
68 DownlinkMacCommand::RXTimingSetupReq(payload) => {
69 self.rx1_delay = del_to_delay_ms(payload.delay());
70 uplink.ack_rx_delay();
71 }
72 _ => (),
73 }
74 }
75 }
76}
77
78pub(crate) struct Mac {
79 pub configuration: Configuration,
80 pub region: region::Configuration,
81 board_eirp: BoardEirp,
82 state: State,
83}
84
85struct BoardEirp {
86 max_power: u8,
87 antenna_gain: i8,
88}
89
90#[allow(clippy::large_enum_variant)]
91enum State {
92 Joined(Session),
93 Otaa(otaa::Otaa),
94 Unjoined,
95}
96
97#[derive(Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum Error {
100 NotJoined,
101 InvalidResponse(Response),
102}
103
104pub struct SendData<'a> {
105 pub data: &'a [u8],
106 pub fport: u8,
107 pub confirmed: bool,
108}
109
110pub(crate) type Result<T = ()> = core::result::Result<T, Error>;
111
112impl Mac {
113 pub(crate) fn new(region: region::Configuration, max_power: u8, antenna_gain: i8) -> Self {
114 let data_rate = region.get_default_datarate();
115 Self {
116 board_eirp: BoardEirp { max_power, antenna_gain },
117 region,
118 state: State::Unjoined,
119 configuration: Configuration {
120 data_rate,
121 rx1_delay: region::constants::RECEIVE_DELAY1,
122 join_accept_delay1: region::constants::JOIN_ACCEPT_DELAY1,
123 join_accept_delay2: region::constants::JOIN_ACCEPT_DELAY2,
124 },
125 }
126 }
127
128 pub(crate) fn join_otaa<C: CryptoFactory + Default, RNG: RngCore, const N: usize>(
131 &mut self,
132 rng: &mut RNG,
133 credentials: NetworkCredentials,
134 buf: &mut RadioBuffer<N>,
135 ) -> (radio::TxConfig, u16) {
136 let mut otaa = otaa::Otaa::new(credentials);
137 let dev_nonce = otaa.prepare_buffer::<C, RNG, N>(rng, buf);
138 self.state = State::Otaa(otaa);
139 let mut tx_config =
140 self.region.create_tx_config(rng, self.configuration.data_rate, &Frame::Join);
141 tx_config.adjust_power(self.board_eirp.max_power, self.board_eirp.antenna_gain);
142 (tx_config, dev_nonce)
143 }
144
145 pub(crate) fn join_abp(
147 &mut self,
148 newskey: NewSKey,
149 appskey: AppSKey,
150 devaddr: DevAddr<[u8; 4]>,
151 ) {
152 self.state = State::Joined(Session::new(newskey, appskey, devaddr));
153 }
154
155 pub(crate) fn set_session(&mut self, session: Session) {
157 self.state = State::Joined(session);
158 }
159
160 pub(crate) fn send<C: CryptoFactory + Default, RNG: RngCore, const N: usize>(
163 &mut self,
164 rng: &mut RNG,
165 buf: &mut RadioBuffer<N>,
166 send_data: &SendData,
167 ) -> Result<(radio::TxConfig, FcntUp)> {
168 let fcnt = match &mut self.state {
169 State::Joined(ref mut session) => Ok(session.prepare_buffer::<C, N>(send_data, buf)),
170 State::Otaa(_) => Err(Error::NotJoined),
171 State::Unjoined => Err(Error::NotJoined),
172 }?;
173 let mut tx_config =
174 self.region.create_tx_config(rng, self.configuration.data_rate, &Frame::Data);
175 tx_config.adjust_power(self.board_eirp.max_power, self.board_eirp.antenna_gain);
176 Ok((tx_config, fcnt))
177 }
178
179 pub(crate) fn get_rx_delay(&self, frame: &Frame, window: &Window) -> u32 {
180 match frame {
181 Frame::Join => match window {
182 Window::_1 => self.configuration.join_accept_delay1,
183 Window::_2 => self.configuration.join_accept_delay2,
184 },
185 Frame::Data => match window {
186 Window::_1 => self.configuration.rx1_delay,
187 Window::_2 => self.configuration.rx1_delay + 1000,
190 },
191 }
192 }
193
194 pub(crate) fn get_rx_parameters_legacy(
196 &mut self,
197 frame: &Frame,
198 window: &Window,
199 ) -> (RfConfig, u32) {
200 (
201 self.region.get_rx_config(self.configuration.data_rate, frame, window),
202 self.get_rx_delay(frame, window),
203 )
204 }
205
206 pub(crate) fn handle_rx<C: CryptoFactory + Default, const N: usize, const D: usize>(
211 &mut self,
212 buf: &mut RadioBuffer<N>,
213 dl: &mut Vec<Downlink, D>,
214 ) -> Response {
215 match &mut self.state {
216 State::Joined(ref mut session) => session.handle_rx::<C, N, D>(
217 &mut self.region,
218 &mut self.configuration,
219 buf,
220 dl,
221 false,
222 ),
223 State::Otaa(ref mut otaa) => {
224 if let Some(session) =
225 otaa.handle_rx::<C, N>(&mut self.region, &mut self.configuration, buf)
226 {
227 self.state = State::Joined(session);
228 Response::JoinSuccess
229 } else {
230 Response::NoUpdate
231 }
232 }
233 State::Unjoined => Response::NoUpdate,
234 }
235 }
236
237 pub(crate) fn handle_rxc<C: CryptoFactory + Default, const N: usize, const D: usize>(
241 &mut self,
242 buf: &mut RadioBuffer<N>,
243 dl: &mut Vec<Downlink, D>,
244 ) -> Result<Response> {
245 match &mut self.state {
246 State::Joined(ref mut session) => Ok(session.handle_rx::<C, N, D>(
247 &mut self.region,
248 &mut self.configuration,
249 buf,
250 dl,
251 true,
252 )),
253 State::Otaa(_) => Err(Error::NotJoined),
254 State::Unjoined => Err(Error::NotJoined),
255 }
256 }
257
258 pub(crate) fn rx2_complete(&mut self) -> Response {
259 match &mut self.state {
260 State::Joined(session) => session.rx2_complete(),
261 State::Otaa(otaa) => otaa.rx2_complete(),
262 State::Unjoined => Response::NoUpdate,
263 }
264 }
265
266 pub(crate) fn get_session_keys(&self) -> Option<SessionKeys> {
267 match &self.state {
268 State::Joined(session) => session.get_session_keys(),
269 State::Otaa(_) => None,
270 State::Unjoined => None,
271 }
272 }
273
274 pub(crate) fn get_session(&self) -> Option<&Session> {
275 match &self.state {
276 State::Joined(session) => Some(session),
277 State::Otaa(_) => None,
278 State::Unjoined => None,
279 }
280 }
281
282 pub(crate) fn is_joined(&self) -> bool {
283 matches!(&self.state, State::Joined(_))
284 }
285
286 pub(crate) fn get_fcnt_up(&self) -> Option<FcntUp> {
287 match &self.state {
288 State::Joined(session) => Some(session.fcnt_up),
289 State::Otaa(_) => None,
290 State::Unjoined => None,
291 }
292 }
293
294 pub(crate) fn get_rx_config(&self, buffer_ms: u32, frame: &Frame, window: &Window) -> RxConfig {
295 RxConfig {
296 rf: self.region.get_rx_config(self.configuration.data_rate, frame, window),
297 mode: RxMode::Single { ms: buffer_ms },
298 }
299 }
300
301 pub(crate) fn get_rxc_config(&self) -> RxConfig {
302 RxConfig {
303 rf: self.region.get_rxc_config(self.configuration.data_rate),
304 mode: RxMode::Continuous,
305 }
306 }
307}
308
309#[cfg_attr(feature = "defmt", derive(defmt::Format))]
310#[derive(Debug)]
311pub enum Response {
312 NoAck,
313 SessionExpired,
314 DownlinkReceived(FcntDown),
315 NoJoinAccept,
316 JoinSuccess,
317 NoUpdate,
318 RxComplete,
319}
320
321impl From<Response> for nb_device::Response {
322 fn from(r: Response) -> Self {
323 match r {
324 Response::SessionExpired => nb_device::Response::SessionExpired,
325 Response::DownlinkReceived(fcnt) => nb_device::Response::DownlinkReceived(fcnt),
326 Response::NoAck => nb_device::Response::NoAck,
327 Response::NoJoinAccept => nb_device::Response::NoJoinAccept,
328 Response::JoinSuccess => nb_device::Response::JoinSuccess,
329 Response::NoUpdate => nb_device::Response::NoUpdate,
330 Response::RxComplete => nb_device::Response::RxComplete,
331 }
332 }
333}
334
335impl TryFrom<Response> for async_device::SendResponse {
336 type Error = Error;
337
338 fn try_from(r: Response) -> Result<async_device::SendResponse> {
339 match r {
340 Response::SessionExpired => Ok(async_device::SendResponse::SessionExpired),
341 Response::DownlinkReceived(fcnt) => {
342 Ok(async_device::SendResponse::DownlinkReceived(fcnt))
343 }
344 Response::NoAck => Ok(async_device::SendResponse::NoAck),
345 Response::RxComplete => Ok(async_device::SendResponse::RxComplete),
346 r => Err(Error::InvalidResponse(r)),
347 }
348 }
349}
350
351impl TryFrom<Response> for async_device::JoinResponse {
352 type Error = Error;
353
354 fn try_from(r: Response) -> Result<async_device::JoinResponse> {
355 match r {
356 Response::NoJoinAccept => Ok(async_device::JoinResponse::NoJoinAccept),
357 Response::JoinSuccess => Ok(async_device::JoinResponse::JoinSuccess),
358 r => Err(Error::InvalidResponse(r)),
359 }
360 }
361}
362
363fn del_to_delay_ms(del: u8) -> u32 {
364 match del {
365 2..=15 => del as u32 * 1000,
366 _ => region::constants::RECEIVE_DELAY1,
367 }
368}