1use super::mac::Mac;
4
5use super::mac::{self, Frame, Window};
6pub use super::{
7 mac::{NetworkCredentials, SendData, Session},
8 region::{self, Region},
9 Downlink, JoinMode,
10};
11use crate::log;
12use core::marker::PhantomData;
13use futures::{future::select, future::Either, pin_mut};
14use heapless::Vec;
15use lorawan::{self, keys::CryptoFactory};
16use rand_core::RngCore;
17
18pub use crate::region::DR;
19use crate::{radio::RadioBuffer, rng};
20
21pub mod radio;
22
23#[cfg(feature = "embassy-time")]
24mod embassy_time;
25#[cfg(feature = "embassy-time")]
26pub use embassy_time::EmbassyTimer;
27
28#[cfg(test)]
29mod test;
30
31use self::radio::{RxQuality, RxStatus};
32
33pub struct Device<R, C, T, G, const N: usize = 256, const D: usize = 1>
49where
50 R: radio::PhyRxTx + Timings,
51 T: radio::Timer,
52 C: CryptoFactory + Default,
53 G: RngCore,
54{
55 crypto: PhantomData<C>,
56 radio: R,
57 rng: G,
58 timer: T,
59 mac: Mac,
60 radio_buffer: RadioBuffer<N>,
61 downlink: Vec<Downlink, D>,
62 class_c: bool,
63}
64
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66#[derive(Debug)]
67pub enum Error<R> {
68 Radio(R),
69 Mac(mac::Error),
70}
71
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[derive(Debug)]
74pub enum SendResponse {
75 DownlinkReceived(mac::FcntDown),
76 SessionExpired,
77 NoAck,
78 RxComplete,
79}
80
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82#[derive(Debug)]
83pub enum JoinResponse {
84 JoinSuccess,
85 NoJoinAccept,
86}
87
88impl<R> From<mac::Error> for Error<R> {
89 fn from(e: mac::Error) -> Self {
90 Error::Mac(e)
91 }
92}
93
94impl<R, C, T, const N: usize> Device<R, C, T, rng::Prng, N>
95where
96 R: radio::PhyRxTx + Timings,
97 C: CryptoFactory + Default,
98 T: radio::Timer,
99{
100 pub fn new_with_seed(region: region::Configuration, radio: R, timer: T, seed: u64) -> Self {
110 Device::new_with_seed_and_session(region, radio, timer, seed, None)
111 }
112
113 pub fn new_with_seed_and_session(
123 region: region::Configuration,
124 radio: R,
125 timer: T,
126 seed: u64,
127 session: Option<Session>,
128 ) -> Self {
129 let rng = rng::Prng::new(seed);
130 Device::new_with_session(region, radio, timer, rng, session)
131 }
132}
133
134impl<R, C, T, G, const N: usize, const D: usize> Device<R, C, T, G, N, D>
135where
136 R: radio::PhyRxTx + Timings,
137 C: CryptoFactory + Default,
138 T: radio::Timer,
139 G: RngCore,
140{
141 pub fn new(region: region::Configuration, radio: R, timer: T, rng: G) -> Self {
147 Device::new_with_session(region, radio, timer, rng, None)
148 }
149
150 pub fn new_with_session(
152 region: region::Configuration,
153 radio: R,
154 timer: T,
155 rng: G,
156 session: Option<Session>,
157 ) -> Self {
158 let mut mac = Mac::new(region, R::MAX_RADIO_POWER, R::ANTENNA_GAIN);
159 if let Some(session) = session {
160 mac.set_session(session);
161 }
162 Self {
163 crypto: PhantomData,
164 radio,
165 rng,
166 mac,
167 radio_buffer: RadioBuffer::new(),
168 timer,
169 downlink: Vec::new(),
170 class_c: false,
171 }
172 }
173
174 pub fn enable_class_c(&mut self) {
178 self.class_c = true;
179 }
180
181 pub fn disable_class_c(&mut self) {
184 self.class_c = false;
185 }
186
187 pub fn get_session(&mut self) -> Option<&Session> {
188 self.mac.get_session()
189 }
190
191 pub fn get_region(&mut self) -> ®ion::Configuration {
192 &self.mac.region
193 }
194
195 pub fn get_radio(&mut self) -> &R {
196 &self.radio
197 }
198
199 pub fn get_mut_radio(&mut self) -> &mut R {
200 &mut self.radio
201 }
202
203 pub fn get_datarate(&mut self) -> DR {
205 self.mac.configuration.data_rate
206 }
207
208 pub fn set_datarate(&mut self, datarate: DR) {
210 self.mac.configuration.data_rate = datarate;
211 }
212
213 pub async fn join(&mut self, join_mode: &JoinMode) -> Result<JoinResponse, Error<R::PhyError>> {
221 match join_mode {
222 JoinMode::OTAA { deveui, appeui, appkey } => {
223 let (tx_config, _) = self.mac.join_otaa::<C, G, N>(
224 &mut self.rng,
225 NetworkCredentials::new(*appeui, *deveui, *appkey),
226 &mut self.radio_buffer,
227 );
228
229 let ms = self
231 .radio
232 .tx(tx_config, self.radio_buffer.as_ref_for_read())
233 .await
234 .map_err(Error::Radio)?;
235
236 self.timer.reset();
238 Ok(self.rx_downlink(&Frame::Join, ms).await?.try_into()?)
239 }
240 JoinMode::ABP { newskey, appskey, devaddr } => {
241 self.mac.join_abp(*newskey, *appskey, *devaddr);
242 Ok(JoinResponse::JoinSuccess)
243 }
244 }
245 }
246
247 pub async fn send(
258 &mut self,
259 data: &[u8],
260 fport: u8,
261 confirmed: bool,
262 ) -> Result<SendResponse, Error<R::PhyError>> {
263 let (tx_config, _fcnt_up) = self.mac.send::<C, G, N>(
265 &mut self.rng,
266 &mut self.radio_buffer,
267 &SendData { data, fport, confirmed },
268 )?;
269 let ms = self
271 .radio
272 .tx(tx_config, self.radio_buffer.as_ref_for_read())
273 .await
274 .map_err(Error::Radio)?;
275
276 self.timer.reset();
278 Ok(self.rx_downlink(&Frame::Data, ms).await?.try_into()?)
279 }
280
281 pub fn take_downlink(&mut self) -> Option<Downlink> {
285 self.downlink.pop()
286 }
287
288 async fn window_complete(&mut self) -> Result<(), Error<R::PhyError>> {
289 if self.class_c {
290 let rf_config = self.mac.get_rxc_config();
291 self.radio.setup_rx(rf_config).await.map_err(Error::Radio)
292 } else {
293 self.radio.low_power().await.map_err(Error::Radio)
294 }
295 }
296
297 async fn between_windows(
298 &mut self,
299 duration: u32,
300 ) -> Result<Option<mac::Response>, Error<R::PhyError>> {
301 if !self.class_c {
302 self.radio.low_power().await.map_err(Error::Radio)?;
303 self.timer.at(duration.into()).await;
304 return Ok(None);
305 }
306
307 #[allow(unused)]
308 enum RxcWindowResponse<F: futures::Future<Output = ()> + Sized + Unpin> {
309 Rx(usize, RxQuality, F),
310 Timeout(u32),
311 }
312
313 async fn rxc_listen_until_timeout<F, R, const N: usize>(
315 radio: &mut R,
316 rx_buf: &mut RadioBuffer<N>,
317 window_duration: u32,
318 timeout_fut: F,
319 ) -> RxcWindowResponse<F>
320 where
321 F: futures::Future<Output = ()> + Sized + Unpin,
322 R: radio::PhyRxTx + Timings,
323 {
324 let rx_fut = radio.rx_continuous(rx_buf.as_mut());
325 pin_mut!(rx_fut);
326 match select(rx_fut, timeout_fut).await {
328 Either::Left((r, timeout_fut)) => match r {
329 Ok((sz, q)) => RxcWindowResponse::Rx(sz, q, timeout_fut),
330 _ => {
333 timeout_fut.await;
334 RxcWindowResponse::Timeout(0)
335 }
336 },
337 Either::Right(_) => RxcWindowResponse::Timeout(window_duration),
339 }
340 }
341
342 let rx_config = self.mac.get_rxc_config();
344 log::debug!("Configuring RXC window with config {}.", rx_config);
345 self.radio.setup_rx(rx_config).await.map_err(Error::Radio)?;
346 let mut response = None;
347 let timeout_fut = self.timer.at(duration.into());
348 pin_mut!(timeout_fut);
349 let mut maybe_timeout_fut = Some(timeout_fut);
350
351 while let Some(timeout_fut) = maybe_timeout_fut.take() {
353 match rxc_listen_until_timeout(
354 &mut self.radio,
355 &mut self.radio_buffer,
356 duration,
357 timeout_fut,
358 )
359 .await
360 {
361 RxcWindowResponse::Rx(sz, _, timeout_fut) => {
362 log::debug!("RXC window received {} bytes.", sz);
363 self.radio_buffer.set_pos(sz);
364 match self
365 .mac
366 .handle_rxc::<C, N, D>(&mut self.radio_buffer, &mut self.downlink)?
367 {
368 mac::Response::NoUpdate => {
369 log::debug!("RXC frame was invalid.");
370 self.radio_buffer.clear();
371 maybe_timeout_fut = Some(timeout_fut);
373 }
374 r => {
375 log::debug!("Valid RXC frame received.");
376 self.radio_buffer.clear();
377 response = Some(r);
378 maybe_timeout_fut = Some(timeout_fut);
380 }
381 }
382 }
383 RxcWindowResponse::Timeout(_) => return Ok(response),
384 };
385 }
386 Ok(response)
387 }
388
389 async fn rx_downlink(
392 &mut self,
393 frame: &Frame,
394 window_delay: u32,
395 ) -> Result<mac::Response, Error<R::PhyError>> {
396 self.radio_buffer.clear();
397
398 let rx1_start_delay = self.mac.get_rx_delay(frame, &Window::_1) + window_delay
399 - self.radio.get_rx_window_lead_time_ms();
400
401 log::debug!("Starting RX1 in {} ms.", rx1_start_delay);
402 let _ = self.between_windows(rx1_start_delay).await?;
404
405 let rx_config =
407 self.mac.get_rx_config(self.radio.get_rx_window_buffer(), frame, &Window::_1);
408 log::debug!("Configuring RX1 window with config {}.", rx_config);
409 self.radio.setup_rx(rx_config).await.map_err(Error::Radio)?;
410
411 if let Some(response) = self.rx_listen().await? {
412 log::debug!("RX1 received {}", response);
413 return Ok(response);
414 }
415
416 let rx2_start_delay = self.mac.get_rx_delay(frame, &Window::_2) + window_delay
417 - self.radio.get_rx_window_lead_time_ms();
418 log::debug!("RX1 did not receive anything. Awaiting RX2 for {} ms.", rx2_start_delay);
419 let _ = self.between_windows(rx2_start_delay).await?;
421
422 let rx_config =
424 self.mac.get_rx_config(self.radio.get_rx_window_buffer(), frame, &Window::_2);
425 log::debug!("Configuring RX2 window with config {}.", rx_config);
426 self.radio.setup_rx(rx_config).await.map_err(Error::Radio)?;
427
428 if let Some(response) = self.rx_listen().await? {
429 log::debug!("RX2 received {}", response);
430 return Ok(response);
431 }
432 log::debug!("RX2 did not receive anything.");
433 Ok(self.mac.rx2_complete())
434 }
435
436 async fn rx_listen(&mut self) -> Result<Option<mac::Response>, Error<R::PhyError>> {
437 let response =
438 match self.radio.rx_single(self.radio_buffer.as_mut()).await.map_err(Error::Radio)? {
439 RxStatus::Rx(s, _q) => {
440 self.radio_buffer.set_pos(s);
441 match self.mac.handle_rx::<C, N, D>(&mut self.radio_buffer, &mut self.downlink)
442 {
443 mac::Response::NoUpdate => None,
444 r => Some(r),
445 }
446 }
447 RxStatus::RxTimeout => None,
448 };
449 self.radio_buffer.clear();
450 self.window_complete().await?;
451 Ok(response)
452 }
453
454 pub async fn rxc_listen(&mut self) -> Result<mac::Response, Error<R::PhyError>> {
457 loop {
458 let (sz, _rx_quality) =
459 self.radio.rx_continuous(self.radio_buffer.as_mut()).await.map_err(Error::Radio)?;
460 self.radio_buffer.set_pos(sz);
461 match self.mac.handle_rxc::<C, N, D>(&mut self.radio_buffer, &mut self.downlink)? {
462 mac::Response::NoUpdate => {
463 self.radio_buffer.clear();
464 }
465 r => {
466 self.radio_buffer.clear();
467 return Ok(r);
468 }
469 }
470 }
471 }
472}
473
474pub trait Timings {
476 fn get_rx_window_lead_time_ms(&self) -> u32;
480
481 fn get_rx_window_buffer(&self) -> u32 {
485 self.get_rx_window_lead_time_ms()
486 }
487}