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_common as fidl_common;
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 Cbw {
27    Cbw20,
28    Cbw40, // Same as Cbw40Above
29    Cbw40Below,
30    Cbw80,
31    Cbw160,
32    Cbw80P80 { secondary80: u8 },
33}
34
35impl Cbw {
36    // TODO(https://fxbug.dev/42164482): Implement `From `instead.
37    pub fn to_fidl(&self) -> (fidl_common::ChannelBandwidth, u8) {
38        match self {
39            Cbw::Cbw20 => (fidl_common::ChannelBandwidth::Cbw20, 0),
40            Cbw::Cbw40 => (fidl_common::ChannelBandwidth::Cbw40, 0),
41            Cbw::Cbw40Below => (fidl_common::ChannelBandwidth::Cbw40Below, 0),
42            Cbw::Cbw80 => (fidl_common::ChannelBandwidth::Cbw80, 0),
43            Cbw::Cbw160 => (fidl_common::ChannelBandwidth::Cbw160, 0),
44            Cbw::Cbw80P80 { secondary80 } => {
45                (fidl_common::ChannelBandwidth::Cbw80P80, *secondary80)
46            }
47        }
48    }
49
50    pub fn from_fidl(
51        fidl_cbw: fidl_common::ChannelBandwidth,
52        fidl_secondary80: u8,
53    ) -> Result<Self, anyhow::Error> {
54        match fidl_cbw {
55            fidl_common::ChannelBandwidth::Cbw20 => Ok(Cbw::Cbw20),
56            fidl_common::ChannelBandwidth::Cbw40 => Ok(Cbw::Cbw40),
57            fidl_common::ChannelBandwidth::Cbw40Below => Ok(Cbw::Cbw40Below),
58            fidl_common::ChannelBandwidth::Cbw80 => Ok(Cbw::Cbw80),
59            fidl_common::ChannelBandwidth::Cbw160 => Ok(Cbw::Cbw160),
60            fidl_common::ChannelBandwidth::Cbw80P80 => {
61                Ok(Cbw::Cbw80P80 { secondary80: fidl_secondary80 })
62            }
63            fidl_common::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    // TODO(porce): Augment with country and band
77    pub primary: u8,
78    pub cbw: Cbw,
79}
80
81// Fuchsia's short CBW notation. Not IEEE standard.
82impl fmt::Display for Cbw {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        match self {
85            Cbw::Cbw20 => write!(f, ""),       // Vanilla plain 20 MHz bandwidth
86            Cbw::Cbw40 => write!(f, "+"),      // SCA, often denoted by "+1"
87            Cbw::Cbw40Below => write!(f, "-"), // SCB, often denoted by "-1",
88            Cbw::Cbw80 => write!(f, "V"),      // VHT 80 MHz (V from VHT)
89            Cbw::Cbw160 => write!(f, "W"),     // VHT 160 MHz (as Wide as V + V ;) )
90            Cbw::Cbw80P80 { secondary80 } => write!(f, "+{}P", secondary80), // VHT 80Plus80 (not often obvious, but P is the first alphabet)
91        }
92    }
93}
94
95impl fmt::Display for Channel {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        write!(f, "{}{}", self.primary, self.cbw)
98    }
99}
100
101impl Channel {
102    pub fn new(primary: u8, cbw: Cbw) -> Self {
103        Channel { primary, cbw }
104    }
105
106    // Weak validity test w.r.t the 2 GHz band primary channel only
107    fn is_primary_2ghz(&self) -> bool {
108        let p = self.primary;
109        p >= 1 && p <= 14
110    }
111
112    // Weak validity test w.r.t the 5 GHz band primary channel only
113    fn is_primary_5ghz(&self) -> bool {
114        let p = self.primary;
115        match p {
116            36..=64 => (p - 36) % 4 == 0,
117            100..=144 => (p - 100) % 4 == 0,
118            149..=165 => (p - 149) % 4 == 0,
119            _ => false,
120        }
121    }
122
123    fn get_band_start_freq(&self) -> Result<MHz, anyhow::Error> {
124        if self.is_primary_2ghz() {
125            Ok(BASE_FREQ_2GHZ)
126        } else if self.is_primary_5ghz() {
127            Ok(BASE_FREQ_5GHZ)
128        } else {
129            return Err(format_err!("cannot get band start freq for channel {}", self));
130        }
131    }
132
133    // Note get_center_chan_idx() is to assist channel validity test.
134    // Return of Ok() does not imply the channel under test is valid.
135    fn get_center_chan_idx(&self) -> Result<u8, anyhow::Error> {
136        if !(self.is_primary_2ghz() || self.is_primary_5ghz()) {
137            return Err(format_err!(
138                "cannot get center channel index for an invalid primary channel {}",
139                self
140            ));
141        }
142
143        let p = self.primary;
144        match self.cbw {
145            Cbw::Cbw20 => Ok(p),
146            Cbw::Cbw40 => Ok(p + 2),
147            Cbw::Cbw40Below => Ok(p - 2),
148            Cbw::Cbw80 | Cbw::Cbw80P80 { .. } => match p {
149                36..=48 => Ok(42),
150                52..=64 => Ok(58),
151                100..=112 => Ok(106),
152                116..=128 => Ok(122),
153                132..=144 => Ok(138),
154                148..=161_ => Ok(155),
155                _ => {
156                    return Err(format_err!(
157                        "cannot get center channel index for invalid channel {}",
158                        self
159                    ))
160                }
161            },
162            Cbw::Cbw160 => {
163                // See IEEE Std 802.11-2016 Table 9-252 and 9-253.
164                // Note CBW160 has only one frequency segment, regardless of
165                // encodings on CCFS0 and CCFS1 in VHT Operation Information IE.
166                match p {
167                    36..=64 => Ok(50),
168                    100..=128 => Ok(114),
169                    _ => {
170                        return Err(format_err!(
171                            "cannot get center channel index for invalid channel {}",
172                            self
173                        ))
174                    }
175                }
176            }
177        }
178    }
179
180    /// Returns the center frequency of the first consecutive frequency segment of the channel
181    /// in MHz if the channel is valid, Err(String) otherwise.
182    pub fn get_center_freq(&self) -> Result<MHz, anyhow::Error> {
183        // IEEE Std 802.11-2016, 21.3.14
184        let start_freq = self.get_band_start_freq()?;
185        let center_chan_idx = self.get_center_chan_idx()?;
186        let spacing: MHz = 5;
187        Ok(start_freq + spacing * center_chan_idx as u16)
188    }
189
190    /// Returns true if the primary channel index, channel bandwidth, and the secondary consecutive
191    /// frequency segment (Cbw80P80 only) are all consistent and meet regulatory requirements of
192    /// the USA. TODO(https://fxbug.dev/42104247): Other countries.
193    pub fn is_valid_in_us(&self) -> bool {
194        if self.is_primary_2ghz() {
195            self.is_valid_2ghz_in_us()
196        } else if self.is_primary_5ghz() {
197            self.is_valid_5ghz_in_us()
198        } else {
199            false
200        }
201    }
202
203    fn is_valid_2ghz_in_us(&self) -> bool {
204        if !self.is_primary_2ghz() {
205            return false;
206        }
207        let p = self.primary;
208        match self.cbw {
209            Cbw::Cbw20 => p <= 11,
210            Cbw::Cbw40 => p <= 7,
211            Cbw::Cbw40Below => p >= 5,
212            _ => false,
213        }
214    }
215
216    fn is_valid_5ghz_in_us(&self) -> bool {
217        if !self.is_primary_5ghz() {
218            return false;
219        }
220        let p = self.primary;
221        match self.cbw {
222            Cbw::Cbw20 => true,
223            Cbw::Cbw40 => p != 165 && (p % 8) == (if p <= 144 { 4 } else { 5 }),
224            Cbw::Cbw40Below => p != 165 && (p % 8) == (if p <= 144 { 0 } else { 1 }),
225            Cbw::Cbw80 => p != 165,
226            Cbw::Cbw160 => p < 132,
227            Cbw::Cbw80P80 { secondary80 } => {
228                if p == 165 {
229                    return false;
230                }
231                let valid_secondary80: [u8; 6] = [42, 58, 106, 122, 138, 155];
232                if !valid_secondary80.contains(&secondary80) {
233                    return false;
234                }
235                let ccfs0 = match self.get_center_chan_idx() {
236                    Ok(v) => v,
237                    Err(_) => return false,
238                };
239                let ccfs1 = secondary80;
240                let gap = (ccfs0 as i16 - ccfs1 as i16).abs();
241                gap > 16
242            }
243        }
244    }
245
246    /// Returns true if the channel is 2GHz. Does not perform validity checks.
247    pub fn is_2ghz(&self) -> bool {
248        self.is_primary_2ghz()
249    }
250
251    /// Returns true if the channel is 5GHz. Does not perform validity checks.
252    pub fn is_5ghz(&self) -> bool {
253        self.is_primary_5ghz()
254    }
255
256    // TODO(https://fxbug.dev/335283785): Remove or explain unused code.
257    #[allow(dead_code)]
258    fn is_unii1(&self) -> bool {
259        let p = self.primary;
260        p >= 32 && p <= 50
261    }
262
263    fn is_unii2a(&self) -> bool {
264        let p = self.primary;
265        // Note the overlap with U-NII-1
266        p >= 50 && p <= 68
267    }
268
269    fn is_unii2c(&self) -> bool {
270        let p = self.primary;
271        p >= 96 && p <= 144
272    }
273
274    // TODO(https://fxbug.dev/335283785): Remove or explain unused code.
275    #[allow(dead_code)]
276    fn is_unii3(&self) -> bool {
277        let p = self.primary;
278        // Note the overlap with U-NII-2C
279        p >= 138 && p <= 165
280    }
281
282    pub fn is_dfs(&self) -> bool {
283        self.is_unii2a() || self.is_unii2c()
284    }
285}
286
287impl From<Channel> for fidl_common::WlanChannel {
288    fn from(channel: Channel) -> fidl_common::WlanChannel {
289        fidl_common::WlanChannel::from(&channel)
290    }
291}
292
293impl From<&Channel> for fidl_common::WlanChannel {
294    fn from(channel: &Channel) -> fidl_common::WlanChannel {
295        let (cbw, secondary80) = channel.cbw.to_fidl();
296        fidl_common::WlanChannel { primary: channel.primary, cbw, secondary80 }
297    }
298}
299
300impl TryFrom<fidl_common::WlanChannel> for Channel {
301    type Error = anyhow::Error;
302    fn try_from(fidl_channel: fidl_common::WlanChannel) -> Result<Channel, Self::Error> {
303        Channel::try_from(&fidl_channel)
304    }
305}
306
307impl TryFrom<&fidl_common::WlanChannel> for Channel {
308    type Error = anyhow::Error;
309
310    fn try_from(fidl_channel: &fidl_common::WlanChannel) -> Result<Channel, Self::Error> {
311        Ok(Channel {
312            primary: fidl_channel.primary,
313            cbw: Cbw::from_fidl(fidl_channel.cbw, fidl_channel.secondary80)?,
314        })
315    }
316}
317
318/// Derive channel given DSSS param set, HT operation, and VHT operation IEs from
319/// beacon or probe response, and the primary channel from which such frame is
320/// received on.
321///
322/// Primary channel is extracted from HT op, DSSS param set, or `rx_primary_channel`,
323/// in descending priority.
324pub fn derive_channel(
325    rx_primary_channel: u8,
326    dsss_channel: Option<u8>,
327    ht_op: Option<ie::HtOperation>,
328    vht_op: Option<ie::VhtOperation>,
329) -> fidl_common::WlanChannel {
330    let primary = ht_op
331        .as_ref()
332        .map(|ht_op| ht_op.primary_channel)
333        .or(dsss_channel)
334        .unwrap_or(rx_primary_channel);
335
336    let ht_op_cbw = ht_op.map(|ht_op| ht_op.ht_op_info.sta_chan_width());
337    let vht_cbw_and_segs =
338        vht_op.map(|vht_op| (vht_op.vht_cbw, vht_op.center_freq_seg0, vht_op.center_freq_seg1));
339
340    let (cbw, secondary80) = match ht_op_cbw {
341        // Inspect vht/ht op parameters to determine the channel width.
342        Some(ie::StaChanWidth::ANY) => {
343            // Safe to unwrap `ht_op` because `ht_op_cbw` is only Some(_) if `ht_op` has a value.
344            let sec_chan_offset = ht_op.unwrap().ht_op_info.secondary_chan_offset();
345            derive_wide_channel_bandwidth(vht_cbw_and_segs, sec_chan_offset)
346        }
347        // Default to Cbw20 if HT CBW field is set to 0 or not present.
348        _ => Cbw::Cbw20,
349    }
350    .to_fidl();
351
352    fidl_common::WlanChannel { primary, cbw, secondary80 }
353}
354
355/// Derive a CBW for a primary channel or channel switch.
356/// VHT parameter derivation is defined identically by:
357///     IEEE Std 802.11-2016 9.4.2.159 Table 9-252 for channel switching
358///     IEEE Std 802.11-2016 11.40.1 Table 11-24 for VHT operation
359/// SecChanOffset is defined identially by:
360///     IEEE Std 802.11-2016 9.4.2.20 for channel switching
361///     IEEE Std 802.11-2016 9.4.2.57 Table 9-168 for HT operation
362pub fn derive_wide_channel_bandwidth(
363    vht_cbw_and_segs: Option<(ie::VhtChannelBandwidth, u8, u8)>,
364    sec_chan_offset: ie::SecChanOffset,
365) -> Cbw {
366    use ie::VhtChannelBandwidth as Vcb;
367    match vht_cbw_and_segs {
368        Some((Vcb::CBW_80_160_80P80, _, 0)) => Cbw::Cbw80,
369        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) == 8 => Cbw::Cbw160,
370        Some((Vcb::CBW_80_160_80P80, seg0, seg1)) if abs_sub(seg0, seg1) > 16 => {
371            // See IEEE 802.11-2016, Table 9-252, about channel center frequency segment 1
372            Cbw::Cbw80P80 { secondary80: seg1 }
373        }
374        // Use HT CBW if
375        // - VHT op is not present,
376        // - VHT op has deprecated parameters sets, or
377        // - VHT CBW field is set to 0
378        _ => match sec_chan_offset {
379            ie::SecChanOffset::SECONDARY_ABOVE => Cbw::Cbw40,
380            ie::SecChanOffset::SECONDARY_BELOW => Cbw::Cbw40Below,
381            ie::SecChanOffset::SECONDARY_NONE | _ => Cbw::Cbw20,
382        },
383    }
384}
385
386fn abs_sub(v1: u8, v2: u8) -> u8 {
387    if v2 >= v1 {
388        v2 - v1
389    } else {
390        v1 - v2
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    #[test]
399    fn fmt_display() {
400        let mut c = Channel::new(100, Cbw::Cbw40);
401        assert_eq!(format!("{}", c), "100+");
402        c.cbw = Cbw::Cbw160;
403        assert_eq!(format!("{}", c), "100W");
404        c.cbw = Cbw::Cbw80P80 { secondary80: 200 };
405        assert_eq!(format!("{}", c), "100+200P");
406    }
407
408    #[test]
409    fn test_is_primary_2ghz_or_5ghz() {
410        // Note Cbw is ignored in this test.
411        assert!(Channel::new(1, Cbw::Cbw160).is_primary_2ghz());
412        assert!(!Channel::new(1, Cbw::Cbw160).is_primary_5ghz());
413
414        assert!(Channel::new(12, Cbw::Cbw160).is_primary_2ghz());
415        assert!(!Channel::new(12, Cbw::Cbw160).is_primary_5ghz());
416
417        assert!(!Channel::new(36, Cbw::Cbw160).is_primary_2ghz());
418        assert!(Channel::new(36, Cbw::Cbw160).is_primary_5ghz());
419
420        assert!(!Channel::new(37, Cbw::Cbw160).is_primary_2ghz());
421        assert!(!Channel::new(37, Cbw::Cbw160).is_primary_5ghz());
422
423        assert!(!Channel::new(165, Cbw::Cbw160).is_primary_2ghz());
424        assert!(Channel::new(165, Cbw::Cbw160).is_primary_5ghz());
425
426        assert!(!Channel::new(166, Cbw::Cbw160).is_primary_2ghz());
427        assert!(!Channel::new(166, Cbw::Cbw160).is_primary_5ghz());
428    }
429
430    #[test]
431    fn test_band_start_freq() {
432        assert_eq!(BASE_FREQ_2GHZ, Channel::new(1, Cbw::Cbw20).get_band_start_freq().unwrap());
433        assert_eq!(BASE_FREQ_5GHZ, Channel::new(100, Cbw::Cbw20).get_band_start_freq().unwrap());
434        assert!(Channel::new(15, Cbw::Cbw20).get_band_start_freq().is_err());
435        assert!(Channel::new(200, Cbw::Cbw20).get_band_start_freq().is_err());
436    }
437
438    #[test]
439    fn test_get_center_chan_idx() {
440        assert!(Channel::new(1, Cbw::Cbw80).get_center_chan_idx().is_err());
441        assert_eq!(9, Channel::new(11, Cbw::Cbw40Below).get_center_chan_idx().unwrap());
442        assert_eq!(8, Channel::new(6, Cbw::Cbw40).get_center_chan_idx().unwrap());
443        assert_eq!(36, Channel::new(36, Cbw::Cbw20).get_center_chan_idx().unwrap());
444        assert_eq!(38, Channel::new(36, Cbw::Cbw40).get_center_chan_idx().unwrap());
445        assert_eq!(42, Channel::new(36, Cbw::Cbw80).get_center_chan_idx().unwrap());
446        assert_eq!(50, Channel::new(36, Cbw::Cbw160).get_center_chan_idx().unwrap());
447        assert_eq!(
448            42,
449            Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).get_center_chan_idx().unwrap()
450        );
451    }
452
453    #[test]
454    fn test_get_center_freq() {
455        assert_eq!(2412 as MHz, Channel::new(1, Cbw::Cbw20).get_center_freq().unwrap());
456        assert_eq!(2437 as MHz, Channel::new(6, Cbw::Cbw20).get_center_freq().unwrap());
457        assert_eq!(2447 as MHz, Channel::new(6, Cbw::Cbw40).get_center_freq().unwrap());
458        assert_eq!(2427 as MHz, Channel::new(6, Cbw::Cbw40Below).get_center_freq().unwrap());
459        assert_eq!(5180 as MHz, Channel::new(36, Cbw::Cbw20).get_center_freq().unwrap());
460        assert_eq!(5190 as MHz, Channel::new(36, Cbw::Cbw40).get_center_freq().unwrap());
461        assert_eq!(5210 as MHz, Channel::new(36, Cbw::Cbw80).get_center_freq().unwrap());
462        assert_eq!(5250 as MHz, Channel::new(36, Cbw::Cbw160).get_center_freq().unwrap());
463        assert_eq!(
464            5210 as MHz,
465            Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).get_center_freq().unwrap()
466        );
467    }
468
469    #[test]
470    fn test_valid_us_combo() {
471        assert!(Channel::new(1, Cbw::Cbw20).is_valid_in_us());
472        assert!(Channel::new(1, Cbw::Cbw40).is_valid_in_us());
473        assert!(Channel::new(5, Cbw::Cbw40Below).is_valid_in_us());
474        assert!(Channel::new(6, Cbw::Cbw20).is_valid_in_us());
475        assert!(Channel::new(6, Cbw::Cbw40).is_valid_in_us());
476        assert!(Channel::new(6, Cbw::Cbw40Below).is_valid_in_us());
477        assert!(Channel::new(7, Cbw::Cbw40).is_valid_in_us());
478        assert!(Channel::new(11, Cbw::Cbw20).is_valid_in_us());
479        assert!(Channel::new(11, Cbw::Cbw40Below).is_valid_in_us());
480
481        assert!(Channel::new(36, Cbw::Cbw20).is_valid_in_us());
482        assert!(Channel::new(36, Cbw::Cbw40).is_valid_in_us());
483        assert!(Channel::new(36, Cbw::Cbw160).is_valid_in_us());
484        assert!(Channel::new(40, Cbw::Cbw20).is_valid_in_us());
485        assert!(Channel::new(40, Cbw::Cbw40Below).is_valid_in_us());
486        assert!(Channel::new(40, Cbw::Cbw160).is_valid_in_us());
487        assert!(Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).is_valid_in_us());
488        assert!(Channel::new(40, Cbw::Cbw80P80 { secondary80: 155 }).is_valid_in_us());
489        assert!(Channel::new(161, Cbw::Cbw80P80 { secondary80: 42 }).is_valid_in_us());
490    }
491
492    #[test]
493    fn test_invalid_us_combo() {
494        assert!(!Channel::new(1, Cbw::Cbw40Below).is_valid_in_us());
495        assert!(!Channel::new(4, Cbw::Cbw40Below).is_valid_in_us());
496        assert!(!Channel::new(8, Cbw::Cbw40).is_valid_in_us());
497        assert!(!Channel::new(11, Cbw::Cbw40).is_valid_in_us());
498        assert!(!Channel::new(6, Cbw::Cbw80).is_valid_in_us());
499        assert!(!Channel::new(6, Cbw::Cbw160).is_valid_in_us());
500        assert!(!Channel::new(6, Cbw::Cbw80P80 { secondary80: 155 }).is_valid_in_us());
501
502        assert!(!Channel::new(36, Cbw::Cbw40Below).is_valid_in_us());
503        assert!(!Channel::new(36, Cbw::Cbw80P80 { secondary80: 58 }).is_valid_in_us());
504        assert!(!Channel::new(40, Cbw::Cbw40).is_valid_in_us());
505        assert!(!Channel::new(40, Cbw::Cbw80P80 { secondary80: 42 }).is_valid_in_us());
506
507        assert!(!Channel::new(165, Cbw::Cbw80).is_valid_in_us());
508        assert!(!Channel::new(165, Cbw::Cbw80P80 { secondary80: 42 }).is_valid_in_us());
509    }
510
511    #[test]
512    fn test_is_2ghz_or_5ghz() {
513        assert!(Channel::new(1, Cbw::Cbw20).is_2ghz());
514        assert!(!Channel::new(1, Cbw::Cbw20).is_5ghz());
515        assert!(Channel::new(13, Cbw::Cbw20).is_2ghz());
516        assert!(!Channel::new(13, Cbw::Cbw20).is_5ghz());
517        assert!(Channel::new(36, Cbw::Cbw20).is_5ghz());
518        assert!(!Channel::new(36, Cbw::Cbw20).is_2ghz());
519    }
520
521    #[test]
522    fn test_is_dfs() {
523        assert!(!Channel::new(1, Cbw::Cbw20).is_dfs());
524        assert!(!Channel::new(36, Cbw::Cbw20).is_dfs());
525        assert!(Channel::new(50, Cbw::Cbw20).is_dfs());
526        assert!(Channel::new(144, Cbw::Cbw20).is_dfs());
527        assert!(!Channel::new(149, Cbw::Cbw20).is_dfs());
528    }
529
530    #[test]
531    fn test_convert_fidl_channel() {
532        let mut f = fidl_common::WlanChannel::from(Channel::new(1, Cbw::Cbw20));
533        assert!(
534            f.primary == 1 && f.cbw == fidl_common::ChannelBandwidth::Cbw20 && f.secondary80 == 0
535        );
536
537        f = Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).into();
538        assert!(
539            f.primary == 36
540                && f.cbw == fidl_common::ChannelBandwidth::Cbw80P80
541                && f.secondary80 == 155
542        );
543
544        let mut c = Channel::try_from(fidl_common::WlanChannel {
545            primary: 11,
546            cbw: fidl_common::ChannelBandwidth::Cbw40Below,
547            secondary80: 123,
548        })
549        .unwrap();
550        assert!(c.primary == 11 && c.cbw == Cbw::Cbw40Below);
551        c = fidl_common::WlanChannel {
552            primary: 149,
553            cbw: fidl_common::ChannelBandwidth::Cbw80P80,
554            secondary80: 42,
555        }
556        .try_into()
557        .unwrap();
558        assert!(c.primary == 149 && c.cbw == Cbw::Cbw80P80 { secondary80: 42 });
559
560        let r = Channel::try_from(fidl_common::WlanChannel {
561            primary: 11,
562            cbw: fidl_common::ChannelBandwidth::unknown(),
563            secondary80: 123,
564        });
565        assert!(r.is_err());
566    }
567
568    const RX_PRIMARY_CHAN: u8 = 11;
569    const HT_PRIMARY_CHAN: u8 = 48;
570
571    #[test]
572    fn test_derive_channel_basic() {
573        let channel = derive_channel(RX_PRIMARY_CHAN, None, None, None);
574        assert_eq!(
575            channel,
576            fidl_common::WlanChannel {
577                primary: RX_PRIMARY_CHAN,
578                cbw: fidl_common::ChannelBandwidth::Cbw20,
579                secondary80: 0,
580            }
581        );
582    }
583
584    #[test]
585    fn test_derive_channel_with_dsss_param() {
586        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), None, None);
587        assert_eq!(
588            channel,
589            fidl_common::WlanChannel {
590                primary: 6,
591                cbw: fidl_common::ChannelBandwidth::Cbw20,
592                secondary80: 0
593            }
594        );
595    }
596
597    #[test]
598    fn test_derive_channel_with_ht_20mhz() {
599        let expected_channel = fidl_common::WlanChannel {
600            primary: HT_PRIMARY_CHAN,
601            cbw: fidl_common::ChannelBandwidth::Cbw20,
602            secondary80: 0,
603        };
604
605        let test_params = [
606            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_NONE),
607            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_ABOVE),
608            (ie::StaChanWidth::TWENTY_MHZ, ie::SecChanOffset::SECONDARY_BELOW),
609            (ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_NONE),
610        ];
611
612        for (ht_width, sec_chan_offset) in test_params.iter() {
613            let ht_op = ht_op(HT_PRIMARY_CHAN, *ht_width, *sec_chan_offset);
614            let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), None);
615            assert_eq!(channel, expected_channel);
616        }
617    }
618
619    #[test]
620    fn test_derive_channel_with_ht_40mhz() {
621        let ht_op =
622            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
623        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), None);
624        assert_eq!(
625            channel,
626            fidl_common::WlanChannel {
627                primary: HT_PRIMARY_CHAN,
628                cbw: fidl_common::ChannelBandwidth::Cbw40,
629                secondary80: 0,
630            }
631        );
632    }
633
634    #[test]
635    fn test_derive_channel_with_ht_40mhz_below() {
636        let ht_op =
637            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_BELOW);
638        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), None);
639        assert_eq!(
640            channel,
641            fidl_common::WlanChannel {
642                primary: HT_PRIMARY_CHAN,
643                cbw: fidl_common::ChannelBandwidth::Cbw40Below,
644                secondary80: 0,
645            }
646        );
647    }
648
649    #[test]
650    fn test_derive_channel_with_vht_80mhz() {
651        let ht_op =
652            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
653        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 8, 0);
654        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), Some(vht_op));
655        assert_eq!(
656            channel,
657            fidl_common::WlanChannel {
658                primary: HT_PRIMARY_CHAN,
659                cbw: fidl_common::ChannelBandwidth::Cbw80,
660                secondary80: 0,
661            }
662        );
663    }
664
665    #[test]
666    fn test_derive_channel_with_vht_160mhz() {
667        let ht_op =
668            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
669        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 0, 8);
670        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), Some(vht_op));
671        assert_eq!(
672            channel,
673            fidl_common::WlanChannel {
674                primary: HT_PRIMARY_CHAN,
675                cbw: fidl_common::ChannelBandwidth::Cbw160,
676                secondary80: 0,
677            }
678        );
679    }
680
681    #[test]
682    fn test_derive_channel_with_vht_80plus80mhz() {
683        let ht_op =
684            ht_op(HT_PRIMARY_CHAN, ie::StaChanWidth::ANY, ie::SecChanOffset::SECONDARY_ABOVE);
685        let vht_op = vht_op(ie::VhtChannelBandwidth::CBW_80_160_80P80, 18, 1);
686        let channel = derive_channel(RX_PRIMARY_CHAN, Some(6), Some(ht_op), Some(vht_op));
687        assert_eq!(
688            channel,
689            fidl_common::WlanChannel {
690                primary: HT_PRIMARY_CHAN,
691                cbw: fidl_common::ChannelBandwidth::Cbw80P80,
692                secondary80: 1,
693            }
694        );
695    }
696
697    #[test]
698    fn test_derive_channel_none() {
699        let channel = derive_channel(8, None, None, None);
700        assert_eq!(
701            channel,
702            fidl_common::WlanChannel {
703                primary: 8,
704                cbw: fidl_common::ChannelBandwidth::Cbw20,
705                secondary80: 0,
706            }
707        );
708    }
709
710    #[test]
711    fn test_derive_channel_no_rx_primary() {
712        let channel = derive_channel(8, Some(6), None, None);
713        assert_eq!(
714            channel,
715            fidl_common::WlanChannel {
716                primary: 6,
717                cbw: fidl_common::ChannelBandwidth::Cbw20,
718                secondary80: 0,
719            }
720        )
721    }
722
723    fn ht_op(
724        primary_channel: u8,
725        chan_width: ie::StaChanWidth,
726        offset: ie::SecChanOffset,
727    ) -> ie::HtOperation {
728        let ht_op_info =
729            ie::HtOpInfo::new().with_sta_chan_width(chan_width).with_secondary_chan_offset(offset);
730        ie::HtOperation { primary_channel, ht_op_info, basic_ht_mcs_set: ie::SupportedMcsSet(0) }
731    }
732
733    fn vht_op(vht_cbw: ie::VhtChannelBandwidth, seg0: u8, seg1: u8) -> ie::VhtOperation {
734        ie::VhtOperation {
735            vht_cbw,
736            center_freq_seg0: seg0,
737            center_freq_seg1: seg1,
738            basic_mcs_nss: ie::VhtMcsNssMap(0),
739        }
740    }
741}