wlan_mlme/client/
channel_switch.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // TODO(https://fxbug.dev/42060974): Support transmission pause.
53        Err(zx::Status::NOT_SUPPORTED)
54    }
55    fn enable_tx(&mut self) {}
56}
57
58#[derive(Default)]
59pub struct ChannelState {
60    // The current main channel configured in the driver. If None, the driver may
61    // be set to any channel.
62    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        // Calculate the theoretical latest beacon timestamp before now.
105        // Note this may be larger than last_beacon_timestamp if a beacon frame was missed.
106        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    /// Immediately set a new main channel in the device.
115    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    /// Clear the main channel, disable any channel switches, and return to a
136    /// normal idle state. The device will remain on whichever channel was
137    /// most recently configured.
138    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                // TODO(b/254334420): Determine if this should be fatal to the switch.
220                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    // TODO(https://fxbug.dev/42180124): Support transmit power envelope.
246    pub new_transmit_power_envelope_specified: bool,
247}
248
249impl ChannelSwitch {
250    // TODO(https://fxbug.dev/42180124): Support channel switch related feature queries.
251    /// Determines whether this ChannelSwitch can be performed by the driver.
252    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    // Convert a set of received channel-switch-related IEs into the parameters
286    // for a channel switch. Returns an error if the IEs received do not describe
287    // a deterministic, valid channel switch.
288    pub fn build(self) -> ChannelSwitchResult {
289        // Extract shared information from the channel switch or extended channel switch elements
290        // present. If both are present we check that they agree on the destination channel and then
291        // use the CSA instead of the ECSA. This decision is to avoid specifying a
292        // new_operating_class wherever possible, since operating class switches are unsupported.
293        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 both CSA and ECSA elements are present, make sure they match.
297                    if csa.new_channel_number != ecsa.new_channel_number {
298                        return ChannelSwitchResult::Error(ChannelSwitchError::ConflictingElements);
299                    }
300                }
301                // IEEE Std 802.11-2016 11.9.8 describes the operation of a CSA.
302                (csa.mode, csa.new_channel_number, csa.channel_switch_count, None)
303            } else if let Some(ecsa) = self.extended_channel_switch {
304                // IEEE Std 802.11-2016 11.10 describes the operation of an extended CSA.
305                (
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        // IEEE Std 802.11-2016 9.4.2.159 Table 9-252 specifies that wide bandwidth channel switch
326        // elements are treated identically to those in a VHT element.
327        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        // First channel switch announcement (count = 2)
739        {
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        // Second channel switch announcement (count = 1)
761        {
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        // Timeout results in completion.
783        {
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][..]); // Garbage info.
825        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        // Empty beacon frame to configure beacon parameters.
864        {
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        // CSA action frame arrives some time between beacons.
878        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        // Timeout arrives.
898        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        // Empty beacon frame to configure beacon parameters.
926        {
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        // Advance time by a bit more than one beacon, simulating a missed frame.
940        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        // CSA action frame arrives after the missed beacon.
947        {
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        // The CSA should be timed based on our best estimate of the missed beacon.
963        assert_eq!(
964            event_time,
965            fasync::MonotonicInstant::from_nanos(0) + (bcn_header.beacon_interval * 2u16).into()
966        );
967    }
968}