wlan_common/ie/
fields.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::buffer_reader::BufferReader;
6use crate::mac::ReasonCode;
7use crate::organization::Oui;
8use crate::UnalignedView;
9use ieee80211::MacAddr;
10use static_assertions::const_assert_eq;
11use std::mem::size_of;
12use wlan_bitfield::bitfield;
13use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
14use {
15    banjo_fuchsia_wlan_ieee80211 as banjo_ieee80211, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
16};
17
18macro_rules! pub_const {
19    ($name:ident, $val:expr) => {
20        pub const $name: Self = Self($val);
21    };
22}
23
24// IEEE Std 802.11-2016, 9.4.2.3
25#[bitfield(
26    0..=6   rate,
27    7       basic,
28)]
29#[repr(C)]
30#[derive(
31    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy,
32)]
33pub struct SupportedRate(pub u8);
34
35impl SupportedRate {
36    /// Returns `true` if the rate is a supported BSS membership selector.
37    ///
38    /// Membership selector rates describe arbitrary features of a BSS in a backwards compatible
39    /// way. These selectors should **not** be interpreted as rates when the corresponding features
40    /// are supported, as they are designed to appear as rate incompatibility to WLAN
41    /// implementations that are unaware of such features.
42    pub fn is_bss_membership_selector(&self) -> bool {
43        match self.0 {
44            // These rates are used for HT and VHT BSS membership selectors. See IEEE Std
45            // 802.11-2016 9.4.2.2 Table 9-78.
46            0xFF | 0xFE => true,
47            _ => false,
48        }
49    }
50}
51
52// IEEE Std 802.11-2016, 9.4.2.4
53#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
54#[repr(C)]
55pub struct DsssParamSet {
56    pub current_channel: u8,
57}
58
59// IEEE Std 802.11-2016, 9.2.4.6
60#[bitfield(
61    0       group_traffic,
62    1..=7   offset,
63)]
64#[repr(C)]
65#[derive(
66    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy,
67)]
68pub struct BitmapControl(pub u8);
69
70// IEEE Std 802.11-2016, 9.4.2.6
71#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned, Clone, Copy)]
72#[repr(C, packed)]
73pub struct TimHeader {
74    pub dtim_count: u8,
75    pub dtim_period: u8,
76    pub bmp_ctrl: BitmapControl,
77}
78
79pub struct TimView<B> {
80    pub header: TimHeader,
81    pub bitmap: B,
82}
83
84// WFA WMM v1.2, 2.2.1
85#[bitfield(
86    0..=7   union {
87                client_wmm_info as ClientWmmInfo(u8),
88                ap_wmm_info as ApWmmInfo(u8),
89            }
90)]
91#[repr(C)]
92#[derive(
93    PartialEq, Eq, Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Default,
94)]
95pub struct WmmInfo(pub u8);
96
97// WFA WMM v1.2, 2.2.1 Figure 6
98#[bitfield(
99    0..=3   parameter_set_count,
100    4..=6   _, // reserved
101    7       uapsd
102)]
103#[repr(C)]
104#[derive(PartialEq, Eq, Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
105pub struct ApWmmInfo(pub u8);
106
107// WFA WMM v1.2, 2.2.1 Figure 7
108#[bitfield(
109    0       ac_vo_uapsd,
110    1       ac_vi_uapsd,
111    2       ac_bk_uapsd,
112    3       ac_be_uapsd,
113    4       _, // reserved
114    5..=6   max_sp_length,
115    7       _  // reserved
116)]
117#[repr(C)]
118#[derive(PartialEq, Eq, Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
119pub struct ClientWmmInfo(pub u8);
120
121// WFA WMM v1.2.0, 2.2.2 Table 5
122#[repr(C, packed)]
123#[derive(
124    PartialEq,
125    Eq,
126    Clone,
127    Copy,
128    Debug,
129    IntoBytes,
130    KnownLayout,
131    FromBytes,
132    Immutable,
133    Unaligned,
134    Default,
135)]
136pub struct WmmParam {
137    pub wmm_info: WmmInfo,
138    pub _reserved: u8,
139    pub ac_be_params: WmmAcParams,
140    pub ac_bk_params: WmmAcParams,
141    pub ac_vi_params: WmmAcParams,
142    pub ac_vo_params: WmmAcParams,
143}
144
145// WFA WMM v1.2.0, 2.2.2 Figure 9
146#[repr(C, packed)]
147#[derive(
148    PartialEq,
149    Eq,
150    Clone,
151    Copy,
152    Debug,
153    IntoBytes,
154    KnownLayout,
155    FromBytes,
156    Immutable,
157    Unaligned,
158    Default,
159)]
160pub struct WmmAcParams {
161    pub aci_aifsn: WmmAciAifsn,
162    pub ecw_min_max: EcwMinMax,
163    /// unit of 32 microseconds
164    pub txop_limit: u16,
165}
166
167// WFA WMM v1.2.0, 2.2.2 Figure 10
168// TODO(https://fxbug.dev/42163143): ACI is dependent on the AC parameters its encoding, so
169// it shouldn't be allowed to be set arbitrarily.
170#[bitfield(
171    0..=3   aifsn,
172    4       acm,
173    5..=6   aci,
174    7       _  // reserved
175)]
176#[repr(C)]
177#[derive(
178    PartialEq, Eq, Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Default,
179)]
180pub struct WmmAciAifsn(pub u8);
181
182// WFA WMM v1.2.0, 2.2.2 Figure 11
183#[bitfield(
184    0..=3   ecw_min,
185    4..=7   ecw_max,
186)]
187#[repr(C)]
188#[derive(
189    PartialEq, Eq, Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Default,
190)]
191pub struct EcwMinMax(pub u8);
192
193// IEEE Std 802.11-2016, 9.4.2.9
194pub struct CountryView<B> {
195    pub country_code: [u8; 2],
196    pub environment: CountryEnvironment,
197    // the rest are unparsed currently
198    pub subbands: B,
199}
200
201// IEEE Std 802.11-2016 Annex C, dot11CountryString
202#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
203pub struct CountryEnvironment(pub u8);
204
205impl CountryEnvironment {
206    pub const INDOOR: Self = Self(b'I');
207    pub const OUTDOOR: Self = Self(b'O');
208    pub const NON_COUNTRY: Self = Self(b'X');
209    pub const ANY: Self = Self(b' ');
210}
211
212// IEEE Std 802.11-2016, 9.4.2.56
213#[repr(C, packed)]
214#[derive(
215    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy, Debug,
216)]
217pub struct HtCapabilities {
218    pub ht_cap_info: HtCapabilityInfo, // u16
219    pub ampdu_params: AmpduParams,     // u8
220    pub mcs_set: SupportedMcsSet,      // u128
221    pub ht_ext_cap: HtExtCapabilities, // u16
222    pub txbf_cap: TxBfCapability,      // u32
223    pub asel_cap: AselCapability,      // u8
224}
225
226impl From<banjo_ieee80211::HtCapabilities> for HtCapabilities {
227    fn from(cap: banjo_ieee80211::HtCapabilities) -> Self {
228        // Safe to unwrap, since cap.bytes is fixed length.
229        const_assert_eq!(
230            std::mem::size_of::<HtCapabilities>(),
231            banjo_ieee80211::HT_CAP_LEN as usize,
232        );
233        HtCapabilities::read_from_bytes(&cap.bytes[..]).unwrap()
234    }
235}
236
237impl From<HtCapabilities> for banjo_ieee80211::HtCapabilities {
238    fn from(cap: HtCapabilities) -> Self {
239        let mut banjo_cap = Self { bytes: Default::default() };
240        banjo_cap.bytes.copy_from_slice(&cap.as_bytes()[..]);
241        banjo_cap
242    }
243}
244
245impl From<fidl_ieee80211::HtCapabilities> for HtCapabilities {
246    fn from(cap: fidl_ieee80211::HtCapabilities) -> Self {
247        // Safe to unwrap, since cap.bytes is fixed length.
248        const_assert_eq!(
249            std::mem::size_of::<HtCapabilities>(),
250            fidl_ieee80211::HT_CAP_LEN as usize,
251        );
252        HtCapabilities::read_from_bytes(&cap.bytes[..]).unwrap()
253    }
254}
255
256impl From<HtCapabilities> for fidl_ieee80211::HtCapabilities {
257    fn from(cap: HtCapabilities) -> Self {
258        let mut fidl_cap = Self { bytes: Default::default() };
259        fidl_cap.bytes.copy_from_slice(&cap.as_bytes()[..]);
260        fidl_cap
261    }
262}
263
264impl From<fidl_ieee80211::VhtCapabilities> for VhtCapabilities {
265    fn from(cap: fidl_ieee80211::VhtCapabilities) -> Self {
266        // Safe to unwrap, since cap.bytes is fixed length.
267        const_assert_eq!(
268            std::mem::size_of::<VhtCapabilities>(),
269            fidl_ieee80211::VHT_CAP_LEN as usize,
270        );
271        VhtCapabilities::read_from_bytes(&cap.bytes[..]).unwrap()
272    }
273}
274
275impl From<VhtCapabilities> for fidl_ieee80211::VhtCapabilities {
276    fn from(cap: VhtCapabilities) -> Self {
277        let mut fidl_cap = Self { bytes: Default::default() };
278        fidl_cap.bytes.copy_from_slice(&cap.as_bytes()[..]);
279        fidl_cap
280    }
281}
282
283// IEEE Std 802.11-2016, 9.4.2.56.2
284#[bitfield(
285    0       ldpc_coding_cap,
286    1..=1   chan_width_set as ChanWidthSet(u8), // In spec: Supported Channel Width Set
287    2..=3   sm_power_save as SmPowerSave(u8),   // Spatial Multiplexing Power Save
288    4       greenfield,                         // HT-Greenfield.
289    5       short_gi_20,                        // Short Guard Interval for 20 MHz
290    6       short_gi_40,                        // Short Guard Interval for 40 MHz
291    7       tx_stbc,
292
293    8..=9   rx_stbc,                            // maximum number of spatial streams. Up to 3.
294    10      delayed_block_ack,                  // HT-delayed Block Ack
295    11..=11 max_amsdu_len as MaxAmsduLen(u8),
296    12      dsss_in_40,                         // DSSS/CCK Mode in 40 MHz
297    13      _,                                  // reserved
298    14      intolerant_40,                      // 40 MHz Intolerant
299    15      lsig_txop_protect,
300)]
301#[repr(C)]
302#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
303pub struct HtCapabilityInfo(pub u16);
304
305#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
306pub struct ChanWidthSet(pub u8);
307impl ChanWidthSet {
308    pub_const!(TWENTY_ONLY, 0);
309    pub_const!(TWENTY_FORTY, 1);
310}
311
312#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
313pub struct SmPowerSave(pub u8);
314impl SmPowerSave {
315    pub_const!(STATIC, 0);
316    pub_const!(DYNAMIC, 1);
317    // 2 reserved
318    pub_const!(DISABLED, 3);
319}
320
321#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
322pub struct MaxAmsduLen(pub u8);
323impl MaxAmsduLen {
324    pub_const!(OCTETS_3839, 0);
325    pub_const!(OCTETS_7935, 1);
326}
327
328// IEEE Std 802.11-2016, 9.4.2.56.3
329#[bitfield(
330    0..=1 max_ampdu_exponent as MaxAmpduExponent(u8),   // Maximum A-MPDU Length Exponent. 0-3 valid
331    2..=4 min_start_spacing as MinMpduStartSpacing(u8), // Minimum MPDU Start Spacing.
332    5..=7 _,                                            // reserved
333)]
334#[repr(C)]
335#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
336pub struct AmpduParams(pub u8);
337
338#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
339pub struct MaxAmpduExponent(pub u8);
340impl MaxAmpduExponent {
341    pub fn to_len(&self) -> usize {
342        (1 << (13 + self.0)) - 1 as usize
343    }
344}
345
346#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
347pub struct MinMpduStartSpacing(pub u8);
348impl MinMpduStartSpacing {
349    pub_const!(NO_RESTRICT, 0);
350    pub_const!(QUATER_USEC, 1);
351    pub_const!(HALF_USEC, 2);
352    pub_const!(ONE_USEC, 3);
353    pub_const!(TWO_USEC, 4);
354    pub_const!(FOUR_USEC, 5);
355    pub_const!(EIGHT_USEC, 6);
356    pub_const!(SIXTEEN_USEC, 7);
357}
358
359// IEEE Std 802.11-2016, 9.4.2.56.4
360// HT-MCS table in IEEE Std 802.11-2016, Annex B.4.17.2
361// VHT-MCS tables in IEEE Std 802.11-2016, 21.5
362#[bitfield(
363    0..=76      rx_mcs as RxMcsBitmask(u128),
364    77..=79     _,                                  // reserved
365    80..=89     rx_highest_rate,                    // in Mbps
366    90..=95     _,                                  // reserved
367
368    96          tx_set_defined,
369    97          tx_rx_diff,
370    98..=99     tx_max_ss as NumSpatialStreams(u8),
371    100         tx_ueqm,                            // Transmit Unequal Modulation.
372    101..=127   _,                                  // reserved
373)]
374#[repr(C)]
375#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
376pub struct SupportedMcsSet(pub u128);
377
378#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
379pub struct RxMcsBitmask(pub u128);
380impl RxMcsBitmask {
381    pub fn support(&self, mcs_index: u8) -> bool {
382        mcs_index <= 76 && (self.0 & (1 << mcs_index)) != 0
383    }
384}
385
386#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
387pub struct NumSpatialStreams(u8);
388impl NumSpatialStreams {
389    // Value are "off-by-one" by definition. See IEEE 802.11-2016 Table 9-164
390    pub_const!(ONE, 0);
391    pub_const!(TWO, 1);
392    pub_const!(THREE, 2);
393    pub_const!(FOUR, 3);
394
395    pub fn to_human(&self) -> u8 {
396        1 + self.0
397    }
398    pub fn from_human(val: u8) -> Result<Self, String> {
399        if Self::ONE.to_human() <= val && val <= Self::FOUR.to_human() {
400            Ok(Self(val - 1))
401        } else {
402            Err(format!("Number of spatial stream must be between 1 and 4. {} is invalid", val))
403        }
404    }
405}
406
407// IEEE Std 802.11-2016, 9.4.2.56.5
408#[bitfield(
409    0       pco,
410    1..=2   pco_transition as PcoTransitionTime(u8),
411    3..=7   _,                                          // reserved
412    8..=9   mcs_feedback as McsFeedback(u8),
413    10      htc_ht_support,
414    11      rd_responder,
415    12..=15 _,                                          // reserved
416)]
417#[repr(C)]
418#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
419pub struct HtExtCapabilities(pub u16);
420
421#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
422pub struct PcoTransitionTime(pub u8);
423impl PcoTransitionTime {
424    pub_const!(PCO_RESERVED, 0); // Often translated as "No transition".
425    pub_const!(PCO_400_USEC, 1);
426    pub_const!(PCO_1500_USEC, 2);
427    pub_const!(PCO_5000_USEC, 3);
428}
429
430#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
431pub struct McsFeedback(pub u8);
432impl McsFeedback {
433    pub_const!(NO_FEEDBACK, 0);
434    // 1 reserved
435    pub_const!(UNSOLICITED, 2);
436    pub_const!(BOTH, 3);
437}
438
439// IEEE Std 802.11-2016, 9.4.2.56.6
440#[bitfield(
441    0       implicit_rx,
442    1       rx_stag_sounding,
443    2       tx_stag_sounding,
444    3       rx_ndp,
445    4       tx_ndp,
446    5       implicit,
447    6..=7   calibration as Calibration(u8),
448
449    8       csi,                                // Explicit CSI Transmit Beamforming.
450
451    9       noncomp_steering,                   // Explicit Noncompressed Steering
452    10      comp_steering,                      // Explicit Compressed Steering
453    11..=12 csi_feedback as Feedback(u8),
454    13..=14 noncomp_feedback as Feedback(u8),
455    15..=16 comp_feedback as Feedback(u8),
456    17..=18 min_grouping as MinGroup(u8),
457    19..=20 csi_antennas as NumAntennas(u8),
458
459    21..=22 noncomp_steering_ants as NumAntennas(u8),
460    23..=24 comp_steering_ants as NumAntennas(u8),
461    25..=26 csi_rows as NumCsiRows(u8),
462    27..=28 chan_estimation as NumSpaceTimeStreams(u8),
463    29..=31 _,                                  // reserved
464)]
465#[repr(C)]
466#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
467pub struct TxBfCapability(pub u32);
468
469#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
470pub struct Calibration(pub u8);
471impl Calibration {
472    pub_const!(NONE, 0);
473    pub_const!(RESPOND_NO_INITIATE, 1);
474    // 2 Reserved
475    pub_const!(RESPOND_INITIATE, 3);
476}
477
478#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
479pub struct Feedback(pub u8);
480impl Feedback {
481    pub_const!(NONE, 0);
482    pub_const!(DELAYED, 1);
483    pub_const!(IMMEDIATE, 2);
484    pub_const!(DELAYED_IMMEDIATE, 3);
485}
486
487#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
488pub struct MinGroup(pub u8);
489impl MinGroup {
490    pub_const!(ONE, 0); // Meaning no grouping
491    pub_const!(TWO, 1);
492    pub_const!(FOUR, 2);
493    pub_const!(TWO_FOUR, 3);
494}
495
496#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
497pub struct NumAntennas(u8);
498impl NumAntennas {
499    // Value are "off-by-one" by definition. See IEEE 802.11-2016 Table 9-166
500    pub_const!(ONE, 0);
501    pub_const!(TWO, 1);
502    pub_const!(THREE, 2);
503    pub_const!(FOUR, 3);
504
505    pub fn to_human(&self) -> u8 {
506        1 + self.0
507    }
508    pub fn from_human(val: u8) -> Result<Self, String> {
509        if Self::ONE.to_human() <= val && val <= Self::FOUR.to_human() {
510            Ok(Self(val - 1))
511        } else {
512            Err(format!("Number of antennas must be between 1 and 4. {} is invalid", val))
513        }
514    }
515}
516
517#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
518pub struct NumCsiRows(u8);
519impl NumCsiRows {
520    // Value are "off-by-one" by definition. See IEEE 802.11-2016 Table 9-166
521    pub_const!(ONE, 0);
522    pub_const!(TWO, 1);
523    pub_const!(THREE, 2);
524    pub_const!(FOUR, 3);
525
526    pub fn to_human(&self) -> u8 {
527        1 + self.0
528    }
529    pub fn from_human(val: u8) -> Result<Self, String> {
530        if Self::ONE.to_human() <= val && val <= Self::FOUR.to_human() {
531            Ok(Self(val - 1))
532        } else {
533            Err(format!("Number of csi rows must be between 1 and 4. {} is invalid", val))
534        }
535    }
536}
537
538#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
539pub struct NumSpaceTimeStreams(u8);
540impl NumSpaceTimeStreams {
541    // Value are "off-by-one" by definition. See IEEE 802.11-2016 Table 9-166
542    pub_const!(ONE, 0);
543    pub_const!(TWO, 1);
544    pub_const!(THREE, 2);
545    pub_const!(FOUR, 3);
546
547    pub fn to_human(&self) -> u8 {
548        1 + self.0
549    }
550    pub fn from_human(val: u8) -> Result<Self, String> {
551        if 1 <= val && val <= 4 {
552            Ok(Self(val - 1))
553        } else {
554            Err(format!("Number of channel estimation must be between 1 and 4. {} is invalid", val))
555        }
556    }
557}
558
559// IEEE Std 802.11-2016, 9.4.2.56.6
560#[bitfield(
561    0 asel,
562    1 csi_feedback_tx_asel,     // Explicit CSI Feedback based Transmit ASEL
563    2 ant_idx_feedback_tx_asel,
564    3 explicit_csi_feedback,
565    4 antenna_idx_feedback,
566    5 rx_asel,
567    6 tx_sounding_ppdu,
568    7 _,                        // reserved,
569)]
570#[repr(C)]
571#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
572pub struct AselCapability(pub u8);
573
574// IEEE Std 802.11-2016, 9.4.2.57
575#[repr(C, packed)]
576#[derive(
577    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy, Debug,
578)]
579pub struct HtOperation {
580    pub primary_channel: u8, // Primary 20 MHz channel.
581    pub ht_op_info: HtOpInfo,
582    pub basic_ht_mcs_set: SupportedMcsSet, // u128
583}
584
585impl From<HtOperation> for fidl_ieee80211::HtOperation {
586    fn from(op: HtOperation) -> Self {
587        let mut ht_op = Self { bytes: Default::default() };
588        ht_op.bytes.copy_from_slice(&op.as_bytes()[..]);
589        ht_op
590    }
591}
592
593// IEEE Std 802.11-2016, Figure 9-339
594#[bitfield(
595    0..=1 secondary_chan_offset as SecChanOffset(u8),
596    2..=2 sta_chan_width as StaChanWidth(u8),
597    3     rifs_mode_permitted,
598    4..=7 _,    // reserved. Note: used by 802.11n-D1.10 (before 802.11n-2009)
599
600    8..=9   ht_protection as HtProtection(u8),
601    10      nongreenfield_present,
602    11      _,                                  // reserved. Note: used in 802.11n-D1.10
603                                                // (before 802.11n-2009).
604    12      obss_non_ht_stas_present,
605    // IEEE 802.11-2016 Figure 9-339 has an inconsistency so this is Fuchsia interpretation:
606    // The channel number for the second segment in a 80+80 Mhz channel
607    13..=20 center_freq_seg2,                   // For VHT only. See Table 9-250
608    21..=23 _,                                  // reserved
609    24..=29 _,                                  // reserved
610    30      dual_beacon,                        // whether an STBC beacon is transmitted by the AP
611    31      dual_cts_protection,                // whether CTS protection is required
612    32      stbc_beacon,                        // 0 indicates primary beacon, 1 STBC beacon
613    33      lsig_txop_protection,               // only true if all HT STAs in the BSS support this
614    34      pco_active,
615    35..=35 pco_phase as PcoPhase(u8),
616    36..=39 _,                                  // reserved
617)]
618#[repr(C)]
619#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
620pub struct HtOpInfo(pub [u8; 5]);
621impl HtOpInfo {
622    pub fn new() -> HtOpInfo {
623        HtOpInfo([0u8; 5])
624    }
625}
626
627#[repr(C, packed)]
628#[derive(
629    Debug,
630    PartialOrd,
631    PartialEq,
632    Eq,
633    Clone,
634    Copy,
635    IntoBytes,
636    KnownLayout,
637    FromBytes,
638    Immutable,
639    Unaligned,
640)]
641pub struct SecChanOffset(pub u8);
642impl SecChanOffset {
643    pub_const!(SECONDARY_NONE, 0); // No secondary channel
644    pub_const!(SECONDARY_ABOVE, 1); // Secondary channel is above the primary channel
645                                    // 2 reserved
646    pub_const!(SECONDARY_BELOW, 3); // Secondary channel is below the primary channel
647}
648
649#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy)]
650pub struct StaChanWidth(pub u8);
651impl StaChanWidth {
652    pub_const!(TWENTY_MHZ, 0);
653    pub_const!(ANY, 1); // Any in the Supported Channel Width set
654}
655
656#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy)]
657pub struct HtProtection(pub u8);
658impl HtProtection {
659    pub_const!(NONE, 0);
660    pub_const!(NON_MEMBER, 1);
661    pub_const!(TWENTY_MHZ, 2);
662    pub_const!(NON_HT_MIXED, 3);
663}
664
665// IEEE Std 802.11-2016, 9.4.2.45
666#[bitfield(
667    0       link_measurement_enabled,
668    1       neighbor_report_enabled,
669    2       parallel_measurements_enabled,
670    3       repeated_measurements_enabled,
671    4       beacon_passive_measurement_enabled,
672    5       beacon_active_measurement_enabled,
673    6       beacon_table_measurement_enabled,
674    7       beacon_measurement_reporting_conditions_enabled,
675    8       frame_measurement_enabled,
676    9       channel_load_measurement_enabled,
677    10      noise_histogram_measurement_enabled,
678    11      statistics_measurement_enabled,
679    12      lci_measurement_enabled,
680    13      lci_azimuth_enabled,
681    14      tx_stream_category_measurement_enabled,
682    15      trigerred_tx_stream_category_measurement_enabled,
683    16      ap_channel_report_enabled,
684    17      rm_mib_enabled,
685    18..=20 operating_channel_max_measurement_duration,
686    21..=23 nonoperating_channel_max_measurement_duration,
687    24..=26 measurement_pilot_capability,
688    27      measurement_pilot_tx_info_enabled,
689    28      neighbor_report_tsf_offset_enabled,
690    29      rcpi_measurement_enabled,
691    30      rsni_measurement_enabled,
692    31      bss_average_access_delay_enabled,
693    32      bss_available_admission_capacity_enabled,
694    33      antenna_enabled,
695    34      ftm_range_report_enabled,
696    35      civic_location_measurement_enabled,
697    36..=39 _,
698)]
699#[repr(C)]
700#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
701pub struct RmEnabledCapabilities(pub [u8; 5]);
702
703#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy)]
704pub struct PcoPhase(pub u8);
705impl PcoPhase {
706    pub_const!(TWENTY_MHZ, 0);
707    pub_const!(FORTY_MHZ, 1);
708}
709
710#[repr(C)]
711#[derive(PartialEq, Eq, Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
712pub struct MpmProtocol(pub u16);
713
714// IEEE Std 802.11-2016, 9.4.2.102, table 9-222
715impl MpmProtocol {
716    pub_const!(MPM, 0);
717    pub_const!(AMPE, 1);
718    // 2-254 reserved
719    pub_const!(VENDOR_SPECIFIC, 255);
720    // 255-65535 reserved
721}
722
723// IEEE Std 802.11-2016, 9.4.2.27, Table 9-135
724pub struct ExtCapabilitiesView<B> {
725    // Extended capabilities has a variable number of bytes.
726    // The spec defines up to bit 72, but we only need the first 3 bytes right now.
727    pub ext_caps_octet_1: Option<Ref<B, ExtCapabilitiesOctet1>>,
728    pub ext_caps_octet_2: Option<Ref<B, ExtCapabilitiesOctet2>>,
729    pub ext_caps_octet_3: Option<Ref<B, ExtCapabilitiesOctet3>>,
730    pub remaining: B,
731}
732
733#[bitfield(
734    0       twenty_forty_bss_coexistence_mgmt_support,
735    1       _, // reserved
736    2       extended_channel_switching,
737    3       _, // reserved
738    4       psmp_capability,
739    5       _, // reserved
740    6       s_psmp_support,
741    7       event,
742)]
743#[repr(C)]
744#[derive(
745    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy, Unaligned,
746)]
747pub struct ExtCapabilitiesOctet1(pub u8);
748
749#[bitfield(
750    0       diagnostics,
751    1       multicast_diagnostics,
752    2       location_tracking,
753    3       fms,
754    4       proxy_arp_service,
755    5       collocated_interference_reporting,
756    6       civic_location,
757    7       geospatial_location,
758)]
759#[repr(C)]
760#[derive(
761    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy, Unaligned,
762)]
763pub struct ExtCapabilitiesOctet2(pub u8);
764
765#[bitfield(
766    0       tfs,
767    1       wnm_sleep_mode,
768    2       tim_broadcast,
769    3       bss_transition,
770    4       qos_traffic_capability,
771    5       ac_station_count,
772    6       multiple_bssid,
773    7       timing_measurement,
774)]
775#[repr(C)]
776#[derive(
777    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy, Unaligned,
778)]
779pub struct ExtCapabilitiesOctet3(pub u8);
780
781// IEEE Std 802.11-2016, 9.4.2.113, Figure 9-478
782#[bitfield(
783    0       gate_announcement,
784    1       addressing_mode,
785    2       proactive,
786    3..=5   _, // reserved
787    6       addr_ext,
788    7       _, // reserved
789)]
790#[repr(C)]
791#[derive(Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
792pub struct PreqFlags(pub u8);
793
794// Fixed-length fields of the PREQ element that precede
795// the optional Originator External Address field.
796// IEEE Std 802.11-2016, 9.4.2.113, Figure 9-477
797#[repr(C, packed)]
798#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
799pub struct PreqHeader {
800    pub flags: PreqFlags,
801    pub hop_count: u8,
802    pub element_ttl: u8,
803    pub path_discovery_id: u32,
804    pub originator_addr: MacAddr,
805    pub originator_hwmp_seqno: u32,
806}
807
808// Fixed-length fields of the PREQ elements that follow the optional Originator External Address
809// field and precede the variable length per-target fields.
810// IEEE Std 802.11-2016, 9.4.2.113, Figure 9-477
811#[repr(C, packed)]
812#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
813pub struct PreqMiddle {
814    pub lifetime: u32,
815    pub metric: u32,
816    pub target_count: u8,
817}
818
819// IEEE Std 802.11-2016, 9.4.2.113, Figure 9-479
820#[bitfield(
821    0       target_only,
822    1       _, // reserved
823    2       usn,
824    3..=7   _, // reserved
825)]
826#[repr(C)]
827#[derive(Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
828pub struct PreqPerTargetFlags(pub u8);
829
830// An entry of the variable-length part of PREQ
831// IEEE Std 802.11-2016, 9.4.2.113, Figure 9-477
832#[repr(C, packed)]
833#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
834pub struct PreqPerTarget {
835    pub flags: PreqPerTargetFlags,
836    pub target_addr: MacAddr,
837    pub target_hwmp_seqno: u32,
838}
839
840pub struct PreqView<B> {
841    pub header: Ref<B, PreqHeader>,
842    pub originator_external_addr: Option<Ref<B, MacAddr>>,
843    pub middle: Ref<B, PreqMiddle>,
844    pub targets: Ref<B, [PreqPerTarget]>,
845}
846
847// IEEE Std 802.11-2016, 9.4.2.114, Figure 9-481
848#[bitfield(
849    0..=5   _, // reserved
850    6       addr_ext,
851    7       _, // reserved
852)]
853#[repr(C)]
854#[derive(Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
855pub struct PrepFlags(pub u8);
856
857// Fixed-length fields of the PREP element that precede
858// the optional Target External Address field.
859// IEEE Std 802.11-2016, 9.4.2.114, Figure 9-480
860#[repr(C, packed)]
861#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
862pub struct PrepHeader {
863    pub flags: PrepFlags,
864    pub hop_count: u8,
865    pub element_ttl: u8,
866    pub target_addr: MacAddr,
867    pub target_hwmp_seqno: u32,
868}
869
870// Fixed-length fields of the PREP element that follow
871// the optional Target External Address field.
872// IEEE Std 802.11-2016, 9.4.2.114, Figure 9-480
873#[repr(C, packed)]
874#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
875pub struct PrepTail {
876    pub lifetime: u32,
877    pub metric: u32,
878    pub originator_addr: MacAddr,
879    pub originator_hwmp_seqno: u32,
880}
881
882pub struct PrepView<B> {
883    pub header: Ref<B, PrepHeader>,
884    pub target_external_addr: Option<Ref<B, MacAddr>>,
885    pub tail: Ref<B, PrepTail>,
886}
887
888// Fixed-length fields of the PERR element that precede the variable-length
889// per-destination fields.
890// IEEE Std 802.11-2016, 9.4.2.115
891#[repr(C, packed)]
892#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
893pub struct PerrHeader {
894    pub element_ttl: u8,
895    pub num_destinations: u8,
896}
897
898// IEEE Std 802.11-2016, 9.4.2.115, Figure 9-483
899#[bitfield(
900    0..=5   _, // reserved
901    6       addr_ext,
902    7       _, // reserved
903)]
904#[repr(C)]
905#[derive(Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
906pub struct PerrDestinationFlags(pub u8);
907
908// Fixed-length fields of the per-destination chunk of the PERR element
909// that precede the optional "Destination External Address" field.
910// IEEE Std 802.11-2016, 9.4.2.115
911#[repr(C, packed)]
912#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
913pub struct PerrDestinationHeader {
914    pub flags: PerrDestinationFlags,
915    pub dest_addr: MacAddr,
916    pub hwmp_seqno: u32,
917}
918
919pub struct PerrDestinationView<B> {
920    pub header: Ref<B, PerrDestinationHeader>,
921    pub ext_addr: Option<Ref<B, MacAddr>>,
922    pub reason_code: UnalignedView<B, ReasonCode>,
923}
924
925pub struct PerrView<B> {
926    pub header: Ref<B, PerrHeader>,
927    pub destinations: PerrDestinationListView<B>,
928}
929
930pub struct PerrDestinationListView<B>(pub B);
931
932impl<B: SplitByteSlice> IntoIterator for PerrDestinationListView<B> {
933    type Item = PerrDestinationView<B>;
934    type IntoIter = PerrDestinationIter<B>;
935
936    fn into_iter(self) -> Self::IntoIter {
937        PerrDestinationIter(BufferReader::new(self.0))
938    }
939}
940
941impl<'a, B: SplitByteSlice> IntoIterator for &'a PerrDestinationListView<B> {
942    type Item = PerrDestinationView<&'a [u8]>;
943    type IntoIter = PerrDestinationIter<&'a [u8]>;
944
945    fn into_iter(self) -> Self::IntoIter {
946        PerrDestinationIter(BufferReader::new(&self.0[..]))
947    }
948}
949
950impl<B: SplitByteSlice> PerrDestinationListView<B> {
951    pub fn iter(&self) -> PerrDestinationIter<&[u8]> {
952        self.into_iter()
953    }
954}
955
956pub struct PerrDestinationIter<B>(BufferReader<B>);
957
958impl<B: SplitByteSlice> Iterator for PerrDestinationIter<B> {
959    type Item = PerrDestinationView<B>;
960
961    fn next(&mut self) -> Option<Self::Item> {
962        let have_ext_addr = self.0.peek::<PerrDestinationHeader>()?.flags.addr_ext();
963        let dest_len = size_of::<PerrDestinationHeader>()
964            + if have_ext_addr { size_of::<MacAddr>() } else { 0 }
965            + size_of::<ReasonCode>();
966        if self.0.bytes_remaining() < dest_len {
967            None
968        } else {
969            // Unwraps are OK because we checked the length above
970            let header = self.0.read().unwrap();
971            let ext_addr = if have_ext_addr { Some(self.0.read().unwrap()) } else { None };
972            let reason_code = self.0.read_unaligned().unwrap();
973            Some(PerrDestinationView { header, ext_addr, reason_code })
974        }
975    }
976}
977
978impl<B: SplitByteSlice> PerrDestinationIter<B> {
979    pub fn bytes_remaining(&self) -> usize {
980        self.0.bytes_remaining()
981    }
982}
983
984// IEEE Std 802.11-2016 9.4.2.19: Channel Switch Announcement element
985// The element used to advertise a scheduled AP channel switch.
986#[repr(C, packed)]
987#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
988pub struct ChannelSwitchAnnouncement {
989    pub mode: u8,
990    pub new_channel_number: u8,
991    pub channel_switch_count: u8,
992}
993
994// IEEE Std 802.11-2016 9.4.2.53: Extended Channel Switch Announcement element
995// The extended element used to advertise a scheduled AP channel switch with
996// an operating class switch.
997#[repr(C, packed)]
998#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
999pub struct ExtendedChannelSwitchAnnouncement {
1000    pub mode: u8,
1001    pub new_operating_class: u8,
1002    pub new_channel_number: u8,
1003    pub channel_switch_count: u8,
1004}
1005
1006// IEEE Std 802.11-2016 9.4.2.161: Wide Bandwidth Channel Switch element
1007#[repr(C, packed)]
1008#[derive(Clone, Copy, Debug, IntoBytes, KnownLayout, FromBytes, Immutable)]
1009pub struct WideBandwidthChannelSwitch {
1010    pub new_width: VhtChannelBandwidth,
1011    pub new_center_freq_seg0: u8,
1012    pub new_center_freq_seg1: u8,
1013}
1014
1015// IEEE Std 802.11-2016, 9.4.2.162, Table 9-255
1016#[derive(Clone, Copy, Eq, PartialEq, Debug)]
1017pub struct MaxTransmitPowerUnitInterpretation(pub u8);
1018
1019impl MaxTransmitPowerUnitInterpretation {
1020    pub const EIRP: Self = Self(0);
1021}
1022
1023// IEEE Std 802.11-2016, 9.4.2.162, Figure 9-568
1024#[bitfield(
1025    0..=2   max_transmit_power_count,
1026    3..=5   max_transmit_power_unit_interpretation as MaxTransmitPowerUnitInterpretation(u8),
1027    6..=7   _, // reserved
1028)]
1029#[repr(C)]
1030#[derive(Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
1031pub struct TransmitPowerInfo(pub u8);
1032
1033// IEEE Std 802.11-2016 9.2.4.162: Transmit power is interpreted as an
1034// 8-bit 2s complement signed integer with a step of 0.5.
1035#[repr(C)]
1036#[derive(
1037    Clone, Copy, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Eq, PartialEq, Debug,
1038)]
1039pub struct TransmitPower(pub u8);
1040
1041// IEEE Std 802.11-2016 9.2.4.162: Transmit Power Envelope element
1042pub struct TransmitPowerEnvelopeView<B> {
1043    pub transmit_power_info: Ref<B, TransmitPowerInfo>,
1044    pub max_transmit_power_20: Ref<B, TransmitPower>,
1045    pub max_transmit_power_40: Option<Ref<B, TransmitPower>>,
1046    pub max_transmit_power_80: Option<Ref<B, TransmitPower>>,
1047    pub max_transmit_power_160: Option<Ref<B, TransmitPower>>,
1048}
1049
1050// IEEE Std 802.11-2016 9.2.4.163: Channel Switch Wrapper element
1051pub struct ChannelSwitchWrapperView<B> {
1052    pub new_country: Option<CountryView<B>>,
1053    pub wide_bandwidth_channel_switch: Option<Ref<B, WideBandwidthChannelSwitch>>,
1054    pub new_transmit_power_envelope: Option<TransmitPowerEnvelopeView<B>>,
1055}
1056
1057// This enum represents all vendor IEs we know how to parse, plus an Unknown option for all other
1058// vendor IEs.
1059#[derive(Debug)]
1060pub enum VendorIe<B: SplitByteSlice> {
1061    // This does not contain the first byte of the IE body, since this byte identifies the IE as
1062    // WPA rather than another MSFT vendor IE.
1063    MsftLegacyWpa(B),
1064    // WiFi Simple Configuration element.
1065    // Like WPA, this does not contain the bytes identifying the IE as WSC.
1066    Wsc(B),
1067    // WMM Info is a single byte. The IE header and the IE body's first six bytes
1068    // (OUI, OUI type, OUI subtype, and version) are stripped.
1069    WmmInfo(B),
1070    // This does not contain the IE body's first six bytes
1071    // (OUI, OUI type, OUI subtype, and version) that identify IE as WMM Parameter
1072    WmmParam(B),
1073    // IEEE Std 802.11-2016, 9.4.2.26
1074    Unknown { oui: Oui, body: B },
1075}
1076
1077// IEEE Std 802.11-2016, 9.4.2.57
1078#[repr(C, packed)]
1079#[derive(
1080    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy, Debug,
1081)]
1082pub struct VhtCapabilities {
1083    pub vht_cap_info: VhtCapabilitiesInfo, // u32
1084    pub vht_mcs_nss: VhtMcsNssSet,         // u64
1085}
1086
1087impl From<banjo_ieee80211::VhtCapabilities> for VhtCapabilities {
1088    fn from(cap: banjo_ieee80211::VhtCapabilities) -> Self {
1089        // Safe to unwrap, since cap.bytes is fixed length.
1090        const_assert_eq!(
1091            std::mem::size_of::<VhtCapabilities>(),
1092            banjo_ieee80211::VHT_CAP_LEN as usize,
1093        );
1094        VhtCapabilities::read_from_bytes(&cap.bytes[..]).unwrap()
1095    }
1096}
1097
1098impl From<VhtCapabilities> for banjo_ieee80211::VhtCapabilities {
1099    fn from(cap: VhtCapabilities) -> Self {
1100        let mut banjo_cap = Self { bytes: Default::default() };
1101        banjo_cap.bytes.copy_from_slice(&cap.as_bytes()[..]);
1102        banjo_cap
1103    }
1104}
1105
1106// IEEE Std 802.11-2016, 9.4.2.158.2
1107#[bitfield(
1108    0..=1   max_mpdu_len as MaxMpduLen(u8),
1109    2..=3   supported_cbw_set,                          // used with ext_nss_bw, See Table 9-250.
1110    4       rx_ldpc,
1111    5       sgi_cbw80,                                  // for CBW80 only
1112    6       sgi_cbw160,                                 // for CBW160 and CBW80P80
1113    7       tx_stbc,
1114    8..=10  rx_stbc,
1115    11      su_bfer,                                    // single user beamformer capable
1116    12      su_bfee,                                    // single user beamformee capable
1117    13..=15 bfee_sts,                                   // beamformee space-time spreading
1118                                                        // capability
1119
1120    16..=18 num_sounding,                               // number of sounding dimensions
1121    19      mu_bfer,                                    // multi user beamformer capable
1122    20      mu_bfee,                                    // multi user beamformer capable
1123    21      txop_ps,                                    // TXOP power save mode
1124    22      htc_vht,
1125    23..=25 max_ampdu_exponent as MaxAmpduExponent(u8), // valid values: 0-7
1126    26..=27 link_adapt as VhtLinkAdaptation(u8),        // VHT link adapatation capable,
1127                                                        // only valid if htc_vht is true
1128    28      rx_ant_pattern,
1129    29      tx_ant_pattern,
1130    30..=31 ext_nss_bw,                                 // Extended NSS BW support, used with
1131                                                        // supported_cbw_set to indicate NSS support
1132                                                        // for each BW. See Table 9-250.
1133)]
1134#[repr(C)]
1135#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
1136pub struct VhtCapabilitiesInfo(pub u32);
1137
1138// IEEE Std 802.11-2016, 9.4.2.79
1139#[repr(C, packed)]
1140#[derive(
1141    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy,
1142)]
1143pub struct BssMaxIdlePeriod {
1144    // dot11BssMaxIdlePeriod (IEEE Std 802.11-2016, 11.24.13 and Annex C.3) is measured in
1145    // increments of 1000 TUs, with a range from 1-65535.
1146    pub max_idle_period: u16,
1147    pub idle_options: IdleOptions,
1148}
1149
1150// IEEE Std 802.11-2016, 9.4.2.158.2
1151#[bitfield(
1152    0     protected_keep_alive_required, // Set to 1 to indicate only a protected frame indicates
1153                                         // activity
1154    1..=7 _, // reserved
1155)]
1156#[repr(C)]
1157#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
1158pub struct IdleOptions(pub u8);
1159
1160// IEEE Std 802.11-2016, Table 9-249
1161#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
1162pub struct MaxMpduLen(pub u8);
1163impl MaxMpduLen {
1164    pub_const!(OCTECTS_3895, 0);
1165    pub_const!(OCTECTS_7991, 1);
1166    pub_const!(OCTECTS_11454, 2);
1167    // 3 reserved
1168}
1169
1170// IEEE Std 802.11-2016, Table 9-249
1171#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
1172pub struct VhtLinkAdaptation(pub u8);
1173impl VhtLinkAdaptation {
1174    pub_const!(NO_FEEDBACK, 0);
1175    // 1 Reserved
1176    pub_const!(UNSOLICITED, 2);
1177    pub_const!(BOTH, 3);
1178}
1179
1180// IEEE Std 802.11-2016, 9.4.2.158.3
1181#[bitfield(
1182    0..=15  rx_max_mcs as VhtMcsNssMap(u16),
1183
1184    16..=28 rx_max_data_rate,               // Mbps rounded down to the nearest integer
1185    29..=31 max_nsts,
1186
1187    32..=47 tx_max_mcs as VhtMcsNssMap(u16),
1188
1189    48..=60 tx_max_data_rate,               // Mbps rounded down to the nearest integer
1190    61      ext_nss_bw,                     // Extended NSS BW Capable
1191    62..=63 _,                              // reserved
1192)]
1193#[repr(C)]
1194#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
1195pub struct VhtMcsNssSet(pub u64);
1196
1197// IEEE Std 802.11-2016, Figure 9-562.
1198#[bitfield(
1199    0..=1   ss1 as VhtMcsSet(u8),
1200    2..=3   ss2 as VhtMcsSet(u8),
1201    4..=5   ss3 as VhtMcsSet(u8),
1202    6..=7   ss4 as VhtMcsSet(u8),
1203    8..=9   ss5 as VhtMcsSet(u8),
1204    10..=11 ss6 as VhtMcsSet(u8),
1205    12..=13 ss7 as VhtMcsSet(u8),
1206    14..=15 ss8 as VhtMcsSet(u8),
1207)]
1208#[repr(C)]
1209#[derive(PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Clone, Copy)]
1210pub struct VhtMcsNssMap(pub u16);
1211impl VhtMcsNssMap {
1212    const BIT_WIDTH: u8 = 2;
1213    const MASK: u16 = (1 << Self::BIT_WIDTH) - 1;
1214
1215    pub fn ss(&self, num: u8) -> Result<VhtMcsSet, String> {
1216        if num >= 1 && num <= 8 {
1217            Ok(VhtMcsSet((self.0 >> ((num - 1) * Self::BIT_WIDTH) & Self::MASK) as u8))
1218        } else {
1219            Err(format!("spatial stream number must be between 1 and 8, {} invalid", num))
1220        }
1221    }
1222
1223    pub fn set_ss(&mut self, num: u8, val: VhtMcsSet) -> Result<(), String> {
1224        if num == 0 || num > 8 {
1225            Err(format!("spatial stream number must be between 1 and 8, {} invalid", num))
1226        } else if val.0 > 3 {
1227            Err(format!("bitfield is only 2 bit wide, {} invalid", val.0))
1228        } else {
1229            let offset = (num - 1) * Self::BIT_WIDTH;
1230            let mask = Self::MASK << offset;
1231            self.0 = (self.0 & (!mask)) | (((val.0 as u16) & Self::MASK) << offset);
1232            Ok(())
1233        }
1234    }
1235}
1236
1237#[derive(Debug, PartialOrd, PartialEq, Clone, Copy)]
1238pub struct VhtMcsSet(pub u8);
1239impl VhtMcsSet {
1240    pub_const!(UP_TO_7, 0);
1241    pub_const!(UP_TO_8, 1);
1242    pub_const!(UP_TO_9, 2);
1243    pub_const!(NONE, 3);
1244}
1245
1246// IEEE Std 802.11-2016, 9.4.2.159
1247#[repr(C, packed)]
1248#[derive(
1249    PartialEq, Eq, Hash, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Clone, Copy, Debug,
1250)]
1251// TODO(https://fxbug.dev/42104445): Derive phy parameters based on Table 9-250 and 9-253.
1252pub struct VhtOperation {
1253    pub vht_cbw: VhtChannelBandwidth, // u8
1254    pub center_freq_seg0: u8,         // Channel index
1255    pub center_freq_seg1: u8,         // Channel index
1256
1257    pub basic_mcs_nss: VhtMcsNssMap, // u16
1258}
1259
1260impl From<VhtOperation> for fidl_ieee80211::VhtOperation {
1261    fn from(op: VhtOperation) -> Self {
1262        let mut banjo_op = Self { bytes: Default::default() };
1263        banjo_op.bytes.copy_from_slice(&op.as_bytes()[..]);
1264        banjo_op
1265    }
1266}
1267
1268// IEEE Std 802.11-2016, Table 9-252
1269#[repr(C)]
1270#[derive(
1271    Debug,
1272    PartialOrd,
1273    PartialEq,
1274    Eq,
1275    Hash,
1276    IntoBytes,
1277    KnownLayout,
1278    FromBytes,
1279    Immutable,
1280    Clone,
1281    Copy,
1282)]
1283pub struct VhtChannelBandwidth(pub u8);
1284impl VhtChannelBandwidth {
1285    pub_const!(CBW_20_40, 0);
1286    pub_const!(CBW_80_160_80P80, 1);
1287    pub_const!(CBW_160, 2); // deprecated
1288    pub_const!(CBW_80P80, 3); // deprecated
1289                              // 4-255 reserved
1290}
1291
1292#[cfg(test)]
1293mod tests {
1294    use super::*;
1295
1296    #[test]
1297    fn ht_cap_mcs_set_conversion() {
1298        let from = banjo_ieee80211::HtCapabilities {
1299            bytes: [
1300                0, 1, // ht_capability_info
1301                2, // ampdu_params
1302                3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
1303                0x12, // supported_mcs_set
1304                0, 0x13, // ht_ext_capabilities
1305                0, 0, 0, 0x14, // tx_beamforming_capabilities
1306                0x15, // asel_capabilities
1307            ],
1308        };
1309        let ht_cap = Ref::<&[u8], HtCapabilities>::from_bytes(&from.bytes[..]).unwrap();
1310        let mcs_set = ht_cap.mcs_set;
1311        assert_eq!(
1312            mcs_set.as_bytes(),
1313            [3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12]
1314        );
1315    }
1316
1317    #[test]
1318    fn perr_iter_empty() {
1319        let empty: [u8; 0] = [];
1320        let mut iter = PerrDestinationListView(&empty[..]).into_iter();
1321        assert!(iter.next().is_none());
1322        assert_eq!(0, iter.bytes_remaining());
1323    }
1324
1325    #[test]
1326    fn perr_iter_two_destinations() {
1327        #[rustfmt::skip]
1328        let data = [
1329            // Destination 1
1330            0x40, // flags: address extension
1331            0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // dest addr
1332            0x11, 0x22, 0x33, 0x44, // HWMP seqno
1333            0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a,  // ext addr
1334            0x55, 0x66, // reason code
1335            // Destination 2
1336            0, // flags
1337            0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, // dest addr
1338            0x77, 0x88, 0x99, 0xaa, // HWMP seqno
1339            0xbb, 0xcc, // reason code
1340        ];
1341        let mut iter = PerrDestinationListView(&data[..]).into_iter();
1342        assert!(iter.bytes_remaining() > 0);
1343
1344        {
1345            let target = iter.next().expect("expected first target");
1346            assert_eq!(0x44332211, { target.header.hwmp_seqno });
1347            let ext_addr = target.ext_addr.expect("expected external addr");
1348            assert_eq!(MacAddr::from([0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a]), *ext_addr);
1349            assert_eq!(0x6655, target.reason_code.get().0);
1350        }
1351
1352        assert!(iter.bytes_remaining() > 0);
1353
1354        {
1355            let target = iter.next().expect("expected second target");
1356            assert_eq!(0xaa998877, { target.header.hwmp_seqno });
1357            assert!(target.ext_addr.is_none());
1358            assert_eq!(0xccbb, target.reason_code.get().0);
1359        }
1360
1361        assert_eq!(0, iter.bytes_remaining());
1362        assert!(iter.next().is_none());
1363        assert_eq!(0, iter.bytes_remaining());
1364    }
1365
1366    #[test]
1367    fn perr_iter_too_short_for_header() {
1368        #[rustfmt::skip]
1369        let data = [
1370            0x00, // flags: no address extension
1371            0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // dest addr
1372            0x11, 0x22, 0x33, // one byte missing from HWMP seqno
1373        ];
1374        let mut iter = PerrDestinationListView(&data[..]).into_iter();
1375        assert_eq!(data.len(), iter.bytes_remaining());
1376        assert!(iter.next().is_none());
1377        assert_eq!(data.len(), iter.bytes_remaining());
1378    }
1379
1380    #[test]
1381    fn perr_iter_too_short_for_ext_addr() {
1382        #[rustfmt::skip]
1383        let data = [
1384            // Destination 1
1385            0x40, // flags: address extension
1386            0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // dest addr
1387            0x11, 0x22, 0x33, 0x44, // HWMP seqno
1388            0x1a, 0x2a, 0x3a, 0x4a, 0x5a, // one byte missing from ext addr
1389        ];
1390        let mut iter = PerrDestinationListView(&data[..]).into_iter();
1391        assert_eq!(data.len(), iter.bytes_remaining());
1392        assert!(iter.next().is_none());
1393        assert_eq!(data.len(), iter.bytes_remaining());
1394    }
1395
1396    #[test]
1397    fn perr_iter_too_short_for_reason_code() {
1398        #[rustfmt::skip]
1399        let data = [
1400            // Target 1
1401            0x40, // flags: address extension
1402            0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // dest addr
1403            0x11, 0x22, 0x33, 0x44, // HWMP seqno
1404            0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a,  // ext addr
1405            0x55, // one byte missing from the reason code
1406        ];
1407        let mut iter = PerrDestinationListView(&data[..]).into_iter();
1408        assert_eq!(data.len(), iter.bytes_remaining());
1409        assert!(iter.next().is_none());
1410        assert_eq!(data.len(), iter.bytes_remaining());
1411    }
1412
1413    #[test]
1414    fn vht_mcs_nss_map_accessor() {
1415        let mut map = VhtMcsNssMap(0x00ff);
1416        assert_eq!(map.ss(1), Ok(VhtMcsSet(3)));
1417        assert_eq!(map.ss(5), Ok(VhtMcsSet(0)));
1418        assert_eq!(map.set_ss(1, VhtMcsSet(2)), Ok(()));
1419        assert_eq!(map.set_ss(8, VhtMcsSet(3)), Ok(()));
1420        assert_eq!(map.ss(1), Ok(VhtMcsSet(2)));
1421        assert_eq!(map.ss(8), Ok(VhtMcsSet(3)));
1422        assert_eq!(map.0, 0xc0fe);
1423    }
1424
1425    #[test]
1426    fn vht_mcs_nss_map_accssor_error() {
1427        let mut map = VhtMcsNssMap(0);
1428        assert_eq!(
1429            map.ss(0),
1430            Err("spatial stream number must be between 1 and 8, 0 invalid".to_string())
1431        );
1432        assert_eq!(
1433            map.ss(9),
1434            Err("spatial stream number must be between 1 and 8, 9 invalid".to_string())
1435        );
1436        assert_eq!(
1437            map.set_ss(0, VhtMcsSet(3)),
1438            Err("spatial stream number must be between 1 and 8, 0 invalid".to_string())
1439        );
1440        assert_eq!(
1441            map.set_ss(9, VhtMcsSet(3)),
1442            Err("spatial stream number must be between 1 and 8, 9 invalid".to_string())
1443        );
1444        assert_eq!(
1445            map.set_ss(1, VhtMcsSet(4)),
1446            Err("bitfield is only 2 bit wide, 4 invalid".to_string())
1447        );
1448    }
1449
1450    #[test]
1451    fn successfully_convert_ht_operation_to_fidl() {
1452        let ht_op = crate::ie::fake_ies::fake_ht_operation();
1453        let ddk: fidl_ieee80211::HtOperation = ht_op.into();
1454        assert_eq!(ht_op.as_bytes(), ddk.bytes);
1455    }
1456
1457    #[test]
1458    fn successfully_convert_vht_operation_to_fidl() {
1459        let vht_op = crate::ie::fake_ies::fake_vht_operation();
1460        let ddk: fidl_ieee80211::VhtOperation = vht_op.into();
1461        assert_eq!(vht_op.as_bytes(), ddk.bytes);
1462    }
1463}