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