use crate::channel::{Cbw, Channel};
use crate::ie::intersect::*;
use crate::ie::{
self, parse_ht_capabilities, parse_vht_capabilities, HtCapabilities, SupportedRate,
VhtCapabilities,
};
use crate::mac::CapabilityInfo;
use anyhow::{format_err, Context as _, Error};
use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
const OVERRIDE_CAP_INFO_ESS: bool = true;
const OVERRIDE_CAP_INFO_IBSS: bool = false;
const OVERRIDE_CAP_INFO_CF_POLLABLE: bool = false;
const OVERRIDE_CAP_INFO_CF_POLL_REQUEST: bool = false;
const OVERRIDE_CAP_INFO_PRIVACY: bool = false;
const OVERRIDE_CAP_INFO_SPECTRUM_MGMT: bool = false;
const OVERRIDE_HT_CAP_INFO_TX_STBC: bool = false;
const OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET: u32 = 0;
fn override_capability_info(capability_info: CapabilityInfo) -> CapabilityInfo {
capability_info
.with_ess(OVERRIDE_CAP_INFO_ESS)
.with_ibss(OVERRIDE_CAP_INFO_IBSS)
.with_cf_pollable(OVERRIDE_CAP_INFO_CF_POLLABLE)
.with_cf_poll_req(OVERRIDE_CAP_INFO_CF_POLL_REQUEST)
.with_privacy(OVERRIDE_CAP_INFO_PRIVACY)
.with_spectrum_mgmt(OVERRIDE_CAP_INFO_SPECTRUM_MGMT)
}
pub fn derive_join_capabilities(
bss_channel: Channel,
bss_rates: &[SupportedRate],
device_info: &fidl_mlme::DeviceInfo,
) -> Result<ClientCapabilities, Error> {
let band_cap = get_device_band_cap(&device_info, bss_channel.primary)
.ok_or_else(|| format_err!("iface does not support BSS channel {}", bss_channel.primary))?;
let capability_info =
override_capability_info(CapabilityInfo(device_info.softmac_hardware_capability as u16));
let client_rates = band_cap.basic_rates.iter().map(|&r| SupportedRate(r)).collect::<Vec<_>>();
let rates = intersect_rates(ApRates(bss_rates), ClientRates(&client_rates))
.map_err(|error| format_err!("could not intersect rates: {:?}", error))
.context(format!("deriving rates: {:?} + {:?}", band_cap.basic_rates, bss_rates))?;
let (ht_cap, vht_cap) =
override_ht_vht(band_cap.ht_cap.as_ref(), band_cap.vht_cap.as_ref(), bss_channel.cbw)?;
Ok(ClientCapabilities(StaCapabilities { capability_info, rates, ht_cap, vht_cap }))
}
fn override_ht_vht(
fidl_ht_cap: Option<&Box<fidl_ieee80211::HtCapabilities>>,
fidl_vht_cap: Option<&Box<fidl_ieee80211::VhtCapabilities>>,
cbw: Cbw,
) -> Result<(Option<HtCapabilities>, Option<VhtCapabilities>), Error> {
if fidl_ht_cap.is_none() && fidl_vht_cap.is_some() {
return Err(format_err!("VHT Cap without HT Cap is invalid."));
}
let ht_cap = match fidl_ht_cap {
Some(h) => {
let ht_cap = *parse_ht_capabilities(&h.bytes[..]).context("verifying HT Cap")?;
Some(override_ht_capabilities(ht_cap, cbw))
}
None => None,
};
let vht_cap = match fidl_vht_cap {
Some(v) => {
let vht_cap = *parse_vht_capabilities(&v.bytes[..]).context("verifying VHT Cap")?;
Some(override_vht_capabilities(vht_cap, cbw))
}
None => None,
};
Ok((ht_cap, vht_cap))
}
fn override_ht_capabilities(mut ht_cap: HtCapabilities, cbw: Cbw) -> HtCapabilities {
let mut ht_cap_info = ht_cap.ht_cap_info.with_tx_stbc(OVERRIDE_HT_CAP_INFO_TX_STBC);
match cbw {
Cbw::Cbw20 => ht_cap_info.set_chan_width_set(ie::ChanWidthSet::TWENTY_ONLY),
_ => (),
}
ht_cap.ht_cap_info = ht_cap_info;
ht_cap
}
fn override_vht_capabilities(mut vht_cap: VhtCapabilities, cbw: Cbw) -> VhtCapabilities {
let mut vht_cap_info = vht_cap.vht_cap_info;
if vht_cap_info.supported_cbw_set() != OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET {
match cbw {
Cbw::Cbw160 | Cbw::Cbw80P80 { secondary80: _ } => (),
_ => vht_cap_info.set_supported_cbw_set(OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET),
}
}
vht_cap.vht_cap_info = vht_cap_info;
vht_cap
}
fn get_band(primary_channel: u8) -> fidl_ieee80211::WlanBand {
if primary_channel <= 14 {
fidl_ieee80211::WlanBand::TwoGhz
} else {
fidl_ieee80211::WlanBand::FiveGhz
}
}
pub fn get_device_band_cap(
device_info: &fidl_mlme::DeviceInfo,
channel: u8,
) -> Option<&fidl_mlme::BandCapability> {
let target = get_band(channel);
device_info.bands.iter().find(|b| b.band == target)
}
#[derive(Debug, PartialEq)]
pub struct StaCapabilities {
pub capability_info: CapabilityInfo,
pub rates: Vec<SupportedRate>,
pub ht_cap: Option<HtCapabilities>,
pub vht_cap: Option<VhtCapabilities>,
}
#[derive(Debug, PartialEq)]
pub struct ClientCapabilities(pub StaCapabilities);
#[derive(Debug, PartialEq)]
pub struct ApCapabilities(pub StaCapabilities);
pub fn intersect_with_ap_as_client(
client: &ClientCapabilities,
ap: &ApCapabilities,
) -> Result<StaCapabilities, Error> {
let rates = intersect_rates(ApRates(&ap.0.rates[..]), ClientRates(&client.0.rates[..]))
.map_err(|e| format_err!("could not intersect rates: {:?}", e))?;
let (capability_info, ht_cap, vht_cap) = intersect(&client.0, &ap.0);
Ok(StaCapabilities { rates, capability_info, ht_cap, vht_cap })
}
pub fn intersect_with_remote_client_as_ap(
ap: &ApCapabilities,
remote_client: &ClientCapabilities,
) -> StaCapabilities {
let rates = intersect_rates(ApRates(&ap.0.rates[..]), ClientRates(&remote_client.0.rates[..]))
.unwrap_or(vec![]);
let (capability_info, ht_cap, vht_cap) = intersect(&ap.0, &remote_client.0);
StaCapabilities { rates, capability_info, ht_cap, vht_cap }
}
fn intersect(
ours: &StaCapabilities,
theirs: &StaCapabilities,
) -> (CapabilityInfo, Option<HtCapabilities>, Option<VhtCapabilities>) {
let capability_info = CapabilityInfo(ours.capability_info.raw() & theirs.capability_info.raw());
let ht_cap = match (ours.ht_cap, theirs.ht_cap) {
(Some(ours), Some(theirs)) => Some(ours.intersect(&theirs)),
_ => None,
};
let vht_cap = match (ours.vht_cap, theirs.vht_cap) {
(Some(ours), Some(theirs)) => Some(ours.intersect(&theirs)),
_ => None,
};
(capability_info, ht_cap, vht_cap)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::fake_capabilities::fake_5ghz_band_capability_ht_cbw;
use crate::{assert_variant, mac};
use fidl_fuchsia_wlan_common as fidl_common;
#[test]
fn test_build_cap_info() {
let capability_info = CapabilityInfo(0)
.with_ess(!OVERRIDE_CAP_INFO_ESS)
.with_ibss(!OVERRIDE_CAP_INFO_IBSS)
.with_cf_pollable(!OVERRIDE_CAP_INFO_CF_POLLABLE)
.with_cf_poll_req(!OVERRIDE_CAP_INFO_CF_POLL_REQUEST)
.with_privacy(!OVERRIDE_CAP_INFO_PRIVACY)
.with_spectrum_mgmt(!OVERRIDE_CAP_INFO_SPECTRUM_MGMT);
let capability_info = override_capability_info(capability_info);
assert_eq!(capability_info.ess(), OVERRIDE_CAP_INFO_ESS);
assert_eq!(capability_info.ibss(), OVERRIDE_CAP_INFO_IBSS);
assert_eq!(capability_info.cf_pollable(), OVERRIDE_CAP_INFO_CF_POLLABLE);
assert_eq!(capability_info.cf_poll_req(), OVERRIDE_CAP_INFO_CF_POLL_REQUEST);
assert_eq!(capability_info.privacy(), OVERRIDE_CAP_INFO_PRIVACY);
assert_eq!(capability_info.spectrum_mgmt(), OVERRIDE_CAP_INFO_SPECTRUM_MGMT);
}
#[test]
fn test_override_ht_cap() {
let mut ht_cap = ie::fake_ht_capabilities();
let ht_cap_info = ht_cap
.ht_cap_info
.with_tx_stbc(!OVERRIDE_HT_CAP_INFO_TX_STBC)
.with_chan_width_set(ie::ChanWidthSet::TWENTY_FORTY);
ht_cap.ht_cap_info = ht_cap_info;
let mut channel = Channel { primary: 153, cbw: Cbw::Cbw20 };
let ht_cap_info = override_ht_capabilities(ht_cap, channel.cbw).ht_cap_info;
assert_eq!(ht_cap_info.tx_stbc(), OVERRIDE_HT_CAP_INFO_TX_STBC);
assert_eq!(ht_cap_info.chan_width_set(), ie::ChanWidthSet::TWENTY_ONLY);
channel.cbw = Cbw::Cbw40;
let ht_cap_info = override_ht_capabilities(ht_cap, channel.cbw).ht_cap_info;
assert_eq!(ht_cap_info.chan_width_set(), ie::ChanWidthSet::TWENTY_FORTY);
}
#[test]
fn test_override_vht_cap() {
let mut vht_cap = ie::fake_vht_capabilities();
let vht_cap_info = vht_cap.vht_cap_info.with_supported_cbw_set(2);
vht_cap.vht_cap_info = vht_cap_info;
let mut channel = Channel { primary: 153, cbw: Cbw::Cbw20 };
let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
channel.cbw = Cbw::Cbw40;
let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
channel.cbw = Cbw::Cbw80;
let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
channel.cbw = Cbw::Cbw160;
let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
assert_eq!(vht_cap_info.supported_cbw_set(), 2);
channel.cbw = Cbw::Cbw80P80 { secondary80: 42 };
let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
assert_eq!(vht_cap_info.supported_cbw_set(), 2);
}
#[test]
fn band_id() {
assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(1));
assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(14));
assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(36));
assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(165));
}
#[test]
fn test_get_band() {
assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(14));
assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(36));
}
#[test]
fn test_get_device_band_cap() {
let device_info = fidl_mlme::DeviceInfo {
sta_addr: [0; 6],
role: fidl_common::WlanMacRole::Client,
bands: vec![fake_5ghz_band_capability_ht_cbw(ie::ChanWidthSet::TWENTY_FORTY)],
softmac_hardware_capability: 0,
qos_capable: true,
};
assert_eq!(
fidl_ieee80211::WlanBand::FiveGhz,
get_device_band_cap(&device_info, 36).unwrap().band
);
}
fn fake_client_join_cap() -> ClientCapabilities {
ClientCapabilities(StaCapabilities {
capability_info: mac::CapabilityInfo(0x1234),
rates: [101, 102, 103, 104].iter().cloned().map(SupportedRate).collect(),
ht_cap: Some(HtCapabilities {
ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(2).with_tx_stbc(false),
..ie::fake_ht_capabilities()
}),
vht_cap: Some(ie::fake_vht_capabilities()),
})
}
fn fake_ap_join_cap() -> ApCapabilities {
ApCapabilities(StaCapabilities {
capability_info: mac::CapabilityInfo(0x4321),
rates: [101 + 128, 102, 9].iter().cloned().map(SupportedRate).collect(),
ht_cap: Some(HtCapabilities {
ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(1).with_tx_stbc(true),
..ie::fake_ht_capabilities()
}),
vht_cap: Some(ie::fake_vht_capabilities()),
})
}
#[test]
fn client_intersect_with_ap() {
let caps = assert_variant!(
intersect_with_ap_as_client(&fake_client_join_cap(), &fake_ap_join_cap()),
Ok(caps) => caps
);
assert_eq!(
caps,
StaCapabilities {
capability_info: mac::CapabilityInfo(0x0220),
rates: [229, 102].iter().cloned().map(SupportedRate).collect(),
ht_cap: Some(HtCapabilities {
ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(2).with_tx_stbc(false),
..ie::fake_ht_capabilities()
}),
..fake_client_join_cap().0
}
)
}
#[test]
fn ap_intersect_with_remote_client() {
assert_eq!(
intersect_with_remote_client_as_ap(&fake_ap_join_cap(), &fake_client_join_cap()),
StaCapabilities {
capability_info: mac::CapabilityInfo(0x0220),
rates: [229, 102].iter().cloned().map(SupportedRate).collect(),
ht_cap: Some(HtCapabilities {
ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(0).with_tx_stbc(true),
..ie::fake_ht_capabilities()
}),
..fake_ap_join_cap().0
}
);
}
}