use crate::Error;
use core::ops::{Deref, DerefMut};
use bbq2::{
queue::BBQueue,
traits::{coordination::cas::AtomicCoord, notifier::maitake::MaiNotSpsc, storage::Inline},
};
type FramedGrantR<const N: usize> = bbq2::prod_cons::framed::FramedGrantR<
&'static BBQueue<Inline<N>, AtomicCoord, MaiNotSpsc>,
Inline<N>,
AtomicCoord,
MaiNotSpsc,
u16,
>;
type FramedGrantW<const N: usize> = bbq2::prod_cons::framed::FramedGrantW<
&'static BBQueue<Inline<N>, AtomicCoord, MaiNotSpsc>,
Inline<N>,
AtomicCoord,
MaiNotSpsc,
u16,
>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct EsbHeaderBuilder(EsbHeader);
impl Default for EsbHeaderBuilder {
fn default() -> Self {
EsbHeaderBuilder(EsbHeader {
rssi: 0,
pid_no_ack: 0,
length: 0,
pipe: 0,
})
}
}
impl EsbHeaderBuilder {
pub fn pipe(mut self, pipe: u8) -> Self {
self.0.pipe = pipe;
self
}
pub fn max_payload(mut self, max_payload: u8) -> Self {
self.0.length = max_payload;
self
}
pub fn no_ack(mut self, no_ack: bool) -> Self {
if no_ack {
self.0.pid_no_ack &= 0b1111_1110;
} else {
self.0.pid_no_ack |= 0b0000_0001;
}
self
}
pub fn pid(mut self, pid: u8) -> Self {
self.0.pid_no_ack &= 0b0000_0001;
self.0.pid_no_ack |= pid << 1;
self
}
pub fn check(self) -> Result<EsbHeader, Error> {
let bad_length = self.0.length > 252;
let bad_pipe = self.0.pipe > 7;
let bad_pid = self.0.pid_no_ack > 0b0000_0111;
if bad_length || bad_pid || bad_pipe {
return Err(Error::InvalidParameters);
}
Ok(self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct EsbHeader {
rssi: u8,
pipe: u8,
pub(crate) length: u8,
pid_no_ack: u8,
}
#[repr(transparent)]
pub(crate) struct HeaderBytes(pub(crate) [u8; 4]);
impl EsbHeader {
pub fn build() -> EsbHeaderBuilder {
EsbHeaderBuilder::default()
}
pub fn new(max_payload_length: u8, pid: u8, pipe: u8, no_ack: bool) -> Result<Self, Error> {
EsbHeaderBuilder::default()
.max_payload(max_payload_length)
.pid(pid)
.pipe(pipe)
.no_ack(no_ack)
.check()
}
fn into_bytes(self) -> HeaderBytes {
HeaderBytes([
self.rssi,
self.pipe,
self.length,
self.pid_no_ack,
])
}
pub(crate) fn from_bytes(bytes: HeaderBytes) -> Self {
Self {
rssi: bytes.0[Self::rssi_idx()],
pipe: bytes.0[Self::pipe_idx()],
length: bytes.0[Self::length_idx()],
pid_no_ack: bytes.0[Self::pid_no_ack_idx()],
}
}
pub fn pid(self) -> u8 {
self.pid_no_ack >> 1
}
pub fn no_ack(self) -> bool {
self.pid_no_ack & 1 != 1
}
pub fn payload_len(self) -> u16 {
self.length.into()
}
pub fn rssi(self) -> u8 {
self.rssi
}
const fn rssi_idx() -> usize {
0
}
const fn pipe_idx() -> usize {
1
}
const fn length_idx() -> usize {
2
}
const fn pid_no_ack_idx() -> usize {
3
}
pub(crate) const fn header_size() -> u16 {
core::mem::size_of::<HeaderBytes>() as u16
}
const fn dma_payload_offset() -> usize {
2
}
}
pub struct PayloadR<const N: usize> {
grant: FramedGrantR<N>,
}
impl<const N: usize> PayloadR<N>
{
pub(crate) fn new(raw_grant: FramedGrantR<N>) -> Self {
Self { grant: raw_grant }
}
pub fn get_header(&self) -> EsbHeader {
const LEN: usize = EsbHeader::header_size() as usize;
let mut bytes = [0u8; LEN];
bytes.copy_from_slice(&self.grant[..LEN]);
EsbHeader::from_bytes(HeaderBytes(bytes))
}
pub(crate) fn dma_pointer(&self) -> *const u8 {
self.grant[EsbHeader::dma_payload_offset()..].as_ptr()
}
pub fn ccm_slice(&self) -> &[u8] {
&self.grant[EsbHeader::pipe_idx()..]
}
pub fn pipe(&self) -> u8 {
self.grant[EsbHeader::pipe_idx()]
}
pub fn pid(&self) -> u8 {
self.grant[EsbHeader::pid_no_ack_idx()] >> 1
}
pub fn no_ack(&self) -> bool {
self.grant[EsbHeader::pid_no_ack_idx()] & 1 != 1
}
pub fn payload_len(&self) -> usize {
self.grant[EsbHeader::length_idx()] as usize
}
pub fn release(self) {
self.grant.release()
}
}
impl<const N: usize> Deref for PayloadR<N>
{
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.grant[EsbHeader::header_size().into()..]
}
}
impl<const N: usize> DerefMut for PayloadR<N>
{
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.grant[EsbHeader::header_size().into()..]
}
}
pub struct PayloadW<const N: usize> {
grant: FramedGrantW<N>,
}
impl<const N: usize> PayloadW<N>
{
pub fn update_header(&mut self, mut header: EsbHeader) {
let payload_max = self.grant.len().saturating_sub(EsbHeader::header_size().into());
header.length = header.length.min(payload_max as u8);
self.grant[..EsbHeader::header_size().into()].copy_from_slice(&header.into_bytes().0);
}
pub unsafe fn ccm_slice(&mut self) -> &mut [u8] {
&mut self.grant[EsbHeader::pipe_idx()..]
}
pub(crate) fn new_from_app(mut raw_grant: FramedGrantW<N>, header: EsbHeader) -> Self {
raw_grant[..EsbHeader::header_size().into()].copy_from_slice(&header.into_bytes().0);
Self { grant: raw_grant }
}
pub(crate) fn new_from_radio(raw_grant: FramedGrantW<N>) -> Self {
Self { grant: raw_grant }
}
pub(crate) fn dma_pointer(&mut self) -> *mut u8 {
self.grant[EsbHeader::dma_payload_offset()..].as_mut_ptr()
}
#[inline]
pub(crate) fn set_pipe(&mut self, pipe: u8) {
self.grant[EsbHeader::pipe_idx()] = pipe;
}
#[inline]
pub(crate) fn set_rssi(&mut self, rssi: u8) {
self.grant[EsbHeader::rssi_idx()] = rssi;
}
pub fn pipe(&self) -> u8 {
self.grant[EsbHeader::pipe_idx()]
}
pub fn pid(&self) -> u8 {
self.grant[EsbHeader::pid_no_ack_idx()] >> 1
}
pub fn no_ack(&self) -> bool {
self.grant[EsbHeader::pid_no_ack_idx()] & 1 != 1
}
pub fn payload_len(&self) -> u16 {
self.grant[EsbHeader::length_idx()] as u16
}
pub fn commit_all(self) {
let payload_len = self.payload_len();
self.grant.commit(payload_len + EsbHeader::header_size())
}
pub fn commit(mut self, used: usize) {
let payload_len: u16 = self.payload_len().min(used as u16);
self.grant[EsbHeader::length_idx()] = payload_len as u8;
self.grant.commit(payload_len + EsbHeader::header_size())
}
}
impl<const N: usize> Deref for PayloadW<N>
{
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.grant[EsbHeader::header_size().into()..]
}
}
impl<const N: usize> DerefMut for PayloadW<N>
{
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.grant[EsbHeader::header_size().into()..]
}
}