| // 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. |
| |
| use { |
| banjo_fuchsia_hardware_wlan_info as banjo_wlan_info, |
| banjo_fuchsia_hardware_wlan_mac as banjo_wlan_mac, fidl_fuchsia_wlan_common as fidl_common, |
| fidl_fuchsia_wlan_internal as fidl_internal, fidl_fuchsia_wlan_mlme as fidl_mlme, |
| wlan_common::{ |
| ie::{ |
| parse_ht_capabilities, parse_ht_operation, parse_vht_capabilities, parse_vht_operation, |
| }, |
| mac::{Aid, Bssid}, |
| }, |
| }; |
| |
| pub fn ddk_channel_from_fidl(fc: fidl_common::WlanChan) -> banjo_wlan_info::WlanChannel { |
| let cbw = match fc.cbw { |
| fidl_common::Cbw::Cbw20 => banjo_wlan_info::WlanChannelBandwidth::B_20, |
| fidl_common::Cbw::Cbw40 => banjo_wlan_info::WlanChannelBandwidth::B_40, |
| fidl_common::Cbw::Cbw40Below => banjo_wlan_info::WlanChannelBandwidth::B_40BELOW, |
| fidl_common::Cbw::Cbw80 => banjo_wlan_info::WlanChannelBandwidth::B_80, |
| fidl_common::Cbw::Cbw160 => banjo_wlan_info::WlanChannelBandwidth::B_160, |
| fidl_common::Cbw::Cbw80P80 => banjo_wlan_info::WlanChannelBandwidth::B_80P80, |
| }; |
| banjo_wlan_info::WlanChannel { primary: fc.primary, cbw, secondary80: fc.secondary80 } |
| } |
| |
| pub fn build_ddk_assoc_ctx( |
| bssid: Bssid, |
| aid: Aid, |
| cap: fidl_mlme::NegotiatedCapabilities, |
| ht_op: Option<[u8; fidl_internal::HT_OP_LEN as usize]>, |
| vht_op: Option<[u8; fidl_internal::VHT_OP_LEN as usize]>, |
| ) -> banjo_wlan_info::WlanAssocCtx { |
| let mut rates = [0; banjo_wlan_info::WLAN_MAC_MAX_RATES as usize]; |
| rates[..cap.rates.len()].clone_from_slice(&cap.rates); |
| let has_ht_cap = cap.ht_cap.is_some(); |
| let has_vht_cap = cap.vht_cap.is_some(); |
| let phy = match (has_ht_cap, has_vht_cap) { |
| (true, true) => banjo_wlan_info::WlanPhyType::VHT, |
| (true, false) => banjo_wlan_info::WlanPhyType::HT, |
| // It is invalid to have VHT without HT and SME would guarantee it does not happen. |
| // But default to ERP nonetheless just to be safe. |
| _ => banjo_wlan_info::WlanPhyType::ERP, |
| }; |
| let ht_cap_bytes = cap.ht_cap.map_or([0; fidl_internal::HT_CAP_LEN as usize], |h| h.bytes); |
| let vht_cap_bytes = cap.vht_cap.map_or([0; fidl_internal::VHT_CAP_LEN as usize], |v| v.bytes); |
| let ht_op_bytes = ht_op.unwrap_or([0; fidl_internal::HT_OP_LEN as usize]); |
| let vht_op_bytes = vht_op.unwrap_or([0; fidl_internal::VHT_OP_LEN as usize]); |
| banjo_wlan_info::WlanAssocCtx { |
| bssid: bssid.0, |
| aid, |
| // In the association request we sent out earlier, listen_interval is always set to 0, |
| // indicating the client never enters power save mode. |
| // TODO(fxbug.dev/42217): ath10k disregard this value and hard code it to 1. |
| // It is working now but we may need to revisit. |
| listen_interval: 0, |
| phy, |
| chan: ddk_channel_from_fidl(cap.channel), |
| // TODO(fxbug.dev/29325): QoS works with Aruba/Ubiquiti for BlockAck session but it may need to be |
| // dynamically determined for each outgoing data frame. |
| // TODO(fxbug.dev/43938): Derive QoS flag and WMM parameters from device info |
| qos: has_ht_cap, |
| wmm_params: blank_wmm_params(), |
| |
| rates_cnt: cap.rates.len() as u16, // will not overflow as MAX_RATES_LEN is u8 |
| rates, |
| cap_info: cap.cap_info, |
| // All the unwrap are safe because the size of the byte array follow wire format. |
| has_ht_cap, |
| ht_cap: { *parse_ht_capabilities(&ht_cap_bytes[..]).unwrap() }.into(), |
| has_ht_op: ht_op.is_some(), |
| ht_op: { *parse_ht_operation(&ht_op_bytes[..]).unwrap() }.into(), |
| has_vht_cap, |
| vht_cap: { *parse_vht_capabilities(&vht_cap_bytes[..]).unwrap() }.into(), |
| has_vht_op: vht_op.is_some(), |
| vht_op: { *parse_vht_operation(&vht_op_bytes[..]).unwrap() }.into(), |
| } |
| } |
| |
| pub fn get_rssi_dbm(rx_info: banjo_wlan_mac::WlanRxInfo) -> Option<i8> { |
| match rx_info.valid_fields & banjo_wlan_info::WlanRxInfoValid::RSSI.0 != 0 |
| && rx_info.rssi_dbm != 0 |
| { |
| true => Some(rx_info.rssi_dbm), |
| false => None, |
| } |
| } |
| |
| pub fn blank_wmm_params() -> banjo_wlan_info::WlanWmmParams { |
| banjo_wlan_info::WlanWmmParams { |
| apsd: false, |
| ac_be_params: blank_wmm_ac_params(), |
| ac_bk_params: blank_wmm_ac_params(), |
| ac_vi_params: blank_wmm_ac_params(), |
| ac_vo_params: blank_wmm_ac_params(), |
| } |
| } |
| |
| fn blank_wmm_ac_params() -> banjo_wlan_info::WlanWmmAcParams { |
| banjo_wlan_info::WlanWmmAcParams { ecw_min: 0, ecw_max: 0, aifsn: 0, txop_limit: 0, acm: false } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, banjo_ddk_hw_wlan_ieee80211 as banjo_80211, std::convert::TryInto, |
| wlan_common::ie, zerocopy::AsBytes, |
| }; |
| |
| #[test] |
| fn assoc_ctx_construction_successful() { |
| let ddk = build_ddk_assoc_ctx( |
| Bssid([1, 2, 3, 4, 5, 6]), |
| 42, |
| fidl_mlme::NegotiatedCapabilities { |
| channel: fidl_common::WlanChan { |
| primary: 149, |
| cbw: fidl_common::Cbw::Cbw40, |
| secondary80: 42, |
| }, |
| cap_info: 0x1234, |
| rates: vec![111, 112, 113, 114, 115, 116, 117, 118, 119, 120], |
| wmm_param: None, |
| ht_cap: Some(Box::new(fidl_internal::HtCapabilities { |
| bytes: ie::fake_ht_capabilities().as_bytes().try_into().unwrap(), |
| })), |
| vht_cap: Some(Box::new(fidl_internal::VhtCapabilities { |
| bytes: ie::fake_vht_capabilities().as_bytes().try_into().unwrap(), |
| })), |
| }, |
| Some(ie::fake_ht_operation().as_bytes().try_into().unwrap()), |
| Some(ie::fake_vht_operation().as_bytes().try_into().unwrap()), |
| ); |
| assert_eq!([1, 2, 3, 4, 5, 6], ddk.bssid); |
| assert_eq!(42, ddk.aid); |
| assert_eq!(0, ddk.listen_interval); |
| assert_eq!(banjo_wlan_info::WlanPhyType::VHT, ddk.phy); |
| assert_eq!( |
| banjo_wlan_info::WlanChannel { |
| primary: 149, |
| cbw: banjo_wlan_info::WlanChannelBandwidth::B_40, |
| secondary80: 42 |
| }, |
| ddk.chan |
| ); |
| assert_eq!(true, ddk.qos); |
| |
| assert_eq!(10, ddk.rates_cnt); |
| assert_eq!([111, 112, 113, 114, 115, 116, 117, 118, 119, 120], ddk.rates[0..10]); |
| assert_eq!(&[0; 253][..], &ddk.rates[10..]); |
| |
| assert_eq!(0x1234, ddk.cap_info); |
| |
| assert_eq!(true, ddk.has_ht_cap); |
| let expected_ht_cap: banjo_80211::Ieee80211HtCapabilities = |
| ie::fake_ht_capabilities().into(); |
| |
| // PartialEq not derived because supported_mcs_set is a union. Compare fields individually. |
| assert_eq!({ expected_ht_cap.ht_capability_info }, { ddk.ht_cap.ht_capability_info }); |
| assert_eq!({ expected_ht_cap.ampdu_params }, { ddk.ht_cap.ampdu_params }); |
| |
| // A union. Compare both variants. |
| unsafe { |
| assert_eq!(expected_ht_cap.supported_mcs_set.bytes, ddk.ht_cap.supported_mcs_set.bytes); |
| assert_eq!( |
| expected_ht_cap.supported_mcs_set.fields, |
| ddk.ht_cap.supported_mcs_set.fields |
| ); |
| } |
| |
| assert_eq!({ expected_ht_cap.tx_beamforming_capabilities }, { |
| ddk.ht_cap.tx_beamforming_capabilities |
| }); |
| assert_eq!(expected_ht_cap.asel_capabilities, ddk.ht_cap.asel_capabilities); |
| |
| assert_eq!(true, ddk.has_ht_op); |
| let expected_ht_op: banjo_wlan_info::WlanHtOp = ie::fake_ht_operation().into(); |
| assert_eq!(expected_ht_op, ddk.ht_op); |
| |
| assert_eq!(true, ddk.has_vht_cap); |
| let expected_vht_cap: banjo_80211::Ieee80211VhtCapabilities = |
| ie::fake_vht_capabilities().into(); |
| assert_eq!(expected_vht_cap, ddk.vht_cap); |
| |
| assert_eq!(true, ddk.has_vht_op); |
| let expected_vht_op: banjo_wlan_info::WlanVhtOp = ie::fake_vht_operation().into(); |
| assert_eq!(expected_vht_op, ddk.vht_op); |
| } |
| |
| fn empty_rx_info() -> banjo_wlan_mac::WlanRxInfo { |
| banjo_wlan_mac::WlanRxInfo { |
| rx_flags: 0, |
| valid_fields: 0, |
| phy: 0, |
| data_rate: 0, |
| chan: banjo_wlan_info::WlanChannel { |
| primary: 0, |
| cbw: banjo_wlan_info::WlanChannelBandwidth::B_20, |
| secondary80: 0, |
| }, |
| mcs: 0, |
| rssi_dbm: 0, |
| snr_dbh: 0, |
| } |
| } |
| |
| #[test] |
| fn test_get_rssi_dbm_field_not_valid() { |
| let rx_info = |
| banjo_wlan_mac::WlanRxInfo { valid_fields: 0, rssi_dbm: 20, ..empty_rx_info() }; |
| assert_eq!(get_rssi_dbm(rx_info), None); |
| } |
| |
| #[test] |
| fn test_get_rssi_dbm_zero_dbm() { |
| let rx_info = banjo_wlan_mac::WlanRxInfo { |
| valid_fields: banjo_wlan_info::WlanRxInfoValid::RSSI.0, |
| rssi_dbm: 0, |
| ..empty_rx_info() |
| }; |
| assert_eq!(get_rssi_dbm(rx_info), None); |
| } |
| |
| #[test] |
| fn test_get_rssi_dbm_all_good() { |
| let rx_info = banjo_wlan_mac::WlanRxInfo { |
| valid_fields: banjo_wlan_info::WlanRxInfoValid::RSSI.0, |
| rssi_dbm: 20, |
| ..empty_rx_info() |
| }; |
| assert_eq!(get_rssi_dbm(rx_info), Some(20)); |
| } |
| } |