blob: 137b7f1da3ffa53f20883b96515c0738469f21e6 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(dead_code)]
use failure::{self, bail};
use fidl_fuchsia_wlan_common as fidl_common;
use std::fmt;
// IEEE Std 802.11-2016, Annex E
// Note the distinction of index for primary20 and index for center frequency.
// Fuchsia OS minimizes the use of the notion of center frequency,
// with following exceptions:
// - Cbw80P80's secondary frequency segment
// - Frequency conversion at device drivers
pub type MHz = u16;
pub const BASE_FREQ_2GHZ: MHz = 2407;
pub const BASE_FREQ_5GHZ: MHz = 5000;
pub const INVALID_CHAN_IDX: u8 = 0;
/// Channel bandwidth. Cbw80P80 requires the specification of
/// channel index corresponding to the center frequency
/// of the secondary consecutive frequency segment.
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum Cbw {
Cbw20,
Cbw40, // Same as Cbw40Above
Cbw40Below,
Cbw80,
Cbw160,
Cbw80P80 { secondary80: u8 },
}
impl Cbw {
pub fn to_fidl(&self) -> (fidl_common::Cbw, u8) {
match self {
Cbw::Cbw20 => (fidl_common::Cbw::Cbw20, 0),
Cbw::Cbw40 => (fidl_common::Cbw::Cbw40, 0),
Cbw::Cbw40Below => (fidl_common::Cbw::Cbw40Below, 0),
Cbw::Cbw80 => (fidl_common::Cbw::Cbw80, 0),
Cbw::Cbw160 => (fidl_common::Cbw::Cbw160, 0),
Cbw::Cbw80P80 { secondary80 } => (fidl_common::Cbw::Cbw80P80, *secondary80),
}
}
pub fn from_fidl(fidl_cbw: fidl_common::Cbw, fidl_secondary80: u8) -> Self {
match fidl_cbw {
fidl_common::Cbw::Cbw20 => Cbw::Cbw20,
fidl_common::Cbw::Cbw40 => Cbw::Cbw40,
fidl_common::Cbw::Cbw40Below => Cbw::Cbw40Below,
fidl_common::Cbw::Cbw80 => Cbw::Cbw80,
fidl_common::Cbw::Cbw160 => Cbw::Cbw160,
fidl_common::Cbw::Cbw80P80 => Cbw::Cbw80P80 { secondary80: fidl_secondary80 },
}
}
}
/// A short list of IEEE WLAN PHY.
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum Phy {
Hr, // IEEE 802.11b, used for DSSS, HR/DSSS, ERP-DSSS/CCK
Erp, // IEEE 802.11a/g, used for ERP-OFDM
Ht, // IEEE 802.11n
Vht, // IEEE 802.11ac
Hew, // IEEE 802.11ax
}
impl Phy {
pub fn to_fidl(&self) -> fidl_common::Phy {
match self {
Phy::Hr => fidl_common::Phy::Hr,
Phy::Erp => fidl_common::Phy::Erp,
Phy::Ht => fidl_common::Phy::Ht,
Phy::Vht => fidl_common::Phy::Vht,
Phy::Hew => fidl_common::Phy::Hew,
}
}
pub fn from_fidl(phy: fidl_common::Phy) -> Self {
match phy {
fidl_common::Phy::Hr => Phy::Hr,
fidl_common::Phy::Erp => Phy::Erp,
fidl_common::Phy::Ht => Phy::Ht,
fidl_common::Phy::Vht => Phy::Vht,
fidl_common::Phy::Hew => Phy::Hew,
}
}
}
/// A Channel defines the frequency spectrum to be used for radio synchronization.
/// See for sister definitions in FIDL and C/C++
/// - //garnet/lib/wlan/protocol/wlan/protocol/info.h |struct wlan_channel_t|
/// - //garnet/public/fidl/fuchsia.wlan.mlme/wlan_mlme.fidl |struct WlanChan|
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct Channel {
// TODO(porce): Augment with country and band
pub primary: u8,
pub cbw: Cbw,
}
// Fuchsia's short CBW notation. Not IEEE standard.
impl fmt::Display for Cbw {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Cbw::Cbw20 => write!(f, ""), // Vanilla plain 20 MHz bandwidth
Cbw::Cbw40 => write!(f, "+"), // SCA, often denoted by "+1"
Cbw::Cbw40Below => write!(f, "-"), // SCB, often denoted by "-1",
Cbw::Cbw80 => write!(f, "V"), // VHT 80 MHz (V from VHT)
Cbw::Cbw160 => write!(f, "W"), // VHT 160 MHz (as Wide as V + V ;) )
Cbw::Cbw80P80 { secondary80 } => write!(f, "+{}P", secondary80), // VHT 80Plus80 (not often obvious, but P is the first alphabet)
}
}
}
impl fmt::Display for Channel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.primary, self.cbw)
}
}
impl Channel {
pub fn new(primary: u8, cbw: Cbw) -> Self {
Channel { primary, cbw }
}
// Weak validity test w.r.t the 2 GHz band primary channel only
fn is_primary_2ghz(&self) -> bool {
let p = self.primary;
p >= 1 && p <= 11
}
// Weak validity test w.r.t the 5 GHz band primary channel only
fn is_primary_5ghz(&self) -> bool {
let p = self.primary;
match p {
36..=64 => (p - 36) % 4 == 0,
100..=144 => (p - 100) % 4 == 0,
149..=165 => (p - 149) % 4 == 0,
_ => false,
}
}
// Weak validity test w.r.t the primary channel only in any band.
fn is_primary_valid(&self) -> bool {
self.is_primary_2ghz() || self.is_primary_5ghz()
}
fn get_band_start_freq(&self) -> Result<MHz, failure::Error> {
if self.is_primary_2ghz() {
Ok(BASE_FREQ_2GHZ)
} else if self.is_primary_5ghz() {
Ok(BASE_FREQ_5GHZ)
} else {
bail!("cannot get band start freq for channel {}", self)
}
}
// Note get_center_chan_idx() is to assist channel validity test.
// Return of Ok() does not imply the channel under test is valid.
fn get_center_chan_idx(&self) -> Result<u8, failure::Error> {
if !self.is_primary_valid() {
bail!("cannot get center channel index for an invalid primary channel {}", self);
}
let p = self.primary;
match self.cbw {
Cbw::Cbw20 => Ok(p),
Cbw::Cbw40 => Ok(p + 2),
Cbw::Cbw40Below => Ok(p - 2),
Cbw::Cbw80 | Cbw::Cbw80P80 { .. } => match p {
36..=48 => Ok(42),
52..=64 => Ok(58),
100..=112 => Ok(106),
116..=128 => Ok(122),
132..=144 => Ok(138),
148..=161_ => Ok(155),
_ => bail!("cannot get center channel index for invalid channel {}", self),
},
Cbw::Cbw160 => {
// See IEEE Std 802.11-2016 Table 9-252 and 9-253.
// Note CBW160 has only one frequency segment, regardless of
// encodings on CCFS0 and CCFS1 in VHT Operation Information IE.
match p {
36..=64 => Ok(50),
100..=128 => Ok(114),
_ => bail!("cannot get center channel index for invalid channel {}", self),
}
}
}
}
/// Returns the center frequency of the first consecutive frequency segment of the channel
/// in MHz if the channel is valid, Err(String) otherwise.
pub fn get_center_freq(&self) -> Result<MHz, failure::Error> {
// IEEE Std 802.11-2016, 21.3.14
let start_freq = self.get_band_start_freq()?;
let center_chan_idx = self.get_center_chan_idx()?;
let spacing: MHz = 5;
Ok(start_freq + spacing * center_chan_idx as u16)
}
/// Returns true if the primary channel index, channel bandwidth, and the secondary consecutive
/// frequency segment (Cbw80P80 only) are all consistent and meet regulatory requirements of
/// the USA. TODO(WLAN-870): Other countries.
pub fn is_valid(&self) -> bool {
if self.is_primary_2ghz() {
self.is_valid_2ghz()
} else if self.is_primary_5ghz() {
self.is_valid_5ghz()
} else {
false
}
}
fn is_valid_2ghz(&self) -> bool {
if !self.is_primary_2ghz() {
return false;
}
let p = self.primary;
match self.cbw {
Cbw::Cbw20 => true,
Cbw::Cbw40 => p <= 7,
Cbw::Cbw40Below => p >= 5,
_ => false,
}
}
fn is_valid_5ghz(&self) -> bool {
if !self.is_primary_5ghz() {
return false;
}
let p = self.primary;
match self.cbw {
Cbw::Cbw20 => true,
Cbw::Cbw40 => p != 165 && (p % 8) == (if p <= 144 { 4 } else { 5 }),
Cbw::Cbw40Below => p != 165 && (p % 8) == (if p <= 144 { 0 } else { 1 }),
Cbw::Cbw80 => p != 165,
Cbw::Cbw160 => p < 132,
Cbw::Cbw80P80 { secondary80 } => {
if p == 165 {
return false;
}
let valid_secondary80: [u8; 6] = [42, 58, 106, 122, 138, 155];
if !valid_secondary80.contains(&secondary80) {
return false;
}
let ccfs0 = match self.get_center_chan_idx() {
Ok(v) => v,
Err(_) => return false,
};
let ccfs1 = secondary80;
let gap = (ccfs0 as i16 - ccfs1 as i16).abs();
gap > 16
}
}
}
pub fn is_2ghz(&self) -> bool {
self.is_valid_2ghz()
}
pub fn is_5ghz(&self) -> bool {
self.is_valid_5ghz()
}
fn is_unii1(&self) -> bool {
let p = self.primary;
p >= 32 && p <= 50
}
fn is_unii2a(&self) -> bool {
let p = self.primary;
// Note the overlap with U-NII-1
p >= 50 && p <= 68
}
fn is_unii2c(&self) -> bool {
let p = self.primary;
p >= 96 && p <= 144
}
fn is_unii3(&self) -> bool {
let p = self.primary;
// Note the overlap with U-NII-2C
p >= 138 && p <= 165
}
pub fn is_dfs(&self) -> bool {
self.is_unii2a() || self.is_unii2c()
}
pub fn to_fidl(&self) -> fidl_common::WlanChan {
let (cbw, secondary80) = self.cbw.to_fidl();
fidl_common::WlanChan { primary: self.primary, cbw, secondary80 }
}
pub fn from_fidl(c: fidl_common::WlanChan) -> Self {
Channel { primary: c.primary, cbw: Cbw::from_fidl(c.cbw, c.secondary80) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt_display() {
let mut c = Channel::new(100, Cbw::Cbw40);
assert_eq!(format!("{}", c), "100+");
c.cbw = Cbw::Cbw160;
assert_eq!(format!("{}", c), "100W");
c.cbw = Cbw::Cbw80P80 { secondary80: 200 };
assert_eq!(format!("{}", c), "100+200P");
}
#[test]
fn test_is_primary_valid() {
// Note Cbw is ignored in this test.
assert!(Channel::new(1, Cbw::Cbw160).is_primary_valid());
assert!(!Channel::new(12, Cbw::Cbw160).is_primary_valid());
assert!(Channel::new(36, Cbw::Cbw160).is_primary_valid());
assert!(!Channel::new(37, Cbw::Cbw160).is_primary_valid());
assert!(Channel::new(165, Cbw::Cbw160).is_primary_valid());
assert!(!Channel::new(166, Cbw::Cbw160).is_primary_valid());
assert!(Channel::new(1, Cbw::Cbw160).is_primary_2ghz());
assert!(!Channel::new(1, Cbw::Cbw160).is_primary_5ghz());
assert!(!Channel::new(36, Cbw::Cbw160).is_primary_2ghz());
assert!(Channel::new(36, Cbw::Cbw160).is_primary_5ghz());
}
#[test]
fn test_band_start_freq() {
assert_eq!(BASE_FREQ_2GHZ, Channel::new(1, Cbw::Cbw20).get_band_start_freq().unwrap());
assert_eq!(BASE_FREQ_5GHZ, Channel::new(100, Cbw::Cbw20).get_band_start_freq().unwrap());
assert!(Channel::new(15, Cbw::Cbw20).get_band_start_freq().is_err());
assert!(Channel::new(200, Cbw::Cbw20).get_band_start_freq().is_err());
}
#[test]
fn test_get_center_chan_idx() {
assert!(Channel::new(1, Cbw::Cbw80).get_center_chan_idx().is_err());
assert_eq!(9, Channel::new(11, Cbw::Cbw40Below).get_center_chan_idx().unwrap());
assert_eq!(8, Channel::new(6, Cbw::Cbw40).get_center_chan_idx().unwrap());
assert_eq!(36, Channel::new(36, Cbw::Cbw20).get_center_chan_idx().unwrap());
assert_eq!(38, Channel::new(36, Cbw::Cbw40).get_center_chan_idx().unwrap());
assert_eq!(42, Channel::new(36, Cbw::Cbw80).get_center_chan_idx().unwrap());
assert_eq!(50, Channel::new(36, Cbw::Cbw160).get_center_chan_idx().unwrap());
assert_eq!(
42,
Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).get_center_chan_idx().unwrap()
);
}
#[test]
fn test_get_center_freq() {
assert_eq!(2412 as MHz, Channel::new(1, Cbw::Cbw20).get_center_freq().unwrap());
assert_eq!(2437 as MHz, Channel::new(6, Cbw::Cbw20).get_center_freq().unwrap());
assert_eq!(2447 as MHz, Channel::new(6, Cbw::Cbw40).get_center_freq().unwrap());
assert_eq!(2427 as MHz, Channel::new(6, Cbw::Cbw40Below).get_center_freq().unwrap());
assert_eq!(5180 as MHz, Channel::new(36, Cbw::Cbw20).get_center_freq().unwrap());
assert_eq!(5190 as MHz, Channel::new(36, Cbw::Cbw40).get_center_freq().unwrap());
assert_eq!(5210 as MHz, Channel::new(36, Cbw::Cbw80).get_center_freq().unwrap());
assert_eq!(5250 as MHz, Channel::new(36, Cbw::Cbw160).get_center_freq().unwrap());
assert_eq!(
5210 as MHz,
Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).get_center_freq().unwrap()
);
}
#[test]
fn test_valid_combo() {
assert!(Channel::new(1, Cbw::Cbw20).is_valid());
assert!(Channel::new(1, Cbw::Cbw40).is_valid());
assert!(Channel::new(5, Cbw::Cbw40Below).is_valid());
assert!(Channel::new(6, Cbw::Cbw20).is_valid());
assert!(Channel::new(6, Cbw::Cbw40).is_valid());
assert!(Channel::new(6, Cbw::Cbw40Below).is_valid());
assert!(Channel::new(7, Cbw::Cbw40).is_valid());
assert!(Channel::new(11, Cbw::Cbw20).is_valid());
assert!(Channel::new(11, Cbw::Cbw40Below).is_valid());
assert!(Channel::new(36, Cbw::Cbw20).is_valid());
assert!(Channel::new(36, Cbw::Cbw40).is_valid());
assert!(Channel::new(36, Cbw::Cbw160).is_valid());
assert!(Channel::new(40, Cbw::Cbw20).is_valid());
assert!(Channel::new(40, Cbw::Cbw40Below).is_valid());
assert!(Channel::new(40, Cbw::Cbw160).is_valid());
assert!(Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).is_valid());
assert!(Channel::new(40, Cbw::Cbw80P80 { secondary80: 155 }).is_valid());
assert!(Channel::new(161, Cbw::Cbw80P80 { secondary80: 42 }).is_valid());
}
#[test]
fn test_invalid_combo() {
assert!(!Channel::new(1, Cbw::Cbw40Below).is_valid());
assert!(!Channel::new(4, Cbw::Cbw40Below).is_valid());
assert!(!Channel::new(8, Cbw::Cbw40).is_valid());
assert!(!Channel::new(11, Cbw::Cbw40).is_valid());
assert!(!Channel::new(6, Cbw::Cbw80).is_valid());
assert!(!Channel::new(6, Cbw::Cbw160).is_valid());
assert!(!Channel::new(6, Cbw::Cbw80P80 { secondary80: 155 }).is_valid());
assert!(!Channel::new(36, Cbw::Cbw40Below).is_valid());
assert!(!Channel::new(36, Cbw::Cbw80P80 { secondary80: 58 }).is_valid());
assert!(!Channel::new(40, Cbw::Cbw40).is_valid());
assert!(!Channel::new(40, Cbw::Cbw80P80 { secondary80: 42 }).is_valid());
assert!(!Channel::new(165, Cbw::Cbw80).is_valid());
assert!(!Channel::new(165, Cbw::Cbw80P80 { secondary80: 42 }).is_valid());
}
#[test]
fn test_is_2ghz_or_5ghz() {
assert!(Channel::new(1, Cbw::Cbw20).is_2ghz());
assert!(!Channel::new(1, Cbw::Cbw20).is_5ghz());
assert!(Channel::new(36, Cbw::Cbw20).is_5ghz());
assert!(!Channel::new(36, Cbw::Cbw20).is_2ghz());
}
#[test]
fn test_is_dfs() {
assert!(!Channel::new(1, Cbw::Cbw20).is_dfs());
assert!(!Channel::new(36, Cbw::Cbw20).is_dfs());
assert!(Channel::new(50, Cbw::Cbw20).is_dfs());
assert!(Channel::new(144, Cbw::Cbw20).is_dfs());
assert!(!Channel::new(149, Cbw::Cbw20).is_dfs());
}
#[test]
fn test_convert_fidl_channel() {
let mut f = Channel::new(1, Cbw::Cbw20).to_fidl();
assert!(f.primary == 1 && f.cbw == fidl_common::Cbw::Cbw20 && f.secondary80 == 0);
f = Channel::new(36, Cbw::Cbw80P80 { secondary80: 155 }).to_fidl();
assert!(f.primary == 36 && f.cbw == fidl_common::Cbw::Cbw80P80 && f.secondary80 == 155);
let mut c = Channel::from_fidl(fidl_common::WlanChan {
primary: 11,
cbw: fidl_common::Cbw::Cbw40Below,
secondary80: 123,
});
assert!(c.primary == 11 && c.cbw == Cbw::Cbw40Below);
c = Channel::from_fidl(fidl_common::WlanChan {
primary: 149,
cbw: fidl_common::Cbw::Cbw80P80,
secondary80: 42,
});
assert!(c.primary == 149 && c.cbw == Cbw::Cbw80P80 { secondary80: 42 });
}
#[test]
fn test_convert_fidl_phy() {
let p = Phy::Vht;
assert_eq!(p.to_fidl(), fidl_common::Phy::Vht);
assert_eq!(Phy::from_fidl(p.to_fidl()), p);
}
}