Skip to main content

wlan_common/
channel.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::ie;
6use anyhow::format_err;
7use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
8use std::fmt;
9
10// IEEE Std 802.11-2016, Annex E
11// Note the distinction of index for primary20 and index for center frequency.
12// Fuchsia OS minimizes the use of the notion of center frequency,
13// with following exceptions:
14// - Cbw80P80's secondary frequency segment
15// - Frequency conversion at device drivers
16pub type MHz = u16;
17pub const BASE_FREQ_2GHZ: MHz = 2407;
18pub const BASE_FREQ_5GHZ: MHz = 5000;
19
20pub const INVALID_CHAN_IDX: u8 = 0;
21
22/// Channel bandwidth. Cbw80P80 requires the specification of
23/// channel index corresponding to the center frequency
24/// of the secondary consecutive frequency segment.
25#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
26pub enum Bandwidth {
27    Cbw20,
28    Cbw40, // Same as Cbw40Above
29    Cbw40Below,
30    Cbw80,
31    Cbw160,
32    Cbw80P80 { vht_secondary_80_channel: u8 },
33}
34
35impl Bandwidth {
36    // TODO(https://fxbug.dev/42164482): Implement `From `instead.
37    pub fn to_fidl(&self) -> (fidl_ieee80211::ChannelBandwidth, u8) {
38        match self {
39            Bandwidth::Cbw20 => (fidl_ieee80211::ChannelBandwidth::Cbw20, 0),
40            Bandwidth::Cbw40 => (fidl_ieee80211::ChannelBandwidth::Cbw40, 0),
41            Bandwidth::Cbw40Below => (fidl_ieee80211::ChannelBandwidth::Cbw40Below, 0),
42            Bandwidth::Cbw80 => (fidl_ieee80211::ChannelBandwidth::Cbw80, 0),
43            Bandwidth::Cbw160 => (fidl_ieee80211::ChannelBandwidth::Cbw160, 0),
44            Bandwidth::Cbw80P80 { vht_secondary_80_channel } => {
45                (fidl_ieee80211::ChannelBandwidth::Cbw80P80, *vht_secondary_80_channel)
46            }
47        }
48    }
49
50    pub fn from_fidl(
51        fidl_cbw: fidl_ieee80211::ChannelBandwidth,
52        fidl_vht_secondary_80_channel: u8,
53    ) -> Result<Self, anyhow::Error> {
54        match fidl_cbw {
55            fidl_ieee80211::ChannelBandwidth::Cbw20 => Ok(Bandwidth::Cbw20),
56            fidl_ieee80211::ChannelBandwidth::Cbw40 => Ok(Bandwidth::Cbw40),
57            fidl_ieee80211::ChannelBandwidth::Cbw40Below => Ok(Bandwidth::Cbw40Below),
58            fidl_ieee80211::ChannelBandwidth::Cbw80 => Ok(Bandwidth::Cbw80),
59            fidl_ieee80211::ChannelBandwidth::Cbw160 => Ok(Bandwidth::Cbw160),
60            fidl_ieee80211::ChannelBandwidth::Cbw80P80 => {
61                Ok(Bandwidth::Cbw80P80 { vht_secondary_80_channel: fidl_vht_secondary_80_channel })
62            }
63            fidl_ieee80211::ChannelBandwidthUnknown!() => {
64                Err(format_err!("Unknown channel bandwidth from fidl: {:?}", fidl_cbw))
65            }
66        }
67    }
68}
69
70/// A Channel defines the frequency spectrum to be used for radio synchronization.
71/// See for sister definitions in FIDL and C/C++
72///  - //sdk/fidl/fuchsia.wlan.common/wlan_common.fidl |struct wlan_channel_t|
73///  - //sdk/fidl/fuchsia.wlan.mlme/wlan_mlme.fidl |struct WlanChan|
74#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
75pub struct Channel {
76    pub primary: u8,
77    pub bandwidth: Bandwidth,
78    pub band: fidl_ieee80211::WlanBand,
79}
80
81// Fuchsia's short CBW notation. Not IEEE standard.
82impl fmt::Display for Bandwidth {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        match self {
85            Bandwidth::Cbw20 => write!(f, ""), // Vanilla plain 20 MHz bandwidth
86            Bandwidth::Cbw40 => write!(f, "+"), // SCA, often denoted by "+1"
87            Bandwidth::Cbw40Below => write!(f, "-"), // SCB, often denoted by "-1",
88            Bandwidth::Cbw80 => write!(f, "V"), // VHT 80 MHz (V from VHT)
89            Bandwidth::Cbw160 => write!(f, "W"), // VHT 160 MHz (as Wide as V + V ;) )
90            Bandwidth::Cbw80P80 { vht_secondary_80_channel } => {
91                write!(f, "+{}P", vht_secondary_80_channel)
92            } // VHT 80Plus80 (not often obvious, but P is the first alphabet)
93        }
94    }
95}
96
97impl fmt::Display for Channel {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        write!(f, "{}{} ({:?})", self.primary, self.bandwidth, self.band)
100    }
101}
102
103impl Channel {
104    pub const fn new(primary: u8, bandwidth: Bandwidth, band: fidl_ieee80211::WlanBand) -> Self {
105        Channel { primary, bandwidth, band }
106    }
107
108    fn get_band_start_freq(&self) -> Result<MHz, anyhow::Error> {
109        match self.band {
110            fidl_ieee80211::WlanBand::TwoGhz => Ok(BASE_FREQ_2GHZ),
111            fidl_ieee80211::WlanBand::FiveGhz => Ok(BASE_FREQ_5GHZ),
112            _ => Err(format_err!("cannot get band start freq for channel {}", self)),
113        }
114    }
115
116    fn get_center_chan_idx(&self) -> Result<u8, anyhow::Error> {
117        let is_valid = match self.band {
118            fidl_ieee80211::WlanBand::TwoGhz => self.primary <= 14,
119            fidl_ieee80211::WlanBand::FiveGhz => (36..=165).contains(&self.primary),
120            _ => false,
121        };
122        if !is_valid {
123            return Err(format_err!(
124                "cannot get center channel index for an invalid primary channel {}",
125                self
126            ));
127        }
128
129        let p = self.primary;
130        match self.bandwidth {
131            Bandwidth::Cbw20 => Ok(p),
132            Bandwidth::Cbw40 => Ok(p + 2),
133            Bandwidth::Cbw40Below => Ok(p - 2),
134            Bandwidth::Cbw80 | Bandwidth::Cbw80P80 { .. } => match p {
135                36..=48 => Ok(42),
136                52..=64 => Ok(58),
137                100..=112 => Ok(106),
138                116..=128 => Ok(122),
139                132..=144 => Ok(138),
140                148..=161_ => Ok(155),
141                _ => {
142                    return Err(format_err!(
143                        "cannot get center channel index for invalid channel {}",
144                        self
145                    ));
146                }
147            },
148            Bandwidth::Cbw160 => {
149                // See IEEE Std 802.11-2016 Table 9-252 and 9-253.
150                // Note CBW160 has only one frequency segment, regardless of
151                // encodings on CCFS0 and CCFS1 in VHT Operation Information IE.
152                match p {
153                    36..=64 => Ok(50),
154                    100..=128 => Ok(114),
155                    _ => {
156                        return Err(format_err!(
157                            "cannot get center channel index for invalid channel {}",
158                            self
159                        ));
160                    }
161                }
162            }
163        }
164    }
165
166    /// Returns the center frequency of the first consecutive frequency segment of the channel
167    /// in MHz if the channel is valid, Err(String) otherwise.
168    pub fn get_center_freq(&self) -> Result<MHz, anyhow::Error> {
169        // IEEE Std 802.11-2016, 21.3.14
170        let start_freq = self.get_band_start_freq()?;
171        let center_chan_idx = self.get_center_chan_idx()?;
172        let spacing: MHz = 5;
173        Ok(start_freq + spacing * center_chan_idx as u16)
174    }
175}
176
177impl Into<fidl_ieee80211::ChannelNumber> for Channel {
178    fn into(self) -> fidl_ieee80211::ChannelNumber {
179        fidl_ieee80211::ChannelNumber { band: self.band, number: self.primary }
180    }
181}
182impl Channel {
183    pub fn from_fidl(
184        fidl_channel: fidl_ieee80211::ChannelNumber,
185        fidl_cbw: fidl_ieee80211::ChannelBandwidth,
186        fidl_vht_secondary_80_channel: fidl_ieee80211::ChannelNumber,
187    ) -> Result<Self, anyhow::Error> {
188        if fidl_cbw == fidl_ieee80211::ChannelBandwidth::Cbw80P80 {
189            if fidl_vht_secondary_80_channel.band != fidl_channel.band {
190                return Err(format_err!(
191                    "vht_secondary_80_channel band ({:?}) does not match primary band ({:?})",
192                    fidl_vht_secondary_80_channel.band,
193                    fidl_channel.band
194                ));
195            }
196        }
197        let cbw = Bandwidth::from_fidl(fidl_cbw, fidl_vht_secondary_80_channel.number)?;
198        Ok(Channel::new(fidl_channel.number, cbw, fidl_channel.band))
199    }
200}
201
202/// Derive channel given DSSS param set, HT operation, and VHT operation IEs from
203/// beacon or probe response, and the primary channel from which such frame is
204/// received on.
205///
206/// Primary channel is extracted from HT op, DSSS param set, or `rx_primary_channel`,
207/// in descending priority.
208pub fn derive_channel(
209    rx_primary_channel: fidl_ieee80211::ChannelNumber,
210    dsss_channel: Option<u8>,
211    ht_op: Option<ie::HtOperation>,
212    vht_op: Option<ie::VhtOperation>,
213) -> Channel {
214    let primary = ht_op
215        .as_ref()
216        .map(|ht_op| ht_op.primary_channel)
217        .or(dsss_channel)
218        .unwrap_or(rx_primary_channel.number);
219
220    let ht_op_cbw = ht_op.map(|ht_op| ht_op.ht_op_info.sta_chan_width());
221    let vht_cbw_and_segs =
222        vht_op.map(|vht_op| (vht_op.vht_cbw, vht_op.center_freq_seg0, vht_op.center_freq_seg1));
223
224    let cbw = match ht_op_cbw {
225        // Inspect vht/ht op parameters to determine the channel width.
226        Some(ie::StaChanWidth::ANY) => {
227            // Safe to unwrap `ht_op` because `ht_op_cbw` is only Some(_) if `ht_op` has a value.
228            let sec_chan_offset = ht_op.unwrap().ht_op_info.secondary_chan_offset();
229            derive_wide_channel_bandwidth(vht_cbw_and_segs, sec_chan_offset)
230        }
231        // Default to Cbw20 if HT CBW field is set to 0 or not present.
232        _ => Bandwidth::Cbw20,
233    };
234
235    Channel::new(primary, cbw, rx_primary_channel.band)
236}
237
238/// Derive a CBW for a primary channel or channel switch.
239/// VHT parameter derivation is defined identically by:
240///     IEEE Std 802.11-2016 9.4.2.159 Table 9-252 for channel switching
241///     IEEE Std 802.11-2016 11.40.1 Table 11-24 for VHT operation
242/// SecChanOffset is defined identially by:
243///     IEEE Std 802.11-2016 9.4.2.20 for channel switching
244///     IEEE Std 802.11-2016 9.4.2.57 Table 9-168 for HT operation
245pub fn derive_wide_channel_bandwidth(
246    vht_cbw_and_segs: Option<(ie::VhtChannelBandwidth, u8, u8)>,
247    sec_chan_offset: ie::SecChanOffset,
248) -> Bandwidth {
249    use ie::VhtChannelBandwidth as Vcb;
250    match vht_cbw_and_segs {
251        Some((Vcb::CBW_80_160_80P80, _, 0)) => Bandwidth::Cbw80,
252        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) == 8 => Bandwidth::Cbw160,
253        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) > 16 => {
254            // See IEEE 802.11-2016, Table 9-252, about channel center frequency segment 1
255            Bandwidth::Cbw80P80 { vht_secondary_80_channel: seg1 }
256        }
257        // Use HT CBW if
258        // - VHT op is not present, or
259        // - VHT CBW field is set to 0
260        _ => match sec_chan_offset {
261            ie::SecChanOffset::SECONDARY_ABOVE => Bandwidth::Cbw40,
262            ie::SecChanOffset::SECONDARY_BELOW => Bandwidth::Cbw40Below,
263            ie::SecChanOffset::SECONDARY_NONE | _ => Bandwidth::Cbw20,
264        },
265    }
266}
267
268fn abs_sub(v1: u8, v2: u8) -> u8 {
269    if v2 >= v1 { v2 - v1 } else { v1 - v2 }
270}
271
272/// Converts a 20MHz primary channel center frequency in MHz to a channel number. Returns an error
273/// if the frequency does not correspond to the center frequency of a valid
274/// standard channel in the 2.4GHz or 5GHz bands.
275pub fn primary_channel_from_freq(freq: u32) -> Option<u8> {
276    // 2.4 GHz: Channels 1-13
277    if (2412..=2472).contains(&freq) && (freq - 2412) % 5 == 0 {
278        Some(((freq - 2407) / 5) as u8)
279    // 2.4 GHz: Channel 14
280    } else if freq == 2484 {
281        Some(14)
282    // 5 GHz: Channels 36-144
283    } else if (5180..=5720).contains(&freq) && (freq - 5180) % 20 == 0 {
284        Some(((freq - 5000) / 5) as u8)
285    // 5 GHz: Channels 149-173
286    } else if (5745..=5865).contains(&freq) && (freq - 5745) % 20 == 0 {
287        Some(((freq - 5000) / 5) as u8)
288    } else {
289        None
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296    use fidl_ieee80211::WlanBand::{FiveGhz, TwoGhz};
297
298    fn rx_channel(number: u8, band: fidl_ieee80211::WlanBand) -> fidl_ieee80211::ChannelNumber {
299        fidl_ieee80211::ChannelNumber { band, number }
300    }
301
302    #[test]
303    fn fmt_display() {
304        let mut c = Channel::new(100, Bandwidth::Cbw40, FiveGhz);
305        assert_eq!(format!("{}", c), "100+ (FiveGhz)");
306        c.bandwidth = Bandwidth::Cbw160;
307        assert_eq!(format!("{}", c), "100W (FiveGhz)");
308        c.bandwidth = Bandwidth::Cbw80P80 { vht_secondary_80_channel: 200 };
309        assert_eq!(format!("{}", c), "100+200P (FiveGhz)");
310    }
311
312    #[test]
313    fn test_band_start_freq() {
314        assert_eq!(
315            BASE_FREQ_2GHZ,
316            Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_band_start_freq().unwrap()
317        );
318        assert_eq!(
319            BASE_FREQ_5GHZ,
320            Channel::new(100, Bandwidth::Cbw20, FiveGhz).get_band_start_freq().unwrap()
321        );
322    }
323
324    #[test]
325    fn test_get_center_chan_idx() {
326        assert!(Channel::new(1, Bandwidth::Cbw80, TwoGhz).get_center_chan_idx().is_err());
327        assert_eq!(
328            9,
329            Channel::new(11, Bandwidth::Cbw40Below, TwoGhz).get_center_chan_idx().unwrap()
330        );
331        assert_eq!(8, Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_chan_idx().unwrap());
332        assert_eq!(36, Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_chan_idx().unwrap());
333        assert_eq!(38, Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_chan_idx().unwrap());
334        assert_eq!(42, Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
335        assert_eq!(50, Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap());
336        assert_eq!(
337            42,
338            Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
339                .get_center_chan_idx()
340                .unwrap()
341        );
342    }
343
344    #[test]
345    fn test_get_center_freq() {
346        assert_eq!(
347            2412 as MHz,
348            Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
349        );
350        assert_eq!(
351            2437 as MHz,
352            Channel::new(6, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
353        );
354        assert_eq!(
355            2447 as MHz,
356            Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_freq().unwrap()
357        );
358        assert_eq!(
359            2427 as MHz,
360            Channel::new(6, Bandwidth::Cbw40Below, TwoGhz).get_center_freq().unwrap()
361        );
362        assert_eq!(
363            5180 as MHz,
364            Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_freq().unwrap()
365        );
366        assert_eq!(
367            5190 as MHz,
368            Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_freq().unwrap()
369        );
370        assert_eq!(
371            5210 as MHz,
372            Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_freq().unwrap()
373        );
374        assert_eq!(
375            5250 as MHz,
376            Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
377        );
378        assert_eq!(
379            5210 as MHz,
380            Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
381                .get_center_freq()
382                .unwrap()
383        );
384    }
385
386    #[test]
387    fn test_primarychannel_from_freq() {
388        assert_eq!(Some(1), primary_channel_from_freq(2412));
389        // This is between the center frequencies of channel 1 and 2
390        assert_eq!(None, primary_channel_from_freq(2413));
391        assert_eq!(Some(6), primary_channel_from_freq(2437));
392        assert_eq!(Some(13), primary_channel_from_freq(2472));
393        assert_eq!(Some(14), primary_channel_from_freq(2484));
394        // This is below the range of recognized 5GHz channels
395        assert_eq!(None, primary_channel_from_freq(5160));
396        assert_eq!(Some(36), primary_channel_from_freq(5180));
397        // This is between the center frequencies of channel 36 and 40
398        assert_eq!(None, primary_channel_from_freq(5190));
399        assert_eq!(Some(165), primary_channel_from_freq(5825));
400        assert_eq!(Some(173), primary_channel_from_freq(5865));
401        // This is below the range of recognized 2.4GHz channels
402        assert_eq!(None, primary_channel_from_freq(2400));
403        // This is below the range of recognized 5GHz channels
404        assert_eq!(None, primary_channel_from_freq(5000));
405        // This is above the range of recognized channels
406        assert_eq!(None, primary_channel_from_freq(5885));
407    }
408
409    const RX_PRIMARY_CHAN: u8 = 11;
410    const RX_PRIMARY_CHAN_5GHZ: u8 = 36;
411    const HT_PRIMARY_CHAN: u8 = 48;
412
413    #[test]
414    fn test_derive_channel_basic() {
415        let channel = derive_channel(
416            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
417            None,
418            None,
419            None,
420        );
421        assert_eq!(channel, Channel::new(RX_PRIMARY_CHAN, Bandwidth::Cbw20, TwoGhz));
422    }
423
424    #[test]
425    fn test_derive_channel_with_dsss_param() {
426        let channel = derive_channel(
427            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
428            Some(6),
429            None,
430            None,
431        );
432        assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz));
433    }
434
435    #[test]
436    fn test_derive_channel_with_ht_20mhz() {
437        let expected_channel = Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw20, FiveGhz);
438
439        let test_params = [
440            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_NONE),
441            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_ABOVE),
442            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_BELOW),
443            (ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_NONE),
444        ];
445
446        for (ht_width, sec_chan_offset) in test_params.iter() {
447            let ht_op = ht_op(HT_PRIMARY_CHAN, *ht_width, *sec_chan_offset);
448            let channel = derive_channel(
449                fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
450                Some(RX_PRIMARY_CHAN_5GHZ),
451                Some(ht_op),
452                None,
453            );
454            assert_eq!(channel, expected_channel);
455        }
456    }
457
458    #[test]
459    fn test_derive_channel_with_ht_40mhz() {
460        let ht_op =
461            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
462        let channel = derive_channel(
463            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
464            Some(RX_PRIMARY_CHAN_5GHZ),
465            Some(ht_op),
466            None,
467        );
468        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40, FiveGhz));
469    }
470
471    #[test]
472    fn test_derive_channel_with_ht_40mhz_below() {
473        let ht_op =
474            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_BELOW);
475        let channel = derive_channel(
476            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
477            Some(RX_PRIMARY_CHAN_5GHZ),
478            Some(ht_op),
479            None,
480        );
481        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40Below, FiveGhz));
482    }
483
484    #[test]
485    fn test_derive_channel_with_vht_80mhz() {
486        let ht_op =
487            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
488        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 8, 0);
489        let channel = derive_channel(
490            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
491            Some(RX_PRIMARY_CHAN_5GHZ),
492            Some(ht_op),
493            Some(vht_op),
494        );
495        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw80, FiveGhz));
496    }
497
498    #[test]
499    fn test_derive_channel_with_vht_160mhz() {
500        let ht_op =
501            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
502        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 0, 8);
503        let channel = derive_channel(
504            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
505            Some(RX_PRIMARY_CHAN_5GHZ),
506            Some(ht_op),
507            Some(vht_op),
508        );
509        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw160, FiveGhz));
510    }
511
512    #[test]
513    fn test_derive_channel_with_vht_80plus80mhz() {
514        let ht_op =
515            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
516        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 18, 1);
517        let channel = derive_channel(
518            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
519            Some(RX_PRIMARY_CHAN_5GHZ),
520            Some(ht_op),
521            Some(vht_op),
522        );
523        assert_eq!(
524            channel,
525            Channel::new(
526                HT_PRIMARY_CHAN,
527                Bandwidth::Cbw80P80 { vht_secondary_80_channel: 1 },
528                FiveGhz
529            )
530        );
531    }
532
533    #[test]
534    fn test_derive_channel_none() {
535        let channel = derive_channel(rx_channel(8, TwoGhz), None, None, None);
536        assert_eq!(channel, Channel::new(8, Bandwidth::Cbw20, TwoGhz));
537    }
538
539    #[test]
540    fn test_derive_channel_no_rx_primary() {
541        let channel = derive_channel(rx_channel(8, TwoGhz), Some(6), None, None);
542        assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz))
543    }
544
545    fn ht_op(
546        primary_channel: u8,
547        chan_width: ie::StaChanWidth,
548        offset: ie::SecChanOffset,
549    ) -> ie::HtOperation {
550        let ht_op_info =
551            ie::HtOpInfo::new().with_sta_chan_width(chan_width).with_secondary_chan_offset(offset);
552        ie::HtOperation { primary_channel, ht_op_info, basic_ht_mcs_set: ie::SupportedMcsSet(0) }
553    }
554
555    fn vht_op(vht_cbw: ie::VhtChannelBandwidth, seg0: u8, seg1: u8) -> ie::VhtOperation {
556        ie::VhtOperation {
557            vht_cbw,
558            center_freq_seg0: seg0,
559            center_freq_seg1: seg1,
560            basic_mcs_nss: ie::VhtMcsNssMap(0),
561        }
562    }
563}