Skip to main content

pamoja_lorawan/
frame.rs

1//! The LoRaWAN PHYPayload and the small shared pieces of its header.
2
3use crate::error::LorawanError;
4
5/// The largest PHYPayload, in bytes, this crate builds or accepts.
6///
7/// Comfortably above the largest regional LoRaWAN maximum, so a frame always fits.
8pub const MAX_FRAME: usize = 256;
9
10/// The largest application payload, in bytes, a single frame can carry (with no frame
11/// options present).
12pub const MAX_PAYLOAD: usize = MAX_FRAME - 13;
13
14// MType values, in the top three bits of the MHDR.
15pub(crate) const MTYPE_JOIN_REQUEST: u8 = 0x00;
16pub(crate) const MTYPE_JOIN_ACCEPT: u8 = 0x20;
17pub(crate) const MTYPE_UNCONFIRMED_UP: u8 = 0x40;
18pub(crate) const MTYPE_UNCONFIRMED_DOWN: u8 = 0x60;
19pub(crate) const MTYPE_CONFIRMED_UP: u8 = 0x80;
20pub(crate) const MTYPE_CONFIRMED_DOWN: u8 = 0xA0;
21// The mask selecting the MType bits of the MHDR.
22pub(crate) const MTYPE_MASK: u8 = 0xE0;
23
24/// The direction a frame travels, which the MIC and the payload encryption both fold in.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum Direction {
27    /// From an end device up to the network.
28    Uplink,
29    /// From the network down to an end device.
30    Downlink,
31}
32
33impl Direction {
34    // The direction bit used in the MIC and encryption blocks: 0 up, 1 down.
35    pub(crate) fn bit(self) -> u8 {
36        match self {
37            Direction::Uplink => 0,
38            Direction::Downlink => 1,
39        }
40    }
41}
42
43/// An encoded LoRaWAN frame, the bytes that go on the air.
44///
45/// Built by a [`Session`](crate::Session) or a join exchange, and held in a fixed buffer
46/// so encoding never allocates. [`as_bytes`](PhyPayload::as_bytes) hands the radio exactly
47/// what to transmit.
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct PhyPayload {
50    bytes: [u8; MAX_FRAME],
51    len: usize,
52}
53
54impl PhyPayload {
55    // Copies an assembled frame into a fixed buffer.
56    pub(crate) fn new(bytes: &[u8]) -> Result<Self, LorawanError> {
57        if bytes.len() > MAX_FRAME {
58            return Err(LorawanError::PayloadTooLong);
59        }
60        let mut buf = [0u8; MAX_FRAME];
61        buf[..bytes.len()].copy_from_slice(bytes);
62        Ok(PhyPayload {
63            bytes: buf,
64            len: bytes.len(),
65        })
66    }
67
68    /// Returns the frame as bytes, ready to transmit.
69    ///
70    /// # Returns
71    ///
72    /// The whole PHYPayload, MIC included.
73    pub fn as_bytes(&self) -> &[u8] {
74        &self.bytes[..self.len]
75    }
76}