1use crate::client::scanner::Scanner;
6use crate::client::{Context, TimedEvent};
7use crate::device::DeviceOps;
8use anyhow::bail;
9use futures::Future;
10use wlan_common::mac::BeaconHdr;
11use wlan_common::timer::EventHandle;
12use wlan_common::{TimeUnit, ie};
13use zerocopy::SplitByteSlice;
14use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fuchsia_async as fasync};
15
16pub trait ChannelActions {
17 fn switch_channel(
18 &mut self,
19 new_main_channel: fidl_ieee80211::WlanChannel,
20 ) -> impl Future<Output = Result<(), zx::Status>>;
21 fn schedule_channel_switch_timeout(&mut self, time: zx::MonotonicInstant) -> EventHandle;
22 fn disable_scanning(&mut self) -> impl Future<Output = Result<(), zx::Status>>;
23 fn enable_scanning(&mut self);
24 fn disable_tx(&mut self) -> Result<(), zx::Status>;
25 fn enable_tx(&mut self);
26}
27
28pub struct ChannelActionHandle<'a, D> {
29 ctx: &'a mut Context<D>,
30 scanner: &'a mut Scanner,
31}
32
33impl<'a, D: DeviceOps> ChannelActions for ChannelActionHandle<'a, D> {
34 async fn switch_channel(
35 &mut self,
36 new_main_channel: fidl_ieee80211::WlanChannel,
37 ) -> Result<(), zx::Status> {
38 self.ctx.device.set_channel(new_main_channel).await
39 }
40 fn schedule_channel_switch_timeout(&mut self, time: zx::MonotonicInstant) -> EventHandle {
41 self.ctx.timer.schedule_at(time, TimedEvent::ChannelSwitch)
42 }
43 async fn disable_scanning(&mut self) -> Result<(), zx::Status> {
44 let mut bound_scanner = self.scanner.bind(self.ctx);
45 bound_scanner.disable_scanning().await
46 }
47 fn enable_scanning(&mut self) {
48 let mut bound_scanner = self.scanner.bind(self.ctx);
49 bound_scanner.enable_scanning()
50 }
51 fn disable_tx(&mut self) -> Result<(), zx::Status> {
52 Err(zx::Status::NOT_SUPPORTED)
54 }
55 fn enable_tx(&mut self) {}
56}
57
58#[derive(Default)]
59pub struct ChannelState {
60 main_channel: Option<fidl_ieee80211::WlanChannel>,
63 pending_channel_switch: Option<(ChannelSwitch, EventHandle)>,
64 beacon_interval: Option<TimeUnit>,
65 last_beacon_timestamp: Option<fasync::MonotonicInstant>,
66}
67
68pub struct BoundChannelState<'a, T> {
69 channel_state: &'a mut ChannelState,
70 actions: T,
71}
72
73impl ChannelState {
74 #[cfg(test)]
75 pub fn new_with_main_channel(main_channel: fidl_ieee80211::WlanChannel) -> Self {
76 Self { main_channel: Some(main_channel), ..Default::default() }
77 }
78
79 pub fn get_main_channel(&self) -> Option<fidl_ieee80211::WlanChannel> {
80 self.main_channel
81 }
82
83 pub fn bind<'a, D>(
84 &'a mut self,
85 ctx: &'a mut Context<D>,
86 scanner: &'a mut Scanner,
87 ) -> BoundChannelState<'a, ChannelActionHandle<'a, D>> {
88 BoundChannelState { channel_state: self, actions: ChannelActionHandle { ctx, scanner } }
89 }
90
91 #[cfg(test)]
92 pub fn test_bind<'a, T: ChannelActions>(&'a mut self, actions: T) -> BoundChannelState<'a, T> {
93 BoundChannelState { channel_state: self, actions }
94 }
95
96 fn channel_switch_time_from_count(&self, channel_switch_count: u8) -> fasync::MonotonicInstant {
97 let beacon_interval =
98 self.beacon_interval.clone().unwrap_or(TimeUnit::DEFAULT_BEACON_INTERVAL);
99 let beacon_duration = fasync::MonotonicDuration::from(beacon_interval);
100 let duration = beacon_duration * channel_switch_count;
101 let now = fasync::MonotonicInstant::now();
102 let mut last_beacon =
103 self.last_beacon_timestamp.unwrap_or_else(|| fasync::MonotonicInstant::now());
104 while now - last_beacon > beacon_duration {
107 last_beacon += beacon_duration;
108 }
109 last_beacon + duration
110 }
111}
112
113impl<'a, T: ChannelActions> BoundChannelState<'a, T> {
114 pub async fn set_main_channel(
116 &mut self,
117 new_main_channel: fidl_ieee80211::WlanChannel,
118 ) -> Result<(), zx::Status> {
119 self.channel_state.pending_channel_switch.take();
120 let result = self.actions.switch_channel(new_main_channel).await;
121 match result {
122 Ok(()) => {
123 log::info!("Switched to new main channel {:?}", new_main_channel);
124 self.channel_state.main_channel.replace(new_main_channel);
125 }
126 Err(e) => {
127 log::error!("Failed to switch to new main channel {:?}: {}", new_main_channel, e);
128 }
129 }
130 self.actions.enable_scanning();
131 self.actions.enable_tx();
132 result
133 }
134
135 pub fn clear_main_channel(&mut self) {
139 self.channel_state.main_channel.take();
140 self.channel_state.pending_channel_switch.take();
141 self.channel_state.last_beacon_timestamp.take();
142 self.channel_state.beacon_interval.take();
143 self.actions.enable_scanning();
144 self.actions.enable_tx();
145 }
146
147 pub async fn handle_beacon(
148 &mut self,
149 header: &BeaconHdr,
150 elements: &[u8],
151 ) -> Result<(), anyhow::Error> {
152 self.channel_state.last_beacon_timestamp.replace(fasync::MonotonicInstant::now());
153 self.channel_state.beacon_interval.replace(header.beacon_interval);
154 self.handle_channel_switch_elements_if_present(elements, false).await
155 }
156
157 pub async fn handle_announcement_frame(
158 &mut self,
159 elements: &[u8],
160 ) -> Result<(), anyhow::Error> {
161 self.handle_channel_switch_elements_if_present(elements, true).await
162 }
163
164 async fn handle_channel_switch_elements_if_present(
165 &mut self,
166 elements: &[u8],
167 action_frame: bool,
168 ) -> Result<(), anyhow::Error> {
169 let mut csa_builder = ChannelSwitchBuilder::<&[u8]>::default();
170 for (ie_type, range) in ie::IeSummaryIter::new(elements) {
171 match ie_type {
172 ie::IeType::CHANNEL_SWITCH_ANNOUNCEMENT => {
173 let csa = ie::parse_channel_switch_announcement(&elements[range])?;
174 csa_builder.add_channel_switch_announcement((*csa).clone());
175 }
176 ie::IeType::SECONDARY_CHANNEL_OFFSET => {
177 let sco = ie::parse_sec_chan_offset(&elements[range])?;
178 csa_builder.add_secondary_channel_offset((*sco).clone());
179 }
180 ie::IeType::EXTENDED_CHANNEL_SWITCH_ANNOUNCEMENT => {
181 let ecsa = ie::parse_extended_channel_switch_announcement(&elements[range])?;
182 csa_builder.add_extended_channel_switch_announcement((*ecsa).clone());
183 }
184 ie::IeType::CHANNEL_SWITCH_WRAPPER => {
185 let csw = ie::parse_channel_switch_wrapper(&elements[range])?;
186 csa_builder.add_channel_switch_wrapper(csw);
187 }
188 ie::IeType::WIDE_BANDWIDTH_CHANNEL_SWITCH if action_frame => {
189 let wbcs = ie::parse_wide_bandwidth_channel_switch(&elements[range])?;
190 csa_builder.add_wide_bandwidth_channel_switch((*wbcs).clone());
191 }
192 ie::IeType::TRANSMIT_POWER_ENVELOPE if action_frame => {
193 let tpe = ie::parse_transmit_power_envelope(&elements[range])?;
194 csa_builder.add_transmit_power_envelope(tpe);
195 }
196 _ => (),
197 }
198 }
199 match csa_builder.build() {
200 ChannelSwitchResult::ChannelSwitch(cs) => self.handle_channel_switch(cs).await,
201 ChannelSwitchResult::NoChannelSwitch => Ok(()),
202 ChannelSwitchResult::Error(err) => Err(err.into()),
203 }
204 }
205
206 async fn handle_channel_switch(
207 &mut self,
208 channel_switch: ChannelSwitch,
209 ) -> Result<(), anyhow::Error> {
210 if !channel_switch.compatible() {
211 bail!("Incompatible channel switch announcement received.");
212 }
213
214 self.actions.disable_scanning().await?;
215 if channel_switch.channel_switch_count == 0 {
216 self.set_main_channel(channel_switch.new_channel).await.map_err(|e| e.into())
217 } else {
218 if channel_switch.pause_transmission {
219 self.actions.disable_tx()?;
221 }
222 let time = self
223 .channel_state
224 .channel_switch_time_from_count(channel_switch.channel_switch_count);
225 let event_id = self.actions.schedule_channel_switch_timeout(time.into());
226 self.channel_state.pending_channel_switch.replace((channel_switch, event_id));
227 Ok(())
228 }
229 }
230
231 pub async fn handle_channel_switch_timeout(&mut self) -> Result<(), anyhow::Error> {
232 if let Some((channel_switch, _handle)) = self.channel_state.pending_channel_switch.take() {
233 self.set_main_channel(channel_switch.new_channel).await?;
234 }
235 Ok(())
236 }
237}
238
239#[derive(Debug, PartialEq)]
240pub struct ChannelSwitch {
241 pub channel_switch_count: u8,
242 pub new_channel: fidl_ieee80211::WlanChannel,
243 pub pause_transmission: bool,
244 pub new_operating_class: Option<u8>,
245 pub new_transmit_power_envelope_specified: bool,
247}
248
249impl ChannelSwitch {
250 fn compatible(&self) -> bool {
253 self.new_operating_class.is_none()
254 && !self.new_transmit_power_envelope_specified
255 && !self.pause_transmission
256 }
257}
258
259#[derive(Default)]
260pub struct ChannelSwitchBuilder<B> {
261 channel_switch: Option<ie::ChannelSwitchAnnouncement>,
262 secondary_channel_offset: Option<ie::SecChanOffset>,
263 extended_channel_switch: Option<ie::ExtendedChannelSwitchAnnouncement>,
264 new_country: Option<ie::CountryView<B>>,
265 wide_bandwidth_channel_switch: Option<ie::WideBandwidthChannelSwitch>,
266 transmit_power_envelope: Option<ie::TransmitPowerEnvelopeView<B>>,
267}
268
269#[derive(Debug)]
270pub enum ChannelSwitchResult {
271 ChannelSwitch(ChannelSwitch),
272 NoChannelSwitch,
273 Error(ChannelSwitchError),
274}
275
276#[derive(Debug, thiserror::Error)]
277pub enum ChannelSwitchError {
278 #[error("Frame contains multiple channel switch elements with conflicting information.")]
279 ConflictingElements,
280 #[error("Invalid channel switch mode {}", _0)]
281 InvalidChannelSwitchMode(u8),
282}
283
284impl<B: SplitByteSlice> ChannelSwitchBuilder<B> {
285 pub fn build(self) -> ChannelSwitchResult {
289 let (mode, new_channel_number, channel_switch_count, new_operating_class) =
294 if let Some(csa) = self.channel_switch {
295 if let Some(ecsa) = self.extended_channel_switch {
296 if csa.new_channel_number != ecsa.new_channel_number {
298 return ChannelSwitchResult::Error(ChannelSwitchError::ConflictingElements);
299 }
300 }
301 (csa.mode, csa.new_channel_number, csa.channel_switch_count, None)
303 } else if let Some(ecsa) = self.extended_channel_switch {
304 (
306 ecsa.mode,
307 ecsa.new_channel_number,
308 ecsa.channel_switch_count,
309 Some(ecsa.new_operating_class),
310 )
311 } else {
312 return ChannelSwitchResult::NoChannelSwitch;
313 };
314
315 let pause_transmission = match mode {
316 1 => true,
317 0 => false,
318 other => {
319 return ChannelSwitchResult::Error(ChannelSwitchError::InvalidChannelSwitchMode(
320 other,
321 ));
322 }
323 };
324
325 let vht_cbw_and_segs = self
328 .wide_bandwidth_channel_switch
329 .map(|wbcs| (wbcs.new_width, wbcs.new_center_freq_seg0, wbcs.new_center_freq_seg1));
330 let sec_chan_offset =
331 self.secondary_channel_offset.unwrap_or(ie::SecChanOffset::SECONDARY_NONE);
332 let (cbw, secondary80) =
333 wlan_common::channel::derive_wide_channel_bandwidth(vht_cbw_and_segs, sec_chan_offset)
334 .to_fidl();
335
336 ChannelSwitchResult::ChannelSwitch(ChannelSwitch {
337 channel_switch_count: channel_switch_count,
338 new_channel: fidl_ieee80211::WlanChannel {
339 primary: new_channel_number,
340 cbw,
341 secondary80,
342 },
343 pause_transmission,
344 new_operating_class,
345 new_transmit_power_envelope_specified: self.transmit_power_envelope.is_some(),
346 })
347 }
348
349 pub fn add_channel_switch_announcement(&mut self, csa: ie::ChannelSwitchAnnouncement) {
350 self.channel_switch.replace(csa);
351 }
352
353 pub fn add_secondary_channel_offset(&mut self, sco: ie::SecChanOffset) {
354 self.secondary_channel_offset.replace(sco);
355 }
356
357 pub fn add_extended_channel_switch_announcement(
358 &mut self,
359 ecsa: ie::ExtendedChannelSwitchAnnouncement,
360 ) {
361 self.extended_channel_switch.replace(ecsa);
362 }
363
364 pub fn add_wide_bandwidth_channel_switch(&mut self, wbcs: ie::WideBandwidthChannelSwitch) {
365 self.wide_bandwidth_channel_switch.replace(wbcs);
366 }
367
368 pub fn add_transmit_power_envelope(&mut self, tpe: ie::TransmitPowerEnvelopeView<B>) {
369 self.transmit_power_envelope.replace(tpe);
370 }
371
372 pub fn add_channel_switch_wrapper(&mut self, csw: ie::ChannelSwitchWrapperView<B>) {
373 csw.new_country.map(|new_country| self.new_country.replace(new_country));
374 csw.new_transmit_power_envelope.map(|tpe| self.add_transmit_power_envelope(tpe));
375 csw.wide_bandwidth_channel_switch.map(|wbcs| self.add_wide_bandwidth_channel_switch(*wbcs));
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use assert_matches::assert_matches;
383 use futures::task::Poll;
384 use std::pin::pin;
385 use test_case::test_case;
386 use wlan_common::mac::CapabilityInfo;
387 use wlan_common::timer::EventId;
388 use zerocopy::IntoBytes;
389
390 const NEW_CHANNEL: u8 = 10;
391 const NEW_OPERATING_CLASS: u8 = 20;
392 const COUNT: u8 = 30;
393
394 const CHANNEL_SWITCH_ANNOUNCEMENT_HEADER: &[u8] = &[37, 3];
395
396 fn csa(
397 mode: u8,
398 new_channel_number: u8,
399 channel_switch_count: u8,
400 ) -> ie::ChannelSwitchAnnouncement {
401 ie::ChannelSwitchAnnouncement { mode, new_channel_number, channel_switch_count }
402 }
403
404 fn csa_bytes(mode: u8, new_channel_number: u8, channel_switch_count: u8) -> Vec<u8> {
405 let mut elements = vec![];
406 elements.extend(CHANNEL_SWITCH_ANNOUNCEMENT_HEADER);
407 elements.extend(csa(mode, new_channel_number, channel_switch_count).as_bytes());
408 elements
409 }
410
411 fn ecsa(
412 mode: u8,
413 new_operating_class: u8,
414 new_channel_number: u8,
415 channel_switch_count: u8,
416 ) -> ie::ExtendedChannelSwitchAnnouncement {
417 ie::ExtendedChannelSwitchAnnouncement {
418 mode,
419 new_operating_class,
420 new_channel_number,
421 channel_switch_count,
422 }
423 }
424
425 fn wbcs(seg0: u8, seg1: u8) -> ie::WideBandwidthChannelSwitch {
426 ie::WideBandwidthChannelSwitch {
427 new_width: ie::VhtChannelBandwidth::CBW_80_160_80P80,
428 new_center_freq_seg0: seg0,
429 new_center_freq_seg1: seg1,
430 }
431 }
432
433 #[test_case(Some(NEW_OPERATING_CLASS), false, false ; "when operating class present")]
434 #[test_case(None, true, false ; "when new TPE present")]
435 #[test_case(Some(NEW_OPERATING_CLASS), true, false ; "when operating class and new TPE present")]
436 #[test_case(None, false, true ; "when operating class and new TPE absent")]
437 #[fuchsia::test]
438 fn channel_switch_compatible(
439 new_operating_class: Option<u8>,
440 new_transmit_power_envelope_specified: bool,
441 expected_compatible: bool,
442 ) {
443 let channel_switch = ChannelSwitch {
444 channel_switch_count: COUNT,
445 new_channel: fidl_ieee80211::WlanChannel {
446 primary: NEW_CHANNEL,
447 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
448 secondary80: 0,
449 },
450 pause_transmission: false,
451 new_operating_class,
452 new_transmit_power_envelope_specified,
453 };
454 assert_eq!(channel_switch.compatible(), expected_compatible);
455 }
456
457 #[test]
458 fn empty_builder_returns_no_csa() {
459 let builder = ChannelSwitchBuilder::<&[u8]>::default();
460 assert_matches!(builder.build(), ChannelSwitchResult::NoChannelSwitch);
461 }
462
463 #[test_case(0, false ; "when transmission is not paused")]
464 #[test_case(1, true ; "when transmission is paused")]
465 #[fuchsia::test]
466 fn basic_csa_20mhz(mode: u8, pause_transmission: bool) {
467 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
468 builder.add_channel_switch_announcement(csa(mode, NEW_CHANNEL, COUNT));
469 let channel_switch =
470 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
471 let expected_channel_switch = ChannelSwitch {
472 channel_switch_count: COUNT,
473 new_channel: fidl_ieee80211::WlanChannel {
474 primary: NEW_CHANNEL,
475 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
476 secondary80: 0,
477 },
478 pause_transmission,
479 new_operating_class: None,
480 new_transmit_power_envelope_specified: false,
481 };
482 assert_eq!(channel_switch, expected_channel_switch);
483 }
484
485 #[test_case(0, false ; "when transmission is not paused")]
486 #[test_case(1, true ; "when transmission is paused")]
487 #[fuchsia::test]
488 fn basic_ecsa_20mhz(mode: u8, pause_transmission: bool) {
489 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
490 builder.add_extended_channel_switch_announcement(ecsa(
491 mode,
492 NEW_OPERATING_CLASS,
493 NEW_CHANNEL,
494 COUNT,
495 ));
496 let channel_switch =
497 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
498 let expected_channel_switch = ChannelSwitch {
499 channel_switch_count: COUNT,
500 new_channel: fidl_ieee80211::WlanChannel {
501 primary: NEW_CHANNEL,
502 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
503 secondary80: 0,
504 },
505 pause_transmission,
506 new_operating_class: Some(NEW_OPERATING_CLASS),
507 new_transmit_power_envelope_specified: false,
508 };
509 assert_eq!(channel_switch, expected_channel_switch);
510 }
511
512 #[test]
513 fn basic_csa_40mhz() {
514 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
515 builder.add_channel_switch_announcement(csa(0, NEW_CHANNEL, COUNT));
516 builder.add_secondary_channel_offset(ie::SecChanOffset::SECONDARY_ABOVE);
517 let channel_switch =
518 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
519 let expected_channel_switch = ChannelSwitch {
520 channel_switch_count: COUNT,
521 new_channel: fidl_ieee80211::WlanChannel {
522 primary: NEW_CHANNEL,
523 cbw: fidl_ieee80211::ChannelBandwidth::Cbw40,
524 secondary80: 0,
525 },
526 pause_transmission: false,
527 new_operating_class: None,
528 new_transmit_power_envelope_specified: false,
529 };
530 assert_eq!(channel_switch, expected_channel_switch);
531 }
532
533 #[test]
534 fn basic_csa_80mhz() {
535 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
536 builder.add_channel_switch_announcement(csa(0, NEW_CHANNEL, COUNT));
537 builder.add_secondary_channel_offset(ie::SecChanOffset::SECONDARY_ABOVE);
538 builder.add_wide_bandwidth_channel_switch(wbcs(NEW_CHANNEL + 8, 0));
539 let channel_switch =
540 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
541 let expected_channel_switch = ChannelSwitch {
542 channel_switch_count: COUNT,
543 new_channel: fidl_ieee80211::WlanChannel {
544 primary: NEW_CHANNEL,
545 cbw: fidl_ieee80211::ChannelBandwidth::Cbw80,
546 secondary80: 0,
547 },
548 pause_transmission: false,
549 new_operating_class: None,
550 new_transmit_power_envelope_specified: false,
551 };
552 assert_eq!(channel_switch, expected_channel_switch);
553 }
554
555 #[test]
556 fn basic_csa_160mhz() {
557 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
558 builder.add_channel_switch_announcement(csa(0, NEW_CHANNEL, COUNT));
559 builder.add_secondary_channel_offset(ie::SecChanOffset::SECONDARY_ABOVE);
560 builder.add_wide_bandwidth_channel_switch(wbcs(NEW_CHANNEL + 8, NEW_CHANNEL + 16));
561 let channel_switch =
562 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
563 let expected_channel_switch = ChannelSwitch {
564 channel_switch_count: COUNT,
565 new_channel: fidl_ieee80211::WlanChannel {
566 primary: NEW_CHANNEL,
567 cbw: fidl_ieee80211::ChannelBandwidth::Cbw160,
568 secondary80: 0,
569 },
570 pause_transmission: false,
571 new_operating_class: None,
572 new_transmit_power_envelope_specified: false,
573 };
574 assert_eq!(channel_switch, expected_channel_switch);
575 }
576
577 #[test]
578 fn basic_csa_80p80mhz() {
579 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
580 builder.add_channel_switch_announcement(csa(0, NEW_CHANNEL, COUNT));
581 builder.add_secondary_channel_offset(ie::SecChanOffset::SECONDARY_ABOVE);
582 builder.add_wide_bandwidth_channel_switch(wbcs(NEW_CHANNEL + 8, NEW_CHANNEL + 100));
583 let channel_switch =
584 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
585 let expected_channel_switch = ChannelSwitch {
586 channel_switch_count: COUNT,
587 new_channel: fidl_ieee80211::WlanChannel {
588 primary: NEW_CHANNEL,
589 cbw: fidl_ieee80211::ChannelBandwidth::Cbw80P80,
590 secondary80: NEW_CHANNEL + 100,
591 },
592 pause_transmission: false,
593 new_operating_class: None,
594 new_transmit_power_envelope_specified: false,
595 };
596 assert_eq!(channel_switch, expected_channel_switch);
597 }
598
599 #[test_case(0, false ; "when transmission is not paused")]
600 #[test_case(1, true ; "when transmission is paused")]
601 #[fuchsia::test]
602 fn mixed_csa_ecsa_20mhz(mode: u8, pause_transmission: bool) {
603 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
604 builder.add_channel_switch_announcement(csa(mode, NEW_CHANNEL, COUNT));
605 builder.add_extended_channel_switch_announcement(ecsa(
606 mode,
607 NEW_OPERATING_CLASS,
608 NEW_CHANNEL,
609 COUNT,
610 ));
611 let channel_switch =
612 assert_matches!(builder.build(), ChannelSwitchResult::ChannelSwitch(cs) => cs);
613 let expected_channel_switch = ChannelSwitch {
614 channel_switch_count: COUNT,
615 new_channel: fidl_ieee80211::WlanChannel {
616 primary: NEW_CHANNEL,
617 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
618 secondary80: 0,
619 },
620 pause_transmission,
621 new_operating_class: None,
622 new_transmit_power_envelope_specified: false,
623 };
624 assert_eq!(channel_switch, expected_channel_switch);
625 }
626
627 #[test]
628 fn mixed_csa_ecsa_mismatch_20mhz() {
629 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
630 builder.add_channel_switch_announcement(csa(0, NEW_CHANNEL, COUNT));
631 let mut ecsa = ecsa(0, NEW_OPERATING_CLASS, NEW_CHANNEL, COUNT);
632 ecsa.new_channel_number += 1;
633 builder.add_extended_channel_switch_announcement(ecsa);
634 let err = assert_matches!(builder.build(), ChannelSwitchResult::Error(err) => err);
635 assert_matches!(err, ChannelSwitchError::ConflictingElements);
636 }
637
638 #[test]
639 fn basic_csa_invalid_mode_20mhz() {
640 let mut builder = ChannelSwitchBuilder::<&[u8]>::default();
641 builder.add_channel_switch_announcement(csa(123, NEW_CHANNEL, COUNT));
642 let err = assert_matches!(builder.build(), ChannelSwitchResult::Error(err) => err);
643 assert_matches!(err, ChannelSwitchError::InvalidChannelSwitchMode(123));
644 }
645
646 #[derive(Default)]
647 struct MockChannelActions {
648 actions: Vec<ChannelAction>,
649 event_id_ctr: EventId,
650 }
651
652 #[derive(Debug, Copy, Clone)]
653 enum ChannelAction {
654 SwitchChannel(fidl_ieee80211::WlanChannel),
655 Timeout(EventId, fasync::MonotonicInstant),
656 DisableScanning,
657 EnableScanning,
658 DisableTx,
659 EnableTx,
660 }
661
662 impl ChannelActions for &mut MockChannelActions {
663 async fn switch_channel(
664 &mut self,
665 new_main_channel: fidl_ieee80211::WlanChannel,
666 ) -> Result<(), zx::Status> {
667 self.actions.push(ChannelAction::SwitchChannel(new_main_channel));
668 Ok(())
669 }
670 fn schedule_channel_switch_timeout(&mut self, time: zx::MonotonicInstant) -> EventHandle {
671 self.event_id_ctr += 1;
672 self.actions.push(ChannelAction::Timeout(self.event_id_ctr, time.into()));
673 EventHandle::new_test(self.event_id_ctr)
674 }
675 async fn disable_scanning(&mut self) -> Result<(), zx::Status> {
676 self.actions.push(ChannelAction::DisableScanning);
677 Ok(())
678 }
679 fn enable_scanning(&mut self) {
680 self.actions.push(ChannelAction::EnableScanning);
681 }
682 fn disable_tx(&mut self) -> Result<(), zx::Status> {
683 self.actions.push(ChannelAction::DisableTx);
684 Ok(())
685 }
686 fn enable_tx(&mut self) {
687 self.actions.push(ChannelAction::EnableTx);
688 }
689 }
690
691 #[fuchsia::test(allow_stalls = false)]
692 async fn channel_state_ignores_empty_beacon_frame() {
693 let mut channel_state = ChannelState::default();
694 let mut actions = MockChannelActions::default();
695 let header = BeaconHdr::new(TimeUnit(10), CapabilityInfo(0));
696 let elements = [];
697 channel_state
698 .test_bind(&mut actions)
699 .handle_beacon(&header, &elements[..])
700 .await
701 .expect("Failed to handle beacon");
702
703 assert!(actions.actions.is_empty());
704 }
705
706 #[fuchsia::test(allow_stalls = false)]
707 async fn channel_state_handles_immediate_csa_in_beacon_frame() {
708 let mut channel_state = ChannelState::default();
709
710 let mut actions = MockChannelActions::default();
711 let header = BeaconHdr::new(TimeUnit(10), CapabilityInfo(0));
712 let mut elements = vec![];
713 elements.extend(csa_bytes(0, NEW_CHANNEL, 0));
714 channel_state
715 .test_bind(&mut actions)
716 .handle_beacon(&header, &elements[..])
717 .await
718 .expect("Failed to handle beacon");
719
720 assert_eq!(actions.actions.len(), 4);
721 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
722 let new_channel =
723 assert_matches!(actions.actions[1], ChannelAction::SwitchChannel(chan) => chan);
724 assert_eq!(new_channel.primary, NEW_CHANNEL);
725 assert_matches!(actions.actions[2], ChannelAction::EnableScanning);
726 assert_matches!(actions.actions[3], ChannelAction::EnableTx);
727 }
728
729 #[test]
730 fn channel_state_handles_delayed_csa_in_beacon_frame() {
731 let mut exec = fasync::TestExecutor::new_with_fake_time();
732 let mut channel_state = ChannelState::default();
733 let bcn_header = BeaconHdr::new(TimeUnit(10), CapabilityInfo(0));
734 let mut time = fasync::MonotonicInstant::from_nanos(0);
735 exec.set_fake_time(time);
736 let mut actions = MockChannelActions::default();
737
738 {
740 let mut bound_channel_state = channel_state.test_bind(&mut actions);
741 let elements = csa_bytes(0, NEW_CHANNEL, 2);
742 let fut = bound_channel_state.handle_beacon(&bcn_header, &elements[..]);
743 let mut fut = pin!(fut);
744 assert_matches!(
745 exec.run_until_stalled(&mut fut),
746 Poll::Ready(Ok(_)),
747 "Failed to handle beacon"
748 );
749 }
750 assert_eq!(actions.actions.len(), 2);
751 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
752 let (_first_event_id, event_time) =
753 assert_matches!(actions.actions[1], ChannelAction::Timeout(eid, time) => (eid, time));
754 assert_eq!(event_time, (time + (bcn_header.beacon_interval * 2u16).into()).into());
755 actions.actions.clear();
756
757 time += bcn_header.beacon_interval.into();
758 exec.set_fake_time(time);
759
760 {
762 let mut bound_channel_state = channel_state.test_bind(&mut actions);
763 let elements = csa_bytes(0, NEW_CHANNEL, 1);
764 let fut = bound_channel_state.handle_beacon(&bcn_header, &elements[..]);
765 let mut fut = pin!(fut);
766 assert_matches!(
767 exec.run_until_stalled(&mut fut),
768 Poll::Ready(Ok(_)),
769 "Failed to handle beacon"
770 );
771 }
772 assert_eq!(actions.actions.len(), 2);
773 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
774 let (_second_event_id, event_time) =
775 assert_matches!(actions.actions[1], ChannelAction::Timeout(eid, time) => (eid, time));
776 assert_eq!(event_time, (time + bcn_header.beacon_interval.into()).into());
777 actions.actions.clear();
778
779 time += bcn_header.beacon_interval.into();
780 exec.set_fake_time(time);
781
782 {
784 let mut bound_channel_state = channel_state.test_bind(&mut actions);
785 let fut = bound_channel_state.handle_channel_switch_timeout();
786 let mut fut = pin!(fut);
787 assert_matches!(
788 exec.run_until_stalled(&mut fut),
789 Poll::Ready(Ok(_)),
790 "Failed to handle channel switch timeout"
791 );
792 }
793
794 assert_eq!(actions.actions.len(), 3);
795 let new_channel =
796 assert_matches!(actions.actions[0], ChannelAction::SwitchChannel(chan) => chan);
797 assert_eq!(new_channel.primary, NEW_CHANNEL);
798 assert_matches!(actions.actions[1], ChannelAction::EnableScanning);
799 assert_matches!(actions.actions[2], ChannelAction::EnableTx);
800 }
801
802 #[fuchsia::test(allow_stalls = false)]
803 async fn channel_state_cannot_pause_tx() {
804 let mut channel_state = ChannelState::default();
805 let bcn_header = BeaconHdr::new(TimeUnit(10), CapabilityInfo(0));
806 let mut actions = MockChannelActions::default();
807
808 channel_state
809 .test_bind(&mut actions)
810 .handle_beacon(&bcn_header, &csa_bytes(1, NEW_CHANNEL, 2)[..])
811 .await
812 .expect_err("Shouldn't handle channel switch with tx pause");
813 assert_eq!(actions.actions.len(), 0);
814 }
815
816 #[fuchsia::test(allow_stalls = false)]
817 async fn channel_state_cannot_parse_malformed_csa() {
818 let mut channel_state = ChannelState::default();
819 let bcn_header = BeaconHdr::new(TimeUnit(10), CapabilityInfo(0));
820 let mut actions = MockChannelActions::default();
821
822 let mut element = vec![];
823 element.extend(CHANNEL_SWITCH_ANNOUNCEMENT_HEADER);
824 element.extend(&[10, 0, 0][..]); channel_state
826 .test_bind(&mut actions)
827 .handle_beacon(&bcn_header, &element[..])
828 .await
829 .expect_err("Should not handle malformed beacon");
830 assert_eq!(actions.actions.len(), 0);
831 }
832
833 #[fuchsia::test(allow_stalls = false)]
834 async fn channel_state_handles_immediate_csa_in_action_frame() {
835 let mut channel_state = ChannelState::default();
836
837 let mut actions = MockChannelActions::default();
838 channel_state
839 .test_bind(&mut actions)
840 .handle_announcement_frame(&csa_bytes(0, NEW_CHANNEL, 0)[..])
841 .await
842 .expect("Failed to handle beacon");
843
844 assert_eq!(actions.actions.len(), 4);
845 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
846 let new_channel =
847 assert_matches!(actions.actions[1], ChannelAction::SwitchChannel(chan) => chan);
848 assert_eq!(new_channel.primary, NEW_CHANNEL);
849 assert_matches!(actions.actions[2], ChannelAction::EnableScanning);
850 assert_matches!(actions.actions[3], ChannelAction::EnableTx);
851 }
852
853 #[test]
854 fn channel_state_handles_delayed_csa_in_announcement_frame() {
855 let mut exec = fasync::TestExecutor::new_with_fake_time();
856 let mut channel_state = ChannelState::default();
857 let bcn_header = BeaconHdr::new(TimeUnit(100), CapabilityInfo(0));
858 let bcn_time: fasync::MonotonicInstant =
859 fasync::MonotonicInstant::from_nanos(0) + bcn_header.beacon_interval.into();
860 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
861 let mut actions = MockChannelActions::default();
862
863 {
865 let mut bound_channel_state = channel_state.test_bind(&mut actions);
866 let elements = [];
867 let fut = bound_channel_state.handle_beacon(&bcn_header, &elements[..]);
868 let mut fut = pin!(fut);
869 assert_matches!(
870 exec.run_until_stalled(&mut fut),
871 Poll::Ready(Ok(_)),
872 "Failed to handle beacon"
873 );
874 }
875 assert!(actions.actions.is_empty());
876
877 exec.set_fake_time(bcn_time - fasync::MonotonicDuration::from_micros(500));
879 {
880 let mut bound_channel_state = channel_state.test_bind(&mut actions);
881 let elements = csa_bytes(0, NEW_CHANNEL, 1);
882 let fut = bound_channel_state.handle_announcement_frame(&elements[..]);
883 let mut fut = pin!(fut);
884 assert_matches!(
885 exec.run_until_stalled(&mut fut),
886 Poll::Ready(Ok(_)),
887 "Failed to handle announcement"
888 );
889 }
890 assert_eq!(actions.actions.len(), 2);
891 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
892 let (_event_id, event_time) =
893 assert_matches!(actions.actions[1], ChannelAction::Timeout(eid, time) => (eid, time));
894 assert_eq!(event_time, bcn_time);
895 actions.actions.clear();
896
897 exec.set_fake_time(bcn_time);
899 {
900 let mut bound_channel_state = channel_state.test_bind(&mut actions);
901 let fut = bound_channel_state.handle_channel_switch_timeout();
902 let mut fut = pin!(fut);
903 assert_matches!(
904 exec.run_until_stalled(&mut fut),
905 Poll::Ready(Ok(_)),
906 "Failed to handle channel switch timeout"
907 );
908 }
909 assert_eq!(actions.actions.len(), 3);
910 let new_channel =
911 assert_matches!(actions.actions[0], ChannelAction::SwitchChannel(chan) => chan);
912 assert_eq!(new_channel.primary, NEW_CHANNEL);
913 assert_matches!(actions.actions[1], ChannelAction::EnableScanning);
914 assert_matches!(actions.actions[2], ChannelAction::EnableTx);
915 }
916
917 #[test]
918 fn channel_state_handles_delayed_csa_in_announcement_frame_with_missed_beacon() {
919 let mut exec = fasync::TestExecutor::new_with_fake_time();
920 let mut channel_state = ChannelState::default();
921 let bcn_header = BeaconHdr::new(TimeUnit(100), CapabilityInfo(0));
922 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
923 let mut actions = MockChannelActions::default();
924
925 {
927 let mut bound_channel_state = channel_state.test_bind(&mut actions);
928 let elements = [];
929 let fut = bound_channel_state.handle_beacon(&bcn_header, &elements[..]);
930 let mut fut = pin!(fut);
931 assert_matches!(
932 exec.run_until_stalled(&mut fut),
933 Poll::Ready(Ok(_)),
934 "Failed to handle beacon"
935 );
936 }
937 assert!(actions.actions.is_empty());
938
939 exec.set_fake_time(
941 fasync::MonotonicInstant::from_nanos(0)
942 + bcn_header.beacon_interval.into()
943 + fasync::MonotonicDuration::from_micros(500),
944 );
945
946 {
948 let mut bound_channel_state = channel_state.test_bind(&mut actions);
949 let elements = csa_bytes(0, NEW_CHANNEL, 1);
950 let fut = bound_channel_state.handle_announcement_frame(&elements[..]);
951 let mut fut = pin!(fut);
952 assert_matches!(
953 exec.run_until_stalled(&mut fut),
954 Poll::Ready(Ok(_)),
955 "Failed to handle announcement"
956 );
957 }
958 assert_eq!(actions.actions.len(), 2);
959 assert_matches!(actions.actions[0], ChannelAction::DisableScanning);
960 let (_event_id, event_time) =
961 assert_matches!(actions.actions[1], ChannelAction::Timeout(eid, time) => (eid, time));
962 assert_eq!(
964 event_time,
965 fasync::MonotonicInstant::from_nanos(0) + (bcn_header.beacon_interval * 2u16).into()
966 );
967 }
968}