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 => (1..=14).contains(&self.primary),
119            fidl_ieee80211::WlanBand::FiveGhz => (36..=177).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 => {
133                p.checked_add(2).ok_or_else(|| format_err!("invalid channel for Cbw40: {}", p))
134            }
135            Bandwidth::Cbw40Below => p
136                .checked_sub(2)
137                .filter(|&res| res != INVALID_CHAN_IDX)
138                .ok_or_else(|| format_err!("invalid channel for Cbw40Below: {}", p)),
139            Bandwidth::Cbw80 | Bandwidth::Cbw80P80 { .. } => match p {
140                36..=48 => Ok(42),
141                52..=64 => Ok(58),
142                100..=112 => Ok(106),
143                116..=128 => Ok(122),
144                132..=144 => Ok(138),
145                149..=161 => Ok(155),
146                165..=177 => Ok(171),
147                _ => {
148                    return Err(format_err!(
149                        "cannot get center channel index for invalid channel {}",
150                        self
151                    ));
152                }
153            },
154            Bandwidth::Cbw160 => match p {
155                36..=64 => Ok(50),
156                100..=128 => Ok(114),
157                149..=177 => Ok(163),
158                _ => {
159                    return Err(format_err!(
160                        "cannot get center channel index for invalid channel {}",
161                        self
162                    ));
163                }
164            },
165        }
166    }
167
168    /// Returns the center frequency of the first consecutive frequency segment of the channel
169    /// in MHz if the channel is valid, Err(String) otherwise.
170    pub fn get_center_freq(&self) -> Result<MHz, anyhow::Error> {
171        // IEEE Std 802.11-2016, 21.3.14
172        let start_freq = self.get_band_start_freq()?;
173        let center_chan_idx = self.get_center_chan_idx()?;
174        let spacing: MHz = 5;
175        let offset = spacing
176            .checked_mul(center_chan_idx as u16)
177            .ok_or_else(|| format_err!("overflow computing channel spacing offset for {}", self))?;
178        start_freq
179            .checked_add(offset)
180            .ok_or_else(|| format_err!("overflow computing center frequency for {}", self))
181    }
182}
183
184impl Into<fidl_ieee80211::ChannelNumber> for Channel {
185    fn into(self) -> fidl_ieee80211::ChannelNumber {
186        fidl_ieee80211::ChannelNumber { band: self.band, number: self.primary }
187    }
188}
189impl Channel {
190    pub fn from_fidl(
191        fidl_channel: fidl_ieee80211::ChannelNumber,
192        fidl_cbw: fidl_ieee80211::ChannelBandwidth,
193        fidl_vht_secondary_80_channel: fidl_ieee80211::ChannelNumber,
194    ) -> Result<Self, anyhow::Error> {
195        if fidl_cbw == fidl_ieee80211::ChannelBandwidth::Cbw80P80 {
196            if fidl_vht_secondary_80_channel.band != fidl_channel.band {
197                return Err(format_err!(
198                    "vht_secondary_80_channel band ({:?}) does not match primary band ({:?})",
199                    fidl_vht_secondary_80_channel.band,
200                    fidl_channel.band
201                ));
202            }
203        }
204        let cbw = Bandwidth::from_fidl(fidl_cbw, fidl_vht_secondary_80_channel.number)?;
205        Ok(Channel::new(fidl_channel.number, cbw, fidl_channel.band))
206    }
207}
208
209/// Derive channel given DSSS param set, HT operation, and VHT operation IEs from
210/// beacon or probe response, and the primary channel from which such frame is
211/// received on.
212///
213/// Primary channel is extracted from HT op, DSSS param set, or `rx_primary_channel`,
214/// in descending priority.
215pub fn derive_channel(
216    rx_primary_channel: fidl_ieee80211::ChannelNumber,
217    dsss_channel: Option<u8>,
218    ht_op: Option<ie::HtOperation>,
219    vht_op: Option<ie::VhtOperation>,
220) -> Channel {
221    let primary = ht_op
222        .as_ref()
223        .map(|ht_op| ht_op.primary_channel)
224        .or(dsss_channel)
225        .unwrap_or(rx_primary_channel.number);
226
227    let ht_op_cbw = ht_op.map(|ht_op| ht_op.ht_op_info.sta_chan_width());
228    let vht_cbw_and_segs =
229        vht_op.map(|vht_op| (vht_op.vht_cbw, vht_op.center_freq_seg0, vht_op.center_freq_seg1));
230
231    let cbw = match ht_op_cbw {
232        // Inspect vht/ht op parameters to determine the channel width.
233        Some(ie::StaChanWidth::ANY) => {
234            // Safe to unwrap `ht_op` because `ht_op_cbw` is only Some(_) if `ht_op` has a value.
235            let sec_chan_offset = ht_op.unwrap().ht_op_info.secondary_chan_offset();
236            derive_wide_channel_bandwidth(vht_cbw_and_segs, sec_chan_offset)
237        }
238        // Default to Cbw20 if HT CBW field is set to 0 or not present.
239        _ => Bandwidth::Cbw20,
240    };
241
242    Channel::new(primary, cbw, rx_primary_channel.band)
243}
244
245/// Derive a CBW for a primary channel or channel switch.
246/// VHT parameter derivation is defined identically by:
247///     IEEE Std 802.11-2016 9.4.2.159 Table 9-252 for channel switching
248///     IEEE Std 802.11-2016 11.40.1 Table 11-24 for VHT operation
249/// SecChanOffset is defined identially by:
250///     IEEE Std 802.11-2016 9.4.2.20 for channel switching
251///     IEEE Std 802.11-2016 9.4.2.57 Table 9-168 for HT operation
252pub fn derive_wide_channel_bandwidth(
253    vht_cbw_and_segs: Option<(ie::VhtChannelBandwidth, u8, u8)>,
254    sec_chan_offset: ie::SecChanOffset,
255) -> Bandwidth {
256    use ie::VhtChannelBandwidth as Vcb;
257    match vht_cbw_and_segs {
258        Some((Vcb::CBW_80_160_80P80, _, 0)) => Bandwidth::Cbw80,
259        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if seg0.abs_diff(seg1) == 8 => Bandwidth::Cbw160,
260        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if seg0.abs_diff(seg1) > 16 => {
261            // See IEEE 802.11-2016, Table 9-252, about channel center frequency segment 1
262            Bandwidth::Cbw80P80 { vht_secondary_80_channel: seg1 }
263        }
264        // Use HT CBW if
265        // - VHT op is not present, or
266        // - VHT CBW field is set to 0
267        _ => match sec_chan_offset {
268            ie::SecChanOffset::SECONDARY_ABOVE => Bandwidth::Cbw40,
269            ie::SecChanOffset::SECONDARY_BELOW => Bandwidth::Cbw40Below,
270            ie::SecChanOffset::SECONDARY_NONE | _ => Bandwidth::Cbw20,
271        },
272    }
273}
274
275/// Converts a 20MHz primary channel center frequency in MHz to a channel number. Returns an error
276/// if the frequency does not correspond to the center frequency of a valid
277/// standard channel in the 2.4GHz or 5GHz bands.
278pub fn primary_channel_from_freq(freq: u32) -> Option<u8> {
279    // 2.4 GHz: Channels 1-13
280    if (2412..=2472).contains(&freq) && (freq - 2412) % 5 == 0 {
281        Some(((freq - 2407) / 5) as u8)
282    // 2.4 GHz: Channel 14
283    } else if freq == 2484 {
284        Some(14)
285    // 5 GHz: Channels 36-144
286    } else if (5180..=5720).contains(&freq) && (freq - 5180) % 20 == 0 {
287        Some(((freq - 5000) / 5) as u8)
288    // 5 GHz: Channels 149-173
289    } else if (5745..=5865).contains(&freq) && (freq - 5745) % 20 == 0 {
290        Some(((freq - 5000) / 5) as u8)
291    } else {
292        None
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299    use fidl_ieee80211::WlanBand::{FiveGhz, TwoGhz};
300
301    fn rx_channel(number: u8, band: fidl_ieee80211::WlanBand) -> fidl_ieee80211::ChannelNumber {
302        fidl_ieee80211::ChannelNumber { band, number }
303    }
304
305    #[test]
306    fn fmt_display() {
307        let mut c = Channel::new(100, Bandwidth::Cbw40, FiveGhz);
308        assert_eq!(format!("{}", c), "100+ (FiveGhz)");
309        c.bandwidth = Bandwidth::Cbw160;
310        assert_eq!(format!("{}", c), "100W (FiveGhz)");
311        c.bandwidth = Bandwidth::Cbw80P80 { vht_secondary_80_channel: 200 };
312        assert_eq!(format!("{}", c), "100+200P (FiveGhz)");
313    }
314
315    #[test]
316    fn test_band_start_freq() {
317        assert_eq!(
318            BASE_FREQ_2GHZ,
319            Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_band_start_freq().unwrap()
320        );
321        assert_eq!(
322            BASE_FREQ_5GHZ,
323            Channel::new(100, Bandwidth::Cbw20, FiveGhz).get_band_start_freq().unwrap()
324        );
325    }
326
327    #[test]
328    fn test_get_center_chan_idx() {
329        assert!(Channel::new(1, Bandwidth::Cbw80, TwoGhz).get_center_chan_idx().is_err());
330        assert_eq!(
331            9,
332            Channel::new(11, Bandwidth::Cbw40Below, TwoGhz).get_center_chan_idx().unwrap()
333        );
334        assert_eq!(8, Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_chan_idx().unwrap());
335        assert_eq!(36, Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_chan_idx().unwrap());
336        assert_eq!(38, Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_chan_idx().unwrap());
337
338        // 80 MHz ranges: beginning and end of each range
339        assert_eq!(42, Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
340        assert_eq!(42, Channel::new(48, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
341        assert_eq!(58, Channel::new(52, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
342        assert_eq!(58, Channel::new(64, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap());
343        assert_eq!(
344            106,
345            Channel::new(100, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
346        );
347        assert_eq!(
348            106,
349            Channel::new(112, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
350        );
351        assert_eq!(
352            122,
353            Channel::new(116, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
354        );
355        assert_eq!(
356            122,
357            Channel::new(128, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
358        );
359        assert_eq!(
360            138,
361            Channel::new(132, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
362        );
363        assert_eq!(
364            138,
365            Channel::new(144, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
366        );
367        assert_eq!(
368            155,
369            Channel::new(149, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
370        );
371        assert_eq!(
372            155,
373            Channel::new(161, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
374        );
375        assert_eq!(
376            171,
377            Channel::new(165, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
378        );
379        assert_eq!(
380            171,
381            Channel::new(177, Bandwidth::Cbw80, FiveGhz).get_center_chan_idx().unwrap()
382        );
383
384        // 160 MHz ranges: beginning and end of each range
385        assert_eq!(50, Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap());
386        assert_eq!(50, Channel::new(64, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap());
387        assert_eq!(
388            114,
389            Channel::new(100, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap()
390        );
391        assert_eq!(
392            114,
393            Channel::new(128, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap()
394        );
395        assert_eq!(
396            163,
397            Channel::new(149, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap()
398        );
399        assert_eq!(
400            163,
401            Channel::new(177, Bandwidth::Cbw160, FiveGhz).get_center_chan_idx().unwrap()
402        );
403
404        // 80+80 MHz
405        assert_eq!(
406            42,
407            Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
408                .get_center_chan_idx()
409                .unwrap()
410        );
411
412        // These tests are edge-case channel validity checks.
413        // Channel 1 Cbw40Below has the potential to underflow.
414        // Channel 255 Cbw40 has the potential to overflow, but is caught by earlier channel index
415        // validation.
416        assert!(Channel::new(1, Bandwidth::Cbw40Below, TwoGhz).get_center_chan_idx().is_err());
417        assert!(Channel::new(255, Bandwidth::Cbw40, TwoGhz).get_center_chan_idx().is_err());
418    }
419
420    #[test]
421    fn test_get_center_freq() {
422        assert_eq!(
423            2412 as MHz,
424            Channel::new(1, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
425        );
426        assert_eq!(
427            2437 as MHz,
428            Channel::new(6, Bandwidth::Cbw20, TwoGhz).get_center_freq().unwrap()
429        );
430        assert_eq!(
431            2447 as MHz,
432            Channel::new(6, Bandwidth::Cbw40, TwoGhz).get_center_freq().unwrap()
433        );
434        assert_eq!(
435            2427 as MHz,
436            Channel::new(6, Bandwidth::Cbw40Below, TwoGhz).get_center_freq().unwrap()
437        );
438        assert_eq!(
439            5180 as MHz,
440            Channel::new(36, Bandwidth::Cbw20, FiveGhz).get_center_freq().unwrap()
441        );
442        assert_eq!(
443            5190 as MHz,
444            Channel::new(36, Bandwidth::Cbw40, FiveGhz).get_center_freq().unwrap()
445        );
446        assert_eq!(
447            5210 as MHz,
448            Channel::new(36, Bandwidth::Cbw80, FiveGhz).get_center_freq().unwrap()
449        );
450        assert_eq!(
451            5775 as MHz,
452            Channel::new(149, Bandwidth::Cbw80, FiveGhz).get_center_freq().unwrap()
453        );
454        assert_eq!(
455            5855 as MHz,
456            Channel::new(165, Bandwidth::Cbw80, FiveGhz).get_center_freq().unwrap()
457        );
458        assert_eq!(
459            5250 as MHz,
460            Channel::new(36, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
461        );
462        assert_eq!(
463            5570 as MHz,
464            Channel::new(100, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
465        );
466        assert_eq!(
467            5815 as MHz,
468            Channel::new(149, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
469        );
470        assert_eq!(
471            5815 as MHz,
472            Channel::new(165, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
473        );
474        assert_eq!(
475            5815 as MHz,
476            Channel::new(173, Bandwidth::Cbw160, FiveGhz).get_center_freq().unwrap()
477        );
478        assert_eq!(
479            5210 as MHz,
480            Channel::new(36, Bandwidth::Cbw80P80 { vht_secondary_80_channel: 155 }, FiveGhz)
481                .get_center_freq()
482                .unwrap()
483        );
484    }
485
486    #[test]
487    fn test_primarychannel_from_freq() {
488        assert_eq!(Some(1), primary_channel_from_freq(2412));
489        // This is between the center frequencies of channel 1 and 2
490        assert_eq!(None, primary_channel_from_freq(2413));
491        assert_eq!(Some(6), primary_channel_from_freq(2437));
492        assert_eq!(Some(13), primary_channel_from_freq(2472));
493        assert_eq!(Some(14), primary_channel_from_freq(2484));
494        // This is below the range of recognized 5GHz channels
495        assert_eq!(None, primary_channel_from_freq(5160));
496        assert_eq!(Some(36), primary_channel_from_freq(5180));
497        // This is between the center frequencies of channel 36 and 40
498        assert_eq!(None, primary_channel_from_freq(5190));
499        assert_eq!(Some(165), primary_channel_from_freq(5825));
500        assert_eq!(Some(173), primary_channel_from_freq(5865));
501        // This is below the range of recognized 2.4GHz channels
502        assert_eq!(None, primary_channel_from_freq(2400));
503        // This is below the range of recognized 5GHz channels
504        assert_eq!(None, primary_channel_from_freq(5000));
505        // This is above the range of recognized channels
506        assert_eq!(None, primary_channel_from_freq(5885));
507    }
508
509    const RX_PRIMARY_CHAN: u8 = 11;
510    const RX_PRIMARY_CHAN_5GHZ: u8 = 36;
511    const HT_PRIMARY_CHAN: u8 = 48;
512
513    #[test]
514    fn test_derive_channel_basic() {
515        let channel = derive_channel(
516            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
517            None,
518            None,
519            None,
520        );
521        assert_eq!(channel, Channel::new(RX_PRIMARY_CHAN, Bandwidth::Cbw20, TwoGhz));
522    }
523
524    #[test]
525    fn test_derive_channel_with_dsss_param() {
526        let channel = derive_channel(
527            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN, band: TwoGhz },
528            Some(6),
529            None,
530            None,
531        );
532        assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz));
533    }
534
535    #[test]
536    fn test_derive_channel_with_ht_20mhz() {
537        let expected_channel = Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw20, FiveGhz);
538
539        let test_params = [
540            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_NONE),
541            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_ABOVE),
542            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_BELOW),
543            (ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_NONE),
544        ];
545
546        for (ht_width, sec_chan_offset) in test_params.iter() {
547            let ht_op = ht_op(HT_PRIMARY_CHAN, *ht_width, *sec_chan_offset);
548            let channel = derive_channel(
549                fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
550                Some(RX_PRIMARY_CHAN_5GHZ),
551                Some(ht_op),
552                None,
553            );
554            assert_eq!(channel, expected_channel);
555        }
556    }
557
558    #[test]
559    fn test_derive_channel_with_ht_40mhz() {
560        let ht_op =
561            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
562        let channel = derive_channel(
563            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
564            Some(RX_PRIMARY_CHAN_5GHZ),
565            Some(ht_op),
566            None,
567        );
568        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40, FiveGhz));
569    }
570
571    #[test]
572    fn test_derive_channel_with_ht_40mhz_below() {
573        let ht_op =
574            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_BELOW);
575        let channel = derive_channel(
576            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
577            Some(RX_PRIMARY_CHAN_5GHZ),
578            Some(ht_op),
579            None,
580        );
581        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw40Below, FiveGhz));
582    }
583
584    #[test]
585    fn test_derive_channel_with_vht_80mhz() {
586        let ht_op =
587            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
588        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 8, 0);
589        let channel = derive_channel(
590            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
591            Some(RX_PRIMARY_CHAN_5GHZ),
592            Some(ht_op),
593            Some(vht_op),
594        );
595        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw80, FiveGhz));
596    }
597
598    #[test]
599    fn test_derive_channel_with_vht_160mhz() {
600        let ht_op =
601            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
602        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 0, 8);
603        let channel = derive_channel(
604            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
605            Some(RX_PRIMARY_CHAN_5GHZ),
606            Some(ht_op),
607            Some(vht_op),
608        );
609        assert_eq!(channel, Channel::new(HT_PRIMARY_CHAN, Bandwidth::Cbw160, FiveGhz));
610    }
611
612    #[test]
613    fn test_derive_channel_with_vht_80plus80mhz() {
614        let ht_op =
615            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
616        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 18, 1);
617        let channel = derive_channel(
618            fidl_ieee80211::ChannelNumber { number: RX_PRIMARY_CHAN_5GHZ, band: FiveGhz },
619            Some(RX_PRIMARY_CHAN_5GHZ),
620            Some(ht_op),
621            Some(vht_op),
622        );
623        assert_eq!(
624            channel,
625            Channel::new(
626                HT_PRIMARY_CHAN,
627                Bandwidth::Cbw80P80 { vht_secondary_80_channel: 1 },
628                FiveGhz
629            )
630        );
631    }
632
633    #[test]
634    fn test_derive_channel_none() {
635        let channel = derive_channel(rx_channel(8, TwoGhz), None, None, None);
636        assert_eq!(channel, Channel::new(8, Bandwidth::Cbw20, TwoGhz));
637    }
638
639    #[test]
640    fn test_derive_channel_no_rx_primary() {
641        let channel = derive_channel(rx_channel(8, TwoGhz), Some(6), None, None);
642        assert_eq!(channel, Channel::new(6, Bandwidth::Cbw20, TwoGhz))
643    }
644
645    fn ht_op(
646        primary_channel: u8,
647        chan_width: ie::StaChanWidth,
648        offset: ie::SecChanOffset,
649    ) -> ie::HtOperation {
650        let ht_op_info =
651            ie::HtOpInfo::new().with_sta_chan_width(chan_width).with_secondary_chan_offset(offset);
652        ie::HtOperation { primary_channel, ht_op_info, basic_ht_mcs_set: ie::SupportedMcsSet(0) }
653    }
654
655    fn vht_op(vht_cbw: ie::VhtChannelBandwidth, seg0: u8, seg1: u8) -> ie::VhtOperation {
656        ie::VhtOperation {
657            vht_cbw,
658            center_freq_seg0: seg0,
659            center_freq_seg1: seg1,
660            basic_mcs_nss: ie::VhtMcsNssMap(0),
661        }
662    }
663}