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