use panweave_types::{PanId, ShortAddress};
use crate::gatt::COMMISSIONING_SERVICE_16;
pub const AD_TYPE_SERVICE_DATA_16: u8 = 0x16;
pub const AD_LENGTH: u8 = 0x08;
pub const ENCODED_LEN: usize = 9;
pub const VERSION: u8 = 0x1;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Advertisement {
pub tunnel_service: bool,
pub permit_join: bool,
pub pan_id: PanId,
pub nwk_address: ShortAddress,
}
impl Advertisement {
pub const NOT_JOINED: Advertisement = Advertisement {
tunnel_service: false,
permit_join: false,
pan_id: PanId::BROADCAST,
nwk_address: ShortAddress::BROADCAST_ALL,
};
pub const fn joined(&self) -> bool {
self.pan_id.0 != 0xFFFF && self.nwk_address.0 != 0xFFFF
}
pub fn encode(&self) -> [u8; ENCODED_LEN] {
let flags = (u8::from(self.tunnel_service) << 4)
| (u8::from(self.permit_join && self.joined()) << 5);
let pan = if self.joined() { self.pan_id.0 } else { 0xFFFF };
let nwk = if self.joined() {
self.nwk_address.0
} else {
0xFFFF
};
[
AD_LENGTH,
AD_TYPE_SERVICE_DATA_16,
(COMMISSIONING_SERVICE_16 & 0xff) as u8,
(COMMISSIONING_SERVICE_16 >> 8) as u8,
VERSION | flags,
(pan & 0xff) as u8,
(pan >> 8) as u8,
(nwk & 0xff) as u8,
(nwk >> 8) as u8,
]
}
pub fn find(ad: &[u8]) -> Option<Self> {
let mut rest = ad;
while let Some((&len, tail)) = rest.split_first() {
let len = usize::from(len);
if len == 0 {
break;
}
let (structure, next) = tail.split_at_checked(len)?;
rest = next;
let (&ty, data) = structure.split_first()?;
if ty != AD_TYPE_SERVICE_DATA_16 || data.len() < 7 {
continue;
}
let uuid = u16::from_le_bytes([data[0], data[1]]);
if uuid != COMMISSIONING_SERVICE_16 || data[2] & 0x0F != VERSION {
continue;
}
let flags = data[2];
return Some(Advertisement {
tunnel_service: flags & 0x10 != 0,
permit_join: flags & 0x20 != 0,
pan_id: PanId(u16::from_le_bytes([data[3], data[4]])),
nwk_address: ShortAddress(u16::from_le_bytes([data[5], data[6]])),
});
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_and_not_joined_defaults() {
let a = Advertisement {
tunnel_service: true,
permit_join: true,
pan_id: PanId(0x1A62),
nwk_address: ShortAddress(0x0001),
};
let bytes = a.encode();
assert_eq!(
bytes,
[0x08, 0x16, 0xF7, 0xFF, 0x31, 0x62, 0x1A, 0x01, 0x00]
);
let mut ad = [0u8; 12];
ad[..3].copy_from_slice(&[0x02, 0x01, 0x06]);
ad[3..].copy_from_slice(&bytes);
assert_eq!(Advertisement::find(&ad), Some(a));
let f = Advertisement::NOT_JOINED.encode();
assert_eq!(&f[4..], &[0x01, 0xFF, 0xFF, 0xFF, 0xFF]);
let mut n = Advertisement::NOT_JOINED;
n.permit_join = true;
assert_eq!(n.encode()[4], 0x01);
assert_eq!(Advertisement::find(&[0x03, 0x16, 0xF7, 0xFF]), None);
}
}