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