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