use core::array;
use bitfield_struct::bitfield;
use macro_bits::{bit, check_bit, set_bit};
use scroll::{
ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
Pread, Pwrite,
};
#[bitfield(u32, defmt = cfg(feature = "defmt"))]
#[derive(PartialEq, Eq, Hash)]
pub struct SupportedMCSSetFlags {
#[bits(10)]
pub rx_highest_supported_data_rate: u16,
#[bits(6)]
pub __: u8,
pub tx_mcs_set_defined: bool,
pub tx_rx_mcs_set_not_equal: bool,
#[bits(2)]
pub tx_maximum_number_spatial_streams_supported: u8,
pub tx_unequal_modulation_supported: bool,
#[bits(11)]
pub __: u16,
}
impl SupportedMCSSetFlags {
pub const fn is_tx_mcs_undefined(&self) -> bool {
!(self.tx_mcs_set_defined()
&& self.tx_rx_mcs_set_not_equal()
&& self.tx_maximum_number_spatial_streams_supported() == 0
&& self.tx_unequal_modulation_supported())
}
pub const fn is_tx_rx_mcs_defined_equal(&self) -> bool {
self.is_tx_mcs_undefined()
&& !self.tx_rx_mcs_set_not_equal()
&& self.tx_maximum_number_spatial_streams_supported() == 0
&& !self.tx_unequal_modulation_supported()
}
pub const fn may_tx_mcs_set_differ_from_rx(&self) -> bool {
self.is_tx_mcs_undefined() && self.tx_rx_mcs_set_not_equal()
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct SupportedMCSSet {
pub supported_rx_mcs_set: [u8; 10],
pub supported_rx_mcs_set_flags: SupportedMCSSetFlags,
}
impl SupportedMCSSet {
pub fn supported_rx_mcs_indices(&self) -> impl Iterator<Item = bool> + '_ {
self.supported_rx_mcs_set
.into_iter()
.flat_map(|byte| array::from_fn::<bool, 8, _>(|i| check_bit!(byte, bit!(i))))
}
}
impl MeasureWith<()> for SupportedMCSSet {
fn measure_with(&self, _ctx: &()) -> usize {
16
}
}
impl TryFromCtx<'_> for SupportedMCSSet {
type Error = scroll::Error;
fn try_from_ctx(from: &[u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;
let supported_rx_mcs_set = from.gread(&mut offset)?;
let mut supported_rx_mcs_set_flags = [0u8; 4];
supported_rx_mcs_set_flags
.as_mut_slice()
.copy_from_slice(from.gread_with(&mut offset, 4)?);
offset += 2;
let supported_rx_mcs_set_flags =
SupportedMCSSetFlags::from_bits(u32::from_le_bytes(supported_rx_mcs_set_flags));
Ok((
Self {
supported_rx_mcs_set,
supported_rx_mcs_set_flags,
},
offset,
))
}
}
impl TryIntoCtx for SupportedMCSSet {
type Error = scroll::Error;
fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
let mut offset = 0;
buf.gwrite(self.supported_rx_mcs_set, &mut offset)?;
buf.gwrite(
&(self.supported_rx_mcs_set_flags.into_bits() as u64).to_le_bytes()[..6],
&mut offset,
)?;
Ok(offset)
}
}
pub fn generate_supported_rx_mcs_set<I: IntoIterator<Item = bool>>(mcs_indices: I) -> [u8; 10] {
let mut supported_rx_mcs_set = [0; 10];
for (mcs_index, supported) in mcs_indices.into_iter().take(76).enumerate() {
set_bit!(
supported_rx_mcs_set[mcs_index >> 3],
bit!(mcs_index & 0b0000_0111),
supported
);
}
supported_rx_mcs_set
}
#[macro_export]
macro_rules! supported_rx_mcs_set {
($(
$supported_rx_mcs_index:expr
),*) => {
{
const RESULT: [u8; 10] = {
let mut buf = [0x00; 10];
$(
{
use ::ieee80211::macro_bits::{bit, check_bit, set_bit};
::core::assert!($supported_rx_mcs_index >= 0, "MCS indices lower zero are invalid.");
::core::assert!($supported_rx_mcs_index < 77, "MCS indices greater than 76 are invalid.");
let (byte, bit) = ($supported_rx_mcs_index >> 3, $supported_rx_mcs_index & 0b0000_0111);
::core::assert!(!check_bit!(buf[byte], bit!(bit)), concat!("MCS index: ", concat!($supported_rx_mcs_index, " is a duplicate.")));
set_bit!(buf[byte], bit!(bit));
}
)*
buf
};
RESULT
}
};
($start:expr=>$end:expr) => {
{
const RESULT: [u8; 10] = {
use ::ieee80211::macro_bits::{bit, check_bit, set_bit};
::core::assert!($start >= 0, "MCS indices lower zero are invalid.");
::core::assert!($end < 77, "MCS indices greater than 76 are invalid.");
let mut buf = [0x00; 10];
let mut i = $start;
while i < $end {
set_bit!(buf[i >> 3], bit!(i & 0b0000_0111));
i += 1;
}
buf
};
RESULT
}
};
}