Skip to main content

pamoja_lorawan/packages/
mod.rs

1//! The LoRaWAN application layer packages: what a server and a device say to each other on
2//! top of the link layer, TS003, TS004, TS005 and TS006.
3//!
4//! A package is a small command language spoken on a port of its own. Every package shares
5//! the same shape: a message carries one or more commands, each an identifier byte followed
6//! by a payload whose length the identifier fixes, and a device runs them in order and
7//! answers each one. The identifier means one command going down and another coming up, so
8//! reading a message takes the [`Direction`](crate::Direction) it traveled, exactly as a MAC
9//! command does.
10//!
11//! Each package announces itself: [`PackageVersion`] is the answer every package gives to the
12//! version request every package defines, carrying the identifier of the package and the
13//! version of it the device implements.
14//!
15//! - [`clock`]: application layer clock synchronization, TS003-2.0.0, on port
16//!   [`clock::PORT`]. A device tells the server what time it thinks it is and the server
17//!   answers with the correction.
18//! - [`firmware`]: firmware management, TS006-1.0.0, on port [`firmware::PORT`]. A server
19//!   asks what a device is running, what upgrade image it holds, and schedules the reboot
20//!   that installs it.
21//! - [`fragment`]: fragmented data block transport, TS004-2.0.0, on port [`fragment::PORT`].
22//!   A block too large for one frame goes across in pieces, with coded fragments that let a
23//!   device solve for the ones it missed.
24//! - [`multicast`]: remote multicast setup, TS005-2.0.0, on port [`multicast::PORT`]. A
25//!   group of devices is given one address and one key, and a window in which they all
26//!   listen at once.
27//!
28//! These messages are unicast: a device drops them silently when they arrive on a multicast
29//! address.
30
31use crate::LorawanError;
32
33pub mod clock;
34pub mod firmware;
35pub mod fragment;
36pub mod multicast;
37
38#[cfg(test)]
39mod fragment_tests;
40#[cfg(test)]
41mod multicast_tests;
42#[cfg(test)]
43mod tests;
44
45/// The answer every package gives to its own version request.
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub struct PackageVersion {
48    /// Which package this is: 1 for clock synchronization, 4 for firmware management.
49    pub package: u8,
50    /// The version of that package the device implements.
51    pub version: u8,
52}
53
54/// Reads a command's identifier and its payload of a fixed length.
55///
56/// # Arguments
57///
58/// * `bytes` - the message from the identifier on.
59/// * `len` - how many payload bytes the command carries.
60///
61/// # Returns
62///
63/// The payload and how many bytes the whole command took.
64///
65/// # Errors
66///
67/// [`LorawanError::MalformedFrame`] when the message ends inside the command.
68pub(crate) fn payload(bytes: &[u8], len: usize) -> Result<(&[u8], usize), LorawanError> {
69    bytes
70        .get(1..=len)
71        .map(|payload| (payload, len + 1))
72        .ok_or(LorawanError::MalformedFrame)
73}
74
75/// Writes a command's identifier and payload.
76///
77/// # Arguments
78///
79/// * `out` - where to write.
80/// * `cid` - the command identifier.
81/// * `fields` - its payload.
82///
83/// # Returns
84///
85/// How many bytes were written.
86///
87/// # Errors
88///
89/// [`LorawanError::PayloadTooLong`] when the buffer is too small.
90pub(crate) fn write(out: &mut [u8], cid: u8, fields: &[u8]) -> Result<usize, LorawanError> {
91    let len = fields.len() + 1;
92    let room = out.get_mut(..len).ok_or(LorawanError::PayloadTooLong)?;
93    room[0] = cid;
94    room[1..].copy_from_slice(fields);
95    Ok(len)
96}