use embassy_sync::{
blocking_mutex::raw::RawMutex,
channel::{Receiver, Sender},
};
use embassy_time::{with_timeout, Duration, Timer};
use futures::FutureExt;
use rand_core::RngCore;
use crate::{
frame_pool::{FrameBox, RawFrameSlice},
CmdAddr, FrameSerial,
};
pub const OUTGOING_SIZE: usize = 8;
pub const INCOMING_SIZE: usize = 4;
pub trait TgtCfg {
type Mutex: RawMutex + 'static;
type Serial: FrameSerial;
type Rand: RngCore;
const TURNAROUND_DELAY: Duration;
const ADDRESS_CLAIM_TIMEOUT: Duration;
const SELECT_TIMEOUT: Duration;
}
enum TargetError<S> {
Serial(S),
Oom,
}
impl<S> From<crate::Error<S>> for TargetError<S> {
fn from(value: crate::Error<S>) -> Self {
match value {
crate::Error::Serial(s) => Self::Serial(s),
}
}
}
pub struct Target<'a, Cfg, const IN: usize = INCOMING_SIZE, const OUT: usize = OUTGOING_SIZE>
where
Cfg: TgtCfg,
{
serial: Cfg::Serial,
to_app: Sender<'a, Cfg::Mutex, FrameBox, IN>,
from_app: Receiver<'a, Cfg::Mutex, FrameBox, OUT>,
pool: RawFrameSlice,
mac: [u8; 8],
rand: Cfg::Rand,
}
impl<'a, Cfg, const IN: usize, const OUT: usize> Target<'a, Cfg, IN, OUT>
where
Cfg: TgtCfg,
{
pub fn new(
serial: Cfg::Serial,
to_app: Sender<'a, Cfg::Mutex, FrameBox, IN>,
from_app: Receiver<'a, Cfg::Mutex, FrameBox, OUT>,
pool: RawFrameSlice,
mac: [u8; 8],
rand: Cfg::Rand,
) -> Self {
Self {
serial,
to_app,
from_app,
mac,
rand,
pool,
}
}
pub async fn run(&mut self) {
'outer: loop {
let addr = self.get_addr().await;
nut_info!("Got addr: {=u8}", addr);
loop {
match with_timeout(Cfg::SELECT_TIMEOUT, self.exchange_one(addr)).await {
Ok(Ok(())) => {}
Ok(Err(_)) => {
nut_error!("Error :(");
continue 'outer;
}
Err(_) => {
nut_warn!("Timed out!");
continue 'outer;
}
}
}
}
}
async fn exchange_one(
&mut self,
addr: u8,
) -> Result<(), TargetError<<Cfg::Serial as FrameSerial>::SerError>> {
let time = self.get_incoming(addr).await?;
let mut tx_frame = self.from_app.receive().now_or_never();
let mut fallback = [0u8; 1];
let out = match tx_frame.as_deref_mut() {
Some(g) => g,
None => fallback.as_mut_slice(),
};
out[0] = CmdAddr::ReplyFromAddr(addr).into();
Timer::at(time + Cfg::TURNAROUND_DELAY).await;
self.serial.send_frame(out).await?;
Ok(())
}
async fn get_incoming(
&mut self,
addr: u8,
) -> Result<crate::Instant, TargetError<<Cfg::Serial as FrameSerial>::SerError>> {
let mut frame = self.pool.allocate_raw().ok_or(TargetError::Oom)?;
loop {
let buf = &mut frame[..];
let got = self.serial.recv(buf).await?;
if got.frame.is_empty() {
continue;
}
let Ok(cmd_addr) = CmdAddr::try_from(got.frame[0]) else {
continue;
};
if cmd_addr != CmdAddr::SelectAddr(addr) {
continue;
}
let len = got.frame.len();
let stamp = got.end_of_rx;
if len != 1 {
frame.set_len(len);
self.to_app.send(frame).await;
}
return Ok(stamp);
}
}
async fn get_addr(&mut self) -> u8 {
loop {
nut_info!("get_addr...");
let goforit = self.rand.next_u32();
let (offer_addr, offer_challenge) = self.get_offer().await;
if goforit & 0b0000_0111 != 0 {
nut_info!("skipping!");
continue;
} else {
nut_info!("going for it!");
}
let claim_dance = async {
self.send_claim(offer_addr, &offer_challenge).await?;
self.get_success(offer_addr).await?;
let msg: [u8; 1] = [CmdAddr::ReplyFromAddr(offer_addr).into()];
self.serial.send_frame(&msg).await?;
Result::<(), TargetError<<Cfg::Serial as FrameSerial>::SerError>>::Ok(())
};
match with_timeout(Cfg::ADDRESS_CLAIM_TIMEOUT, claim_dance).await {
Ok(Ok(())) => return offer_addr,
_ => continue,
}
}
}
async fn get_success(
&mut self,
offer_addr: u8,
) -> Result<(), TargetError<<Cfg::Serial as FrameSerial>::SerError>> {
let mut scratch = [0u8; 16];
loop {
nut_info!("get success");
let Ok(tframe) = self.serial.recv(&mut scratch).await else {
continue;
};
let Some(ca) = tframe
.frame
.first()
.and_then(|b| CmdAddr::try_from(*b).ok())
else {
continue;
};
let CmdAddr::DiscoverySuccess(addr) = ca else {
continue;
};
if tframe.frame.len() < 9 {
continue;
}
if addr == offer_addr && (tframe.frame[1..9] == self.mac) {
return Ok(());
} else {
continue;
}
}
}
async fn send_claim(
&mut self,
offer_addr: u8,
challenge: &[u8; 8],
) -> Result<(), TargetError<<Cfg::Serial as FrameSerial>::SerError>> {
nut_info!("send claim");
let mut claim = [0u8; 9];
claim[0] = CmdAddr::DiscoveryClaim(offer_addr).into();
let data = &mut claim[1..9];
data.copy_from_slice(&self.mac);
data.iter_mut()
.zip(challenge.iter())
.for_each(|(a, b)| *a ^= *b);
self.serial.send_frame(&claim).await?;
Ok(())
}
async fn get_offer(&mut self) -> (u8, [u8; 8]) {
let mut scratch = [0u8; 16];
loop {
let Ok(tframe) = self.serial.recv(&mut scratch).await else {
continue;
};
let Some(ca) = tframe
.frame
.first()
.and_then(|b| CmdAddr::try_from(*b).ok())
else {
continue;
};
let CmdAddr::DiscoveryOffer(addr) = ca else {
continue;
};
if tframe.frame.len() < 9 {
continue;
}
let mut challenge = [0u8; 8];
challenge.copy_from_slice(&tframe.frame[1..9]);
return (addr, challenge);
}
}
}