pub mod ad_structure;
pub mod advertising;
mod channel_map;
mod comp_id;
mod connection;
pub mod data;
mod device_address;
mod features;
pub mod filter;
pub mod llcp;
pub mod queue;
mod responder;
mod seq_num;
pub use self::comp_id::*;
pub use self::connection::Connection;
pub use self::device_address::*;
pub use self::features::*;
pub use self::responder::*;
use self::advertising::{Pdu, PduBuf};
use self::{ad_structure::AdStructure, seq_num::SeqNum};
use crate::phy::{AdvertisingChannel, DataChannel};
use crate::time::{Duration, Instant, Timer};
use crate::{bytes::ByteReader, config::*, utils::HexSlice, Error};
pub const CRC_POLY: u32 = 0b00000001_00000000_00000110_01011011;
pub const MIN_DATA_PAYLOAD_BUF: usize = 27;
pub const MIN_DATA_PDU_BUF: usize = MIN_DATA_PAYLOAD_BUF + 2;
pub const MIN_PAYLOAD_BUF: usize = 37;
pub const MIN_PDU_BUF: usize = MIN_PAYLOAD_BUF + 2 ;
pub const MIN_PACKET_BUF: usize =
1 +
4 +
MIN_PDU_BUF +
3 ;
enum State<C: Config> {
Standby,
Advertising {
next_adv: Instant,
interval: Duration,
pdu: advertising::PduBuf,
channel: AdvertisingChannel,
data_queues: Option<(ConfConsumer<C>, ConfProducer<C>)>,
},
Connection(Connection<C>),
}
pub struct LinkLayer<C: Config> {
dev_addr: DeviceAddress,
state: State<C>,
timer: C::Timer,
}
impl<C: Config> LinkLayer<C> {
pub fn new(dev_addr: DeviceAddress, timer: C::Timer) -> Self {
trace!("new LinkLayer, dev={:?}", dev_addr);
Self {
dev_addr,
state: State::Standby,
timer,
}
}
pub fn timer(&mut self) -> &mut C::Timer {
&mut self.timer
}
pub fn start_advertise(
&mut self,
interval: Duration,
data: &[AdStructure<'_>],
transmitter: &mut C::Transmitter,
tx: ConfConsumer<C>,
rx: ConfProducer<C>,
) -> Result<NextUpdate, Error> {
let pdu = PduBuf::discoverable(self.dev_addr, data)?;
debug!("start_advertise: adv_data = {:?}", data);
debug!("start_advertise: PDU = {:?}", pdu);
self.state = State::Advertising {
next_adv: self.timer().now(),
interval,
pdu,
channel: AdvertisingChannel::first(),
data_queues: Some((tx, rx)),
};
Ok(self.update_timer(transmitter).next_update)
}
pub fn process_adv_packet(
&mut self,
rx_end: Instant,
tx: &mut C::Transmitter,
header: advertising::Header,
payload: &[u8],
crc_ok: bool,
) -> Cmd {
let pdu = advertising::Pdu::from_header_and_payload(header, &mut ByteReader::new(payload));
if let Ok(pdu) = pdu {
if let State::Advertising {
channel,
data_queues,
..
} = &mut self.state
{
if crc_ok && pdu.receiver() == Some(&self.dev_addr) {
match pdu {
Pdu::ScanRequest { .. } => {
let scan_data = &[]; let response = PduBuf::scan_response(self.dev_addr, scan_data).unwrap();
tx.transmit_advertising(response.header(), *channel);
debug!("-> SCAN RESP: {:?}", response);
}
Pdu::ConnectRequest { lldata, .. } => {
trace!("ADV<- CONN! {:?}", pdu);
let (tx, rx) = data_queues.take().unwrap();
let (conn, cmd) = Connection::create(&lldata, rx_end, tx, rx);
self.state = State::Connection(conn);
return cmd;
}
_ => {}
}
}
}
}
trace!(
"ADV<- {}{:?}, {:?}\n{:?}\n",
if crc_ok { "" } else { "BADCRC " },
header,
HexSlice(payload),
pdu,
);
match self.state {
State::Standby => unreachable!("standby, can't receive packets"),
State::Connection { .. } => unreachable!("process_adv_packet called while connected"),
State::Advertising { channel, .. } => {
Cmd {
radio: RadioCmd::ListenAdvertising { channel },
next_update: NextUpdate::Keep,
queued_work: false,
}
}
}
}
pub fn process_data_packet(
&mut self,
rx_end: Instant,
tx: &mut C::Transmitter,
header: data::Header,
payload: &[u8],
crc_ok: bool,
) -> Cmd {
if let State::Connection(conn) = &mut self.state {
match conn.process_data_packet(rx_end, tx, header, payload, crc_ok) {
Ok(cmd) => cmd,
Err(()) => {
debug!("connection ended, standby");
self.state = State::Standby;
Cmd {
next_update: NextUpdate::Disable,
radio: RadioCmd::Off,
queued_work: false,
}
}
}
} else {
unreachable!("received data channel PDU while not in connected state");
}
}
pub fn update_timer(&mut self, tx: &mut C::Transmitter) -> Cmd {
match &mut self.state {
State::Advertising {
next_adv,
interval,
pdu,
channel,
..
} => {
*channel = channel.cycle();
let payload = pdu.payload();
let buf = tx.tx_payload_buf();
buf[..payload.len()].copy_from_slice(payload);
tx.transmit_advertising(pdu.header(), *channel);
*next_adv += *interval;
Cmd {
radio: RadioCmd::ListenAdvertising { channel: *channel },
next_update: NextUpdate::At(*next_adv),
queued_work: false,
}
}
State::Connection(conn) => match conn.timer_update(&mut self.timer) {
Ok(cmd) => cmd,
Err(()) => {
debug!("connection ended (timer), standby");
self.state = State::Standby;
Cmd {
next_update: NextUpdate::Disable,
radio: RadioCmd::Off,
queued_work: false,
}
}
},
State::Standby => unreachable!("LL in standby received timer event"),
}
}
pub fn connection(&self) -> Option<&Connection<C>> {
if let State::Connection(conn) = &self.state {
Some(conn)
} else {
None
}
}
pub fn is_advertising(&self) -> bool {
matches!(self.state, State::Advertising { .. })
}
pub fn is_connected(&self) -> bool {
matches!(self.state, State::Connection { .. })
}
}
#[must_use]
#[derive(Debug, Clone)]
pub struct Cmd {
pub radio: RadioCmd,
pub next_update: NextUpdate,
pub queued_work: bool,
}
#[derive(Debug, Clone)]
pub enum NextUpdate {
Disable,
Keep,
At(Instant),
}
#[derive(Debug, Clone)]
pub enum RadioCmd {
Off,
ListenAdvertising {
channel: AdvertisingChannel,
},
ListenData {
channel: DataChannel,
access_address: u32,
crc_init: u32,
timeout: bool,
},
}
pub trait Transmitter {
fn tx_payload_buf(&mut self) -> &mut [u8];
fn transmit_advertising(&mut self, header: advertising::Header, channel: AdvertisingChannel);
fn transmit_data(
&mut self,
access_address: u32,
crc_iv: u32,
header: data::Header,
channel: DataChannel,
);
}