1use crate::channel::{Cbw, Channel};
12use crate::ie::intersect::*;
13use crate::ie::{
14 self, parse_ht_capabilities, parse_vht_capabilities, HtCapabilities, SupportedRate,
15 VhtCapabilities,
16};
17use crate::mac::CapabilityInfo;
18use anyhow::{format_err, Context as _, Error};
19use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
20
21const OVERRIDE_CAP_INFO_ESS: bool = true;
25const OVERRIDE_CAP_INFO_IBSS: bool = false;
26
27const OVERRIDE_CAP_INFO_CF_POLLABLE: bool = false;
30const OVERRIDE_CAP_INFO_CF_POLL_REQUEST: bool = false;
31
32const OVERRIDE_CAP_INFO_PRIVACY: bool = false;
35
36const OVERRIDE_CAP_INFO_SPECTRUM_MGMT: bool = false;
38
39const OVERRIDE_HT_CAP_INFO_TX_STBC: bool = false;
41
42const OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET: u32 = 0;
47
48fn override_capability_info(capability_info: CapabilityInfo) -> CapabilityInfo {
51 capability_info
52 .with_ess(OVERRIDE_CAP_INFO_ESS)
53 .with_ibss(OVERRIDE_CAP_INFO_IBSS)
54 .with_cf_pollable(OVERRIDE_CAP_INFO_CF_POLLABLE)
55 .with_cf_poll_req(OVERRIDE_CAP_INFO_CF_POLL_REQUEST)
56 .with_privacy(OVERRIDE_CAP_INFO_PRIVACY)
57 .with_spectrum_mgmt(OVERRIDE_CAP_INFO_SPECTRUM_MGMT)
58}
59
60pub fn derive_join_capabilities(
65 bss_channel: Channel,
66 bss_rates: &[SupportedRate],
67 device_info: &fidl_mlme::DeviceInfo,
68) -> Result<ClientCapabilities, Error> {
69 let band_cap = get_device_band_cap(&device_info, bss_channel.primary)
71 .ok_or_else(|| format_err!("iface does not support BSS channel {}", bss_channel.primary))?;
72
73 let capability_info =
77 override_capability_info(CapabilityInfo(device_info.softmac_hardware_capability as u16));
78
79 let client_rates = band_cap.basic_rates.iter().map(|&r| SupportedRate(r)).collect::<Vec<_>>();
82 let rates = intersect_rates(ApRates(bss_rates), ClientRates(&client_rates))
83 .map_err(|error| format_err!("could not intersect rates: {:?}", error))
84 .context(format!("deriving rates: {:?} + {:?}", band_cap.basic_rates, bss_rates))?;
85
86 let (ht_cap, vht_cap) =
89 override_ht_vht(band_cap.ht_cap.as_ref(), band_cap.vht_cap.as_ref(), bss_channel.cbw)?;
90
91 Ok(ClientCapabilities(StaCapabilities { capability_info, rates, ht_cap, vht_cap }))
92}
93
94fn override_ht_vht(
97 fidl_ht_cap: Option<&Box<fidl_ieee80211::HtCapabilities>>,
98 fidl_vht_cap: Option<&Box<fidl_ieee80211::VhtCapabilities>>,
99 cbw: Cbw,
100) -> Result<(Option<HtCapabilities>, Option<VhtCapabilities>), Error> {
101 if fidl_ht_cap.is_none() && fidl_vht_cap.is_some() {
102 return Err(format_err!("VHT Cap without HT Cap is invalid."));
103 }
104
105 let ht_cap = match fidl_ht_cap {
106 Some(h) => {
107 let ht_cap = *parse_ht_capabilities(&h.bytes[..]).context("verifying HT Cap")?;
108 Some(override_ht_capabilities(ht_cap, cbw))
109 }
110 None => None,
111 };
112
113 let vht_cap = match fidl_vht_cap {
114 Some(v) => {
115 let vht_cap = *parse_vht_capabilities(&v.bytes[..]).context("verifying VHT Cap")?;
116 Some(override_vht_capabilities(vht_cap, cbw))
117 }
118 None => None,
119 };
120 Ok((ht_cap, vht_cap))
121}
122
123fn override_ht_capabilities(mut ht_cap: HtCapabilities, cbw: Cbw) -> HtCapabilities {
126 let mut ht_cap_info = ht_cap.ht_cap_info.with_tx_stbc(OVERRIDE_HT_CAP_INFO_TX_STBC);
127 match cbw {
128 Cbw::Cbw20 => ht_cap_info.set_chan_width_set(ie::ChanWidthSet::TWENTY_ONLY),
129 _ => (),
130 }
131 ht_cap.ht_cap_info = ht_cap_info;
132 ht_cap
133}
134
135fn override_vht_capabilities(mut vht_cap: VhtCapabilities, cbw: Cbw) -> VhtCapabilities {
138 let mut vht_cap_info = vht_cap.vht_cap_info;
139 if vht_cap_info.supported_cbw_set() != OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET {
140 match cbw {
145 Cbw::Cbw160 | Cbw::Cbw80P80 { secondary80: _ } => (),
146 _ => vht_cap_info.set_supported_cbw_set(OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET),
147 }
148 }
149 vht_cap.vht_cap_info = vht_cap_info;
150 vht_cap
151}
152
153fn get_band(primary_channel: u8) -> fidl_ieee80211::WlanBand {
155 if primary_channel <= 14 {
156 fidl_ieee80211::WlanBand::TwoGhz
157 } else {
158 fidl_ieee80211::WlanBand::FiveGhz
159 }
160}
161
162pub fn get_device_band_cap(
163 device_info: &fidl_mlme::DeviceInfo,
164 channel: u8,
165) -> Option<&fidl_mlme::BandCapability> {
166 let target = get_band(channel);
167 device_info.bands.iter().find(|b| b.band == target)
168}
169
170#[derive(Debug, PartialEq)]
176pub struct StaCapabilities {
177 pub capability_info: CapabilityInfo,
178 pub rates: Vec<SupportedRate>,
179 pub ht_cap: Option<HtCapabilities>,
180 pub vht_cap: Option<VhtCapabilities>,
181}
182
183#[derive(Debug, PartialEq)]
184pub struct ClientCapabilities(pub StaCapabilities);
185#[derive(Debug, PartialEq)]
186pub struct ApCapabilities(pub StaCapabilities);
187
188pub fn intersect_with_ap_as_client(
190 client: &ClientCapabilities,
191 ap: &ApCapabilities,
192) -> Result<StaCapabilities, Error> {
193 let rates = intersect_rates(ApRates(&ap.0.rates[..]), ClientRates(&client.0.rates[..]))
194 .map_err(|e| format_err!("could not intersect rates: {:?}", e))?;
195 let (capability_info, ht_cap, vht_cap) = intersect(&client.0, &ap.0);
196 Ok(StaCapabilities { rates, capability_info, ht_cap, vht_cap })
197}
198
199pub fn intersect_with_remote_client_as_ap(
201 ap: &ApCapabilities,
202 remote_client: &ClientCapabilities,
203) -> StaCapabilities {
204 let rates = intersect_rates(ApRates(&ap.0.rates[..]), ClientRates(&remote_client.0.rates[..]))
206 .unwrap_or(vec![]);
207 let (capability_info, ht_cap, vht_cap) = intersect(&ap.0, &remote_client.0);
208 StaCapabilities { rates, capability_info, ht_cap, vht_cap }
209}
210
211fn intersect(
212 ours: &StaCapabilities,
213 theirs: &StaCapabilities,
214) -> (CapabilityInfo, Option<HtCapabilities>, Option<VhtCapabilities>) {
215 let capability_info = CapabilityInfo(ours.capability_info.raw() & theirs.capability_info.raw());
217 let ht_cap = match (ours.ht_cap, theirs.ht_cap) {
218 (Some(ours), Some(theirs)) => Some(ours.intersect(&theirs)),
220 _ => None,
221 };
222 let vht_cap = match (ours.vht_cap, theirs.vht_cap) {
223 (Some(ours), Some(theirs)) => Some(ours.intersect(&theirs)),
225 _ => None,
226 };
227 (capability_info, ht_cap, vht_cap)
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::mac;
234 use crate::test_utils::fake_capabilities::fake_5ghz_band_capability_ht_cbw;
235 use assert_matches::assert_matches;
236 use fidl_fuchsia_wlan_common as fidl_common;
237
238 #[test]
239 fn test_build_cap_info() {
240 let capability_info = CapabilityInfo(0)
241 .with_ess(!OVERRIDE_CAP_INFO_ESS)
242 .with_ibss(!OVERRIDE_CAP_INFO_IBSS)
243 .with_cf_pollable(!OVERRIDE_CAP_INFO_CF_POLLABLE)
244 .with_cf_poll_req(!OVERRIDE_CAP_INFO_CF_POLL_REQUEST)
245 .with_privacy(!OVERRIDE_CAP_INFO_PRIVACY)
246 .with_spectrum_mgmt(!OVERRIDE_CAP_INFO_SPECTRUM_MGMT);
247 let capability_info = override_capability_info(capability_info);
248 assert_eq!(capability_info.ess(), OVERRIDE_CAP_INFO_ESS);
249 assert_eq!(capability_info.ibss(), OVERRIDE_CAP_INFO_IBSS);
250 assert_eq!(capability_info.cf_pollable(), OVERRIDE_CAP_INFO_CF_POLLABLE);
251 assert_eq!(capability_info.cf_poll_req(), OVERRIDE_CAP_INFO_CF_POLL_REQUEST);
252 assert_eq!(capability_info.privacy(), OVERRIDE_CAP_INFO_PRIVACY);
253 assert_eq!(capability_info.spectrum_mgmt(), OVERRIDE_CAP_INFO_SPECTRUM_MGMT);
254 }
255
256 #[test]
257 fn test_override_ht_cap() {
258 let mut ht_cap = ie::fake_ht_capabilities();
259 let ht_cap_info = ht_cap
260 .ht_cap_info
261 .with_tx_stbc(!OVERRIDE_HT_CAP_INFO_TX_STBC)
262 .with_chan_width_set(ie::ChanWidthSet::TWENTY_FORTY);
263 ht_cap.ht_cap_info = ht_cap_info;
264 let mut channel = Channel { primary: 153, cbw: Cbw::Cbw20 };
265
266 let ht_cap_info = override_ht_capabilities(ht_cap, channel.cbw).ht_cap_info;
267 assert_eq!(ht_cap_info.tx_stbc(), OVERRIDE_HT_CAP_INFO_TX_STBC);
268 assert_eq!(ht_cap_info.chan_width_set(), ie::ChanWidthSet::TWENTY_ONLY);
269
270 channel.cbw = Cbw::Cbw40;
271 let ht_cap_info = override_ht_capabilities(ht_cap, channel.cbw).ht_cap_info;
272 assert_eq!(ht_cap_info.chan_width_set(), ie::ChanWidthSet::TWENTY_FORTY);
273 }
274
275 #[test]
276 fn test_override_vht_cap() {
277 let mut vht_cap = ie::fake_vht_capabilities();
278 let vht_cap_info = vht_cap.vht_cap_info.with_supported_cbw_set(2);
279 vht_cap.vht_cap_info = vht_cap_info;
280 let mut channel = Channel { primary: 153, cbw: Cbw::Cbw20 };
281
282 let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
285 assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
286
287 channel.cbw = Cbw::Cbw40;
288 let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
289 assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
290
291 channel.cbw = Cbw::Cbw80;
292 let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
293 assert_eq!(vht_cap_info.supported_cbw_set(), OVERRIDE_VHT_CAP_INFO_SUPPORTED_CBW_SET);
294
295 channel.cbw = Cbw::Cbw160;
298 let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
299 assert_eq!(vht_cap_info.supported_cbw_set(), 2);
300
301 channel.cbw = Cbw::Cbw80P80 { secondary80: 42 };
302 let vht_cap_info = override_vht_capabilities(vht_cap, channel.cbw).vht_cap_info;
303 assert_eq!(vht_cap_info.supported_cbw_set(), 2);
304 }
305
306 #[test]
307 fn band_id() {
308 assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(1));
309 assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(14));
310 assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(36));
311 assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(165));
312 }
313
314 #[test]
315 fn test_get_band() {
316 assert_eq!(fidl_ieee80211::WlanBand::TwoGhz, get_band(14));
317 assert_eq!(fidl_ieee80211::WlanBand::FiveGhz, get_band(36));
318 }
319
320 #[test]
321 fn test_get_device_band_cap() {
322 let device_info = fidl_mlme::DeviceInfo {
323 sta_addr: [0; 6],
324 role: fidl_common::WlanMacRole::Client,
325 bands: vec![fake_5ghz_band_capability_ht_cbw(ie::ChanWidthSet::TWENTY_FORTY)],
326 softmac_hardware_capability: 0,
327 qos_capable: true,
328 };
329 assert_eq!(
330 fidl_ieee80211::WlanBand::FiveGhz,
331 get_device_band_cap(&device_info, 36).unwrap().band
332 );
333 }
334
335 fn fake_client_join_cap() -> ClientCapabilities {
336 ClientCapabilities(StaCapabilities {
337 capability_info: mac::CapabilityInfo(0x1234),
338 rates: [101, 102, 103, 104].iter().cloned().map(SupportedRate).collect(),
339 ht_cap: Some(HtCapabilities {
340 ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(2).with_tx_stbc(false),
341 ..ie::fake_ht_capabilities()
342 }),
343 vht_cap: Some(ie::fake_vht_capabilities()),
344 })
345 }
346
347 fn fake_ap_join_cap() -> ApCapabilities {
348 ApCapabilities(StaCapabilities {
349 capability_info: mac::CapabilityInfo(0x4321),
350 rates: [101 + 128, 102, 9].iter().cloned().map(SupportedRate).collect(),
352 ht_cap: Some(HtCapabilities {
353 ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(1).with_tx_stbc(true),
354 ..ie::fake_ht_capabilities()
355 }),
356 vht_cap: Some(ie::fake_vht_capabilities()),
357 })
358 }
359
360 #[test]
361 fn client_intersect_with_ap() {
362 let caps = assert_matches!(
363 intersect_with_ap_as_client(&fake_client_join_cap(), &fake_ap_join_cap()),
364 Ok(caps) => caps
365 );
366 assert_eq!(
367 caps,
368 StaCapabilities {
369 capability_info: mac::CapabilityInfo(0x0220),
370 rates: [229, 102].iter().cloned().map(SupportedRate).collect(),
371 ht_cap: Some(HtCapabilities {
372 ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(2).with_tx_stbc(false),
373 ..ie::fake_ht_capabilities()
374 }),
375 ..fake_client_join_cap().0
376 }
377 )
378 }
379
380 #[test]
381 fn ap_intersect_with_remote_client() {
382 assert_eq!(
383 intersect_with_remote_client_as_ap(&fake_ap_join_cap(), &fake_client_join_cap()),
384 StaCapabilities {
385 capability_info: mac::CapabilityInfo(0x0220),
386 rates: [229, 102].iter().cloned().map(SupportedRate).collect(),
387 ht_cap: Some(HtCapabilities {
388 ht_cap_info: ie::HtCapabilityInfo(0).with_rx_stbc(0).with_tx_stbc(true),
389 ..ie::fake_ht_capabilities()
390 }),
391 ..fake_ap_join_cap().0
392 }
393 );
394 }
395}