Skip to main content

ieee80211/elements/
vht.rs

1//! This module contains support for the VHT Capabilities and Operation elements.
2
3use core::fmt::Debug;
4
5use bitfield_struct::bitfield;
6use macro_bits::serializable_enum;
7use scroll::{
8    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
9    Endian, Pread, Pwrite,
10};
11
12use super::{Element, ElementID};
13
14#[bitfield(u32, defmt = cfg(feature = "defmt"))]
15#[derive(PartialEq, Eq, Hash)]
16/// General information about the capabilites of the STA's VHT PHY.
17pub struct VHTCapabilitiesInfo {
18    #[bits(2)]
19    pub maximum_mpdu_length: u8,
20    #[bits(2)]
21    pub supported_channel_width_set: u8,
22    pub rx_ldpc: bool,
23    pub short_gi_80mhz: bool,
24    pub short_gi_160mhz: bool,
25    pub tx_stbc: bool,
26    #[bits(3)]
27    pub rx_stbc: u8,
28    pub su_beamformer_capable: bool,
29    pub su_beamformee_capable: bool,
30    #[bits(3)]
31    pub beamformee_sts_capability: u8,
32    #[bits(3)]
33    pub number_of_sounding_dimensions: u8,
34    pub mu_beamformer_capable: bool,
35    pub mu_beamformee_capable: bool,
36    pub txop_ps: bool,
37    pub htc_vht_capable: bool,
38    #[bits(3)]
39    pub maximum_ampdu_length_exponent: u8,
40    #[bits(2)]
41    pub vht_link_adaptation_capable: u8,
42    pub rx_antenna_pattern_consistency: bool,
43    pub tx_antenna_pattern_consistency: bool,
44    #[bits(2)]
45    pub extended_nss_bw_support: u8,
46}
47impl VHTCapabilitiesInfo {
48    pub const fn maximum_mpdu_length_in_bytes(&self) -> Option<u16> {
49        match self.maximum_mpdu_length() {
50            0 => Some(3_895),
51            1 => Some(7_991),
52            2 => Some(11_454),
53            _ => None,
54        }
55    }
56    pub const fn with_maximum_mpdu_length_in_bytes(
57        self,
58        maximum_mpdu_length_in_bytes: u16,
59    ) -> Self {
60        self.with_maximum_mpdu_length(match maximum_mpdu_length_in_bytes {
61            3_895 => 0,
62            7_991 => 1,
63            11_454 => 2,
64            _ => 3,
65        })
66    }
67    pub fn set_maximum_mpdu_length_in_bytes(&mut self, maximum_mpdu_length_in_bytes: u16) {
68        *self = self.with_maximum_mpdu_length_in_bytes(maximum_mpdu_length_in_bytes);
69    }
70}
71serializable_enum! {
72    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
73    /// The supported VHT MCS indices.
74    pub enum VHTMCSSupport : u8 {
75        ZeroToSeven => 0,
76        ZeroToEight => 1,
77        ZeroToNine => 2,
78        #[default]
79        NotSupported => 3
80    }
81}
82#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
83/// The combinations of VHT-MCSs and spatial streams supported by the STA's VHT PHY.
84pub struct VHTMCSMap(u16);
85impl VHTMCSMap {
86    pub const fn from_bits(bits: u16) -> Self {
87        Self(bits)
88    }
89    pub const fn into_bits(self) -> u16 {
90        self.0
91    }
92    /// Returns the supported VHT MCS range for the given number of spatial streams.
93    pub fn vht_mcs_support_for_nss(&self, nss: usize) -> Option<VHTMCSSupport> {
94        if (1..9).contains(&nss) {
95            Some(VHTMCSSupport::from_bits(
96                ((self.0 >> ((nss - 1) * 2)) & 0b0000_0011) as u8,
97            ))
98        } else {
99            None
100        }
101    }
102    /// Returns an [Iterator] over the VHT MCS ranges.
103    pub fn vht_mcs_support_iter(&self) -> impl Iterator<Item = VHTMCSSupport> + '_ {
104        (1..9).filter_map(|nss| self.vht_mcs_support_for_nss(nss))
105    }
106    /// Creates a VHTMCSAndNSSSet field from
107    pub fn from_vht_mcs_iter(iter: impl IntoIterator<Item = VHTMCSSupport>) -> Self {
108        Self(
109            iter.into_iter()
110                .take(8)
111                .enumerate()
112                .fold(0u16, |acc, (i, vht_mcs_support)| {
113                    acc | (vht_mcs_support.into_bits() << (i * 2)) as u16
114                }),
115        )
116    }
117}
118impl Debug for VHTMCSMap {
119    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120        f.debug_list().entries(self.vht_mcs_support_iter()).finish()
121    }
122}
123#[bitfield(u64)]
124#[derive(PartialEq, Eq, Hash)]
125pub struct SupportedVHTMCSAndNSSSet {
126    #[bits(16)]
127    pub rx_vht_mcs_map: VHTMCSMap,
128    #[bits(13)]
129    pub rx_highest_supported_long_gi_data_rate: u16,
130    #[bits(3)]
131    pub maximum_nsts_total: u8,
132    #[bits(16)]
133    pub tx_vht_mcs_map: VHTMCSMap,
134    #[bits(13)]
135    pub tx_highest_supported_long_gi_data_rate: u16,
136    pub vht_extended_nss_bw_capable: bool,
137    #[bits(2)]
138    pub __: u8,
139}
140
141#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
142/// The capabilities of the STA's VHT PHY.
143pub struct VHTCapabilitiesElement {
144    pub vht_capabilities_info: VHTCapabilitiesInfo,
145    pub supported_vht_mcs_and_nss_set: SupportedVHTMCSAndNSSSet,
146}
147impl TryFromCtx<'_> for VHTCapabilitiesElement {
148    type Error = scroll::Error;
149    fn try_from_ctx(from: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
150        let mut offset = 0;
151        let vht_capabilities_info =
152            VHTCapabilitiesInfo::from_bits(from.gread_with(&mut offset, Endian::Little)?);
153        let supported_vht_mcs_and_nss_set =
154            SupportedVHTMCSAndNSSSet::from_bits(from.gread_with(&mut offset, Endian::Little)?);
155        Ok((
156            Self {
157                vht_capabilities_info,
158                supported_vht_mcs_and_nss_set,
159            },
160            offset,
161        ))
162    }
163}
164impl MeasureWith<()> for VHTCapabilitiesElement {
165    fn measure_with(&self, _ctx: &()) -> usize {
166        12
167    }
168}
169impl TryIntoCtx for VHTCapabilitiesElement {
170    type Error = scroll::Error;
171    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
172        let mut offset = 0;
173        buf.gwrite_with(
174            self.vht_capabilities_info.into_bits(),
175            &mut offset,
176            Endian::Little,
177        )?;
178        buf.gwrite_with(
179            self.supported_vht_mcs_and_nss_set.into_bits(),
180            &mut offset,
181            Endian::Little,
182        )?;
183        Ok(offset)
184    }
185}
186impl Element for VHTCapabilitiesElement {
187    const ELEMENT_ID: ElementID = ElementID::Id(0xbf);
188    type ReadType<'a> = VHTCapabilitiesElement;
189}
190serializable_enum! {
191    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
192    pub enum ChannelWidth : u8 {
193        TwentyOrFourtyMHz => 0,
194        EightyOneSixtyOrEightyPlusEightyMhz => 1,
195        OneSixtyMHz => 2,
196        NonContiguousEightyPlusEightyMHz => 3
197    }
198}
199#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
200/// The current VHT operation characteristics.
201pub struct VHTOperationElement {
202    pub channel_bandwidth: ChannelWidth,
203    pub channel_center_frequency_segment_0: u8,
204    pub channel_center_frequency_segment_1: u8,
205    pub basic_vht_mcs_and_nss_set: VHTMCSMap,
206}
207impl TryFromCtx<'_> for VHTOperationElement {
208    type Error = scroll::Error;
209    fn try_from_ctx(from: &'_ [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
210        let mut offset = 0;
211
212        let channel_bandwidth = ChannelWidth::from_bits(from.gread(&mut offset)?);
213        let channel_center_frequency_segment_0 = from.gread(&mut offset)?;
214        let channel_center_frequency_segment_1 = from.gread(&mut offset)?;
215        let basic_vht_mcs_and_nss_set =
216            VHTMCSMap::from_bits(from.gread_with(&mut offset, Endian::Little)?);
217
218        Ok((
219            Self {
220                channel_bandwidth,
221                channel_center_frequency_segment_0,
222                channel_center_frequency_segment_1,
223                basic_vht_mcs_and_nss_set,
224            },
225            offset,
226        ))
227    }
228}
229impl MeasureWith<()> for VHTOperationElement {
230    fn measure_with(&self, _ctx: &()) -> usize {
231        5
232    }
233}
234impl TryIntoCtx for VHTOperationElement {
235    type Error = scroll::Error;
236    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
237        let mut offset = 0;
238
239        buf.gwrite(self.channel_bandwidth.into_bits(), &mut offset)?;
240        buf.gwrite(self.channel_center_frequency_segment_0, &mut offset)?;
241        buf.gwrite(self.channel_center_frequency_segment_1, &mut offset)?;
242        buf.gwrite_with(
243            self.basic_vht_mcs_and_nss_set.into_bits(),
244            &mut offset,
245            Endian::Little,
246        )?;
247
248        Ok(offset)
249    }
250}
251impl Element for VHTOperationElement {
252    const ELEMENT_ID: ElementID = ElementID::Id(0xc0);
253    type ReadType<'a> = VHTOperationElement;
254}