wlan_mlme/
device.rs

1// Copyright 2021 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::common::mac::WlanGi;
6use crate::error::Error;
7use anyhow::format_err;
8use fdf::ArenaStaticBox;
9use futures::Future;
10use futures::channel::mpsc;
11use ieee80211::MacAddr;
12use log::error;
13use std::fmt::Display;
14use std::mem;
15use std::sync::Arc;
16use trace::Id as TraceId;
17use wlan_common::mac::FrameControl;
18use wlan_common::{TimeUnit, tx_vector};
19use wlan_ffi_transport::{EthernetRx, EthernetTx, FfiEthernetTx, FfiWlanRx, WlanRx, WlanTx};
20use {
21    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
22    fidl_fuchsia_wlan_mlme as fidl_mlme, fidl_fuchsia_wlan_softmac as fidl_softmac,
23    fuchsia_trace as trace, wlan_trace as wtrace,
24};
25
26pub use test_utils::*;
27
28#[derive(Debug, PartialEq)]
29pub struct LinkStatus(u32);
30impl LinkStatus {
31    pub const DOWN: Self = Self(0);
32    pub const UP: Self = Self(1);
33}
34
35impl From<fidl_mlme::ControlledPortState> for LinkStatus {
36    fn from(state: fidl_mlme::ControlledPortState) -> Self {
37        match state {
38            fidl_mlme::ControlledPortState::Open => Self::UP,
39            fidl_mlme::ControlledPortState::Closed => Self::DOWN,
40        }
41    }
42}
43
44pub struct Device {
45    ethernet_rx: EthernetRx,
46    ethernet_tx: Option<EthernetTx>,
47    wlan_rx: Option<WlanRx>,
48    wlan_tx: WlanTx,
49    wlan_softmac_bridge_proxy: fidl_softmac::WlanSoftmacBridgeProxy,
50    minstrel: Option<crate::MinstrelWrapper>,
51    event_receiver: Option<mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>>,
52    event_sink: mpsc::UnboundedSender<fidl_mlme::MlmeEvent>,
53}
54
55impl Device {
56    pub fn new(
57        wlan_softmac_bridge_proxy: fidl_softmac::WlanSoftmacBridgeProxy,
58        ethernet_rx: EthernetRx,
59        wlan_tx: WlanTx,
60    ) -> Device {
61        let (event_sink, event_receiver) = mpsc::unbounded();
62        Device {
63            ethernet_rx,
64            ethernet_tx: None,
65            wlan_rx: None,
66            wlan_tx,
67            wlan_softmac_bridge_proxy,
68            minstrel: None,
69            event_receiver: Some(event_receiver),
70            event_sink,
71        }
72    }
73
74    // TODO(https://fxbug.dev/356119431): Share this with fullmac.
75    fn flatten_and_log_error<T>(
76        method_name: impl Display,
77        result: Result<Result<T, zx::sys::zx_status_t>, fidl::Error>,
78    ) -> Result<T, zx::Status> {
79        result
80            .map_err(|fidl_error| {
81                error!("FIDL error during {}: {:?}", method_name, fidl_error);
82                zx::Status::INTERNAL
83            })?
84            .map_err(|status| {
85                error!("{} failed: {:?}", method_name, status);
86                zx::Status::from_raw(status)
87            })
88    }
89}
90
91const REQUIRED_WLAN_HEADER_LEN: usize = 10;
92const PEER_ADDR_OFFSET: usize = 4;
93
94/// This trait abstracts operations performed by the vendor driver and ethernet device.
95pub trait DeviceOps {
96    fn wlan_softmac_query_response(
97        &mut self,
98    ) -> impl Future<Output = Result<fidl_softmac::WlanSoftmacQueryResponse, zx::Status>>;
99    fn discovery_support(
100        &mut self,
101    ) -> impl Future<Output = Result<fidl_softmac::DiscoverySupport, zx::Status>>;
102    fn mac_sublayer_support(
103        &mut self,
104    ) -> impl Future<Output = Result<fidl_common::MacSublayerSupport, zx::Status>>;
105    fn security_support(
106        &mut self,
107    ) -> impl Future<Output = Result<fidl_common::SecuritySupport, zx::Status>>;
108    fn spectrum_management_support(
109        &mut self,
110    ) -> impl Future<Output = Result<fidl_common::SpectrumManagementSupport, zx::Status>>;
111    fn start(
112        &mut self,
113        ifc_bridge: fidl::endpoints::ClientEnd<fidl_softmac::WlanSoftmacIfcBridgeMarker>,
114        ethernet_tx: EthernetTx,
115        wlan_rx: WlanRx,
116    ) -> impl Future<Output = Result<fidl::Channel, zx::Status>>;
117    fn deliver_eth_frame(&mut self, packet: &[u8]) -> Result<(), zx::Status>;
118    /// Sends the slice corresponding to |buffer| as a frame over the air. If the
119    /// caller does not pass an |async_id| to this function, then this function will
120    /// generate its own |async_id| and end the trace if an error occurs.
121    fn send_wlan_frame(
122        &mut self,
123        buffer: ArenaStaticBox<[u8]>,
124        tx_flags: fidl_softmac::WlanTxInfoFlags,
125        async_id: Option<TraceId>,
126    ) -> Result<(), zx::Status>;
127
128    fn set_ethernet_status(
129        &mut self,
130        status: LinkStatus,
131    ) -> impl Future<Output = Result<(), zx::Status>>;
132    fn set_ethernet_up(&mut self) -> impl Future<Output = Result<(), zx::Status>> {
133        self.set_ethernet_status(LinkStatus::UP)
134    }
135    fn set_ethernet_down(&mut self) -> impl Future<Output = Result<(), zx::Status>> {
136        self.set_ethernet_status(LinkStatus::DOWN)
137    }
138    fn set_channel(
139        &mut self,
140        channel: fidl_ieee80211::WlanChannel,
141    ) -> impl Future<Output = Result<(), zx::Status>>;
142    fn set_mac_address(
143        &mut self,
144        mac_addr: fidl_ieee80211::MacAddr,
145    ) -> impl Future<Output = Result<(), zx::Status>>;
146    fn start_passive_scan(
147        &mut self,
148        request: &fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest,
149    ) -> impl Future<Output = Result<fidl_softmac::WlanSoftmacBaseStartPassiveScanResponse, zx::Status>>;
150    fn start_active_scan(
151        &mut self,
152        request: &fidl_softmac::WlanSoftmacStartActiveScanRequest,
153    ) -> impl Future<Output = Result<fidl_softmac::WlanSoftmacBaseStartActiveScanResponse, zx::Status>>;
154    fn cancel_scan(
155        &mut self,
156        request: &fidl_softmac::WlanSoftmacBaseCancelScanRequest,
157    ) -> impl Future<Output = Result<(), zx::Status>>;
158    fn join_bss(
159        &mut self,
160        request: &fidl_common::JoinBssRequest,
161    ) -> impl Future<Output = Result<(), zx::Status>>;
162    fn enable_beaconing(
163        &mut self,
164        request: fidl_softmac::WlanSoftmacBaseEnableBeaconingRequest,
165    ) -> impl Future<Output = Result<(), zx::Status>>;
166    fn disable_beaconing(&mut self) -> impl Future<Output = Result<(), zx::Status>>;
167    fn install_key(
168        &mut self,
169        key_configuration: &fidl_softmac::WlanKeyConfiguration,
170    ) -> impl Future<Output = Result<(), zx::Status>>;
171    fn notify_association_complete(
172        &mut self,
173        assoc_cfg: fidl_softmac::WlanAssociationConfig,
174    ) -> impl Future<Output = Result<(), zx::Status>>;
175    fn clear_association(
176        &mut self,
177        request: &fidl_softmac::WlanSoftmacBaseClearAssociationRequest,
178    ) -> impl Future<Output = Result<(), zx::Status>>;
179    fn update_wmm_parameters(
180        &mut self,
181        request: &fidl_softmac::WlanSoftmacBaseUpdateWmmParametersRequest,
182    ) -> impl Future<Output = Result<(), zx::Status>>;
183    fn take_mlme_event_stream(&mut self) -> Option<mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>>;
184    fn send_mlme_event(&mut self, event: fidl_mlme::MlmeEvent) -> Result<(), anyhow::Error>;
185    fn set_minstrel(&mut self, minstrel: crate::MinstrelWrapper);
186    fn minstrel(&mut self) -> Option<crate::MinstrelWrapper>;
187    fn tx_vector_idx(
188        &mut self,
189        frame_control: &FrameControl,
190        peer_addr: &MacAddr,
191        flags: fidl_softmac::WlanTxInfoFlags,
192    ) -> tx_vector::TxVecIdx {
193        self.minstrel()
194            .as_ref()
195            .and_then(|minstrel| {
196                minstrel.lock().get_tx_vector_idx(frame_control, &peer_addr, flags)
197            })
198            .unwrap_or_else(|| {
199                // We either don't have minstrel, or minstrel failed to generate a tx vector.
200                // Use a reasonable default value instead.
201                // Note: This is only effective if the underlying device meets both criteria below:
202                // 1. Does not support tx status report.
203                // 2. Honors our instruction on tx_vector to use.
204                // TODO(https://fxbug.dev/42103583): Choose an optimal MCS for management frames
205                // TODO(https://fxbug.dev/42119762): Log stats about minstrel usage vs default tx vector.
206                let mcs_idx = if frame_control.is_data() { 7 } else { 3 };
207                tx_vector::TxVector::new(
208                    fidl_common::WlanPhyType::Erp,
209                    WlanGi::G_800NS,
210                    fidl_ieee80211::ChannelBandwidth::Cbw20,
211                    mcs_idx,
212                )
213                .unwrap()
214                .to_idx()
215            })
216    }
217}
218
219pub async fn try_query(
220    device: &mut impl DeviceOps,
221) -> Result<fidl_softmac::WlanSoftmacQueryResponse, Error> {
222    device
223        .wlan_softmac_query_response()
224        .await
225        .map_err(|status| Error::Status(String::from("Failed to query device."), status))
226}
227
228pub async fn try_query_iface_mac(device: &mut impl DeviceOps) -> Result<MacAddr, Error> {
229    try_query(device).await.and_then(|query_response| {
230        query_response.sta_addr.map(From::from).ok_or_else(|| {
231            Error::Internal(format_err!(
232                "Required field not set in device query response: iface MAC"
233            ))
234        })
235    })
236}
237
238pub async fn try_query_discovery_support(
239    device: &mut impl DeviceOps,
240) -> Result<fidl_softmac::DiscoverySupport, Error> {
241    device.discovery_support().await.map_err(|status| {
242        Error::Status(String::from("Failed to query discovery support for device."), status)
243    })
244}
245
246pub async fn try_query_mac_sublayer_support(
247    device: &mut impl DeviceOps,
248) -> Result<fidl_common::MacSublayerSupport, Error> {
249    device.mac_sublayer_support().await.map_err(|status| {
250        Error::Status(String::from("Failed to query MAC sublayer support for device."), status)
251    })
252}
253
254pub async fn try_query_security_support(
255    device: &mut impl DeviceOps,
256) -> Result<fidl_common::SecuritySupport, Error> {
257    device.security_support().await.map_err(|status| {
258        Error::Status(String::from("Failed to query security support for device."), status)
259    })
260}
261
262pub async fn try_query_spectrum_management_support(
263    device: &mut impl DeviceOps,
264) -> Result<fidl_common::SpectrumManagementSupport, Error> {
265    device.spectrum_management_support().await.map_err(|status| {
266        Error::Status(
267            String::from("Failed to query spectrum management support for device."),
268            status,
269        )
270    })
271}
272
273impl DeviceOps for Device {
274    async fn wlan_softmac_query_response(
275        &mut self,
276    ) -> Result<fidl_softmac::WlanSoftmacQueryResponse, zx::Status> {
277        Self::flatten_and_log_error("Query", self.wlan_softmac_bridge_proxy.query().await)
278    }
279
280    async fn discovery_support(&mut self) -> Result<fidl_softmac::DiscoverySupport, zx::Status> {
281        Self::flatten_and_log_error(
282            "QueryDiscoverySupport",
283            self.wlan_softmac_bridge_proxy.query_discovery_support().await,
284        )
285    }
286
287    async fn mac_sublayer_support(
288        &mut self,
289    ) -> Result<fidl_common::MacSublayerSupport, zx::Status> {
290        Self::flatten_and_log_error(
291            "QueryMacSublayerSupport",
292            self.wlan_softmac_bridge_proxy.query_mac_sublayer_support().await,
293        )
294    }
295
296    async fn security_support(&mut self) -> Result<fidl_common::SecuritySupport, zx::Status> {
297        Self::flatten_and_log_error(
298            "QuerySecuritySupport",
299            self.wlan_softmac_bridge_proxy.query_security_support().await,
300        )
301    }
302
303    async fn spectrum_management_support(
304        &mut self,
305    ) -> Result<fidl_common::SpectrumManagementSupport, zx::Status> {
306        Self::flatten_and_log_error(
307            "QuerySpectrumManagementSupport",
308            self.wlan_softmac_bridge_proxy.query_spectrum_management_support().await,
309        )
310    }
311
312    async fn start(
313        &mut self,
314        ifc_bridge: fidl::endpoints::ClientEnd<fidl_softmac::WlanSoftmacIfcBridgeMarker>,
315        ethernet_tx: EthernetTx,
316        wlan_rx: WlanRx,
317    ) -> Result<fidl::Channel, zx::Status> {
318        // Safety: These calls are safe because `ethernet_tx` and
319        // `wlan_rx` will outlive all uses of the constructed
320        // `FfiEthernetTx` and `FfiWlanRx` across the FFI boundary. This includes
321        // during unbind when the C++ portion of wlansoftmac will
322        // ensure no additional calls will be made through
323        // `FfiEthernetTx` and `FfiWlanRx` after unbind begins.
324        let mut ffi_ethernet_tx = unsafe { ethernet_tx.to_ffi() };
325        let mut ffi_wlan_rx = unsafe { wlan_rx.to_ffi() };
326
327        // Re-bind `ffi_ethernet_tx` and `ffi_wlan_rx` to exclusive references that stay in scope across the
328        // await. The exclusive references guarantees the consumer of the references across the FIDL
329        // hop is the only accessor and that the references are valid during the await.
330        let ffi_ethernet_tx = &mut ffi_ethernet_tx;
331        let ffi_wlan_rx = &mut ffi_wlan_rx;
332
333        self.ethernet_tx = Some(ethernet_tx);
334        self.wlan_rx = Some(wlan_rx);
335
336        self.wlan_softmac_bridge_proxy
337            .start(
338                ifc_bridge,
339                ffi_ethernet_tx as *mut FfiEthernetTx as u64,
340                ffi_wlan_rx as *mut FfiWlanRx as u64,
341            )
342            .await
343            .map_err(|error| {
344                error!("Start failed with FIDL error: {:?}", error);
345                zx::Status::INTERNAL
346            })?
347            .map_err(zx::Status::from_raw)
348    }
349
350    fn deliver_eth_frame(&mut self, packet: &[u8]) -> Result<(), zx::Status> {
351        wtrace::duration!(c"Device::deliver_eth_frame");
352        self.ethernet_rx.transfer(&fidl_softmac::EthernetRxTransferRequest {
353            packet_address: Some(packet.as_ptr() as u64),
354            packet_size: Some(packet.len() as u64),
355            ..Default::default()
356        })
357    }
358
359    fn send_wlan_frame(
360        &mut self,
361        buffer: ArenaStaticBox<[u8]>,
362        mut tx_flags: fidl_softmac::WlanTxInfoFlags,
363        async_id: Option<TraceId>,
364    ) -> Result<(), zx::Status> {
365        let async_id_provided = async_id.is_some();
366        let async_id = async_id.unwrap_or_else(|| {
367            let async_id = TraceId::new();
368            wtrace::async_begin_wlansoftmac_tx(async_id, "mlme");
369            async_id
370        });
371        wtrace::duration!(c"Device::send_data_frame");
372
373        let (arena, mut buffer) = ArenaStaticBox::into_raw(buffer);
374
375        // Safety: buffer points to a valid allocation of a slice, and arena remains
376        // is always in scope while buffer is in use.
377        let buffer = unsafe { buffer.as_mut() };
378        if buffer.len() < REQUIRED_WLAN_HEADER_LEN {
379            let status = zx::Status::BUFFER_TOO_SMALL;
380            if !async_id_provided {
381                wtrace::async_end_wlansoftmac_tx(async_id, status);
382            }
383            return Err(status);
384        }
385        // Unwrap is safe because FrameControl is the correct size.
386        const _: () =
387            assert!(mem::size_of::<FrameControl>() == 2, "Size of FrameControl is not 2 bytes");
388        let frame_control = zerocopy::Ref::into_ref(
389            zerocopy::Ref::<&[u8], FrameControl>::from_bytes(&buffer[0..=1]).unwrap(),
390        );
391        if frame_control.protected() {
392            tx_flags |= fidl_softmac::WlanTxInfoFlags::PROTECTED;
393        }
394        let peer_addr: MacAddr = {
395            let mut peer_addr = [0u8; 6];
396            // Safety: buffer is points to a slice of sufficient length
397            peer_addr.copy_from_slice(&buffer[PEER_ADDR_OFFSET..PEER_ADDR_OFFSET + 6]);
398            peer_addr.into()
399        };
400        let tx_vector_idx = self.tx_vector_idx(frame_control, &peer_addr, tx_flags);
401
402        let tx_info = wlan_common::tx_vector::TxVector::from_idx(tx_vector_idx)
403            .to_fidl_tx_info(tx_flags, self.minstrel.is_some());
404        let packet_address = Some(buffer.as_ptr() as *mut u8 as u64);
405        let packet_size = Some(buffer.len() as u64);
406
407        self.wlan_tx
408            .transfer(&fidl_softmac::WlanTxTransferRequest {
409                arena: Some(arena.as_ptr() as u64),
410                packet_size,
411                packet_address,
412                packet_info: Some(tx_info),
413                async_id: Some(async_id.into()),
414                ..Default::default()
415            })
416            .map_err(|s| {
417                if !async_id_provided {
418                    wtrace::async_end_wlansoftmac_tx(async_id, s);
419                }
420                s
421            })
422    }
423
424    async fn set_ethernet_status(&mut self, status: LinkStatus) -> Result<(), zx::Status> {
425        self.wlan_softmac_bridge_proxy.set_ethernet_status(status.0).await.map_err(|error| {
426            error!("SetEthernetStatus failed with FIDL error: {:?}", error);
427            zx::Status::INTERNAL
428        })
429    }
430
431    async fn set_channel(
432        &mut self,
433        channel: fidl_ieee80211::WlanChannel,
434    ) -> Result<(), zx::Status> {
435        self.wlan_softmac_bridge_proxy
436            .set_channel(&fidl_softmac::WlanSoftmacBaseSetChannelRequest {
437                channel: Some(channel),
438                ..Default::default()
439            })
440            .await
441            .map_err(|error| {
442                error!("SetChannel failed with FIDL error: {:?}", error);
443                zx::Status::INTERNAL
444            })?
445            .map_err(zx::Status::from_raw)
446    }
447
448    /// Setting the MAC address is currently unavailable in the softmac.fidl, due to stable
449    /// versioning limitations. It may be added in the future to support MAC address changes on
450    /// softmac devices.
451    async fn set_mac_address(
452        &mut self,
453        _mac_addr: fidl_fuchsia_wlan_ieee80211::MacAddr,
454    ) -> Result<(), zx::Status> {
455        Err(zx::Status::NOT_SUPPORTED)
456    }
457
458    async fn start_passive_scan(
459        &mut self,
460        request: &fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest,
461    ) -> Result<fidl_softmac::WlanSoftmacBaseStartPassiveScanResponse, zx::Status> {
462        Self::flatten_and_log_error(
463            "StartPassiveScan",
464            self.wlan_softmac_bridge_proxy.start_passive_scan(request).await,
465        )
466    }
467
468    async fn start_active_scan(
469        &mut self,
470        request: &fidl_softmac::WlanSoftmacStartActiveScanRequest,
471    ) -> Result<fidl_softmac::WlanSoftmacBaseStartActiveScanResponse, zx::Status> {
472        Self::flatten_and_log_error(
473            "StartActiveScan",
474            self.wlan_softmac_bridge_proxy.start_active_scan(request).await,
475        )
476    }
477
478    async fn cancel_scan(
479        &mut self,
480        request: &fidl_softmac::WlanSoftmacBaseCancelScanRequest,
481    ) -> Result<(), zx::Status> {
482        Self::flatten_and_log_error(
483            "CancelScan",
484            self.wlan_softmac_bridge_proxy.cancel_scan(request).await,
485        )
486    }
487
488    async fn join_bss(&mut self, request: &fidl_common::JoinBssRequest) -> Result<(), zx::Status> {
489        Self::flatten_and_log_error(
490            "JoinBss",
491            self.wlan_softmac_bridge_proxy.join_bss(request).await,
492        )
493    }
494
495    async fn enable_beaconing(
496        &mut self,
497        request: fidl_softmac::WlanSoftmacBaseEnableBeaconingRequest,
498    ) -> Result<(), zx::Status> {
499        self.wlan_softmac_bridge_proxy
500            .enable_beaconing(&request)
501            .await
502            .map_err(|error| {
503                error!("FIDL error during EnableBeaconing: {:?}", error);
504                zx::Status::INTERNAL
505            })?
506            .map_err(zx::Status::from_raw)
507    }
508
509    async fn disable_beaconing(&mut self) -> Result<(), zx::Status> {
510        self.wlan_softmac_bridge_proxy
511            .disable_beaconing()
512            .await
513            .map_err(|error| {
514                error!("DisableBeaconing failed with FIDL error: {:?}", error);
515                zx::Status::INTERNAL
516            })?
517            .map_err(zx::Status::from_raw)
518    }
519
520    async fn install_key(
521        &mut self,
522        key_configuration: &fidl_softmac::WlanKeyConfiguration,
523    ) -> Result<(), zx::Status> {
524        self.wlan_softmac_bridge_proxy
525            .install_key(&key_configuration)
526            .await
527            .map_err(|error| {
528                error!("FIDL error during InstallKey: {:?}", error);
529                zx::Status::INTERNAL
530            })?
531            .map_err(zx::Status::from_raw)
532    }
533
534    async fn notify_association_complete(
535        &mut self,
536        assoc_cfg: fidl_softmac::WlanAssociationConfig,
537    ) -> Result<(), zx::Status> {
538        if let Some(minstrel) = &self.minstrel {
539            minstrel.lock().add_peer(&assoc_cfg)?;
540        }
541        Self::flatten_and_log_error(
542            "NotifyAssociationComplete",
543            self.wlan_softmac_bridge_proxy.notify_association_complete(&assoc_cfg).await,
544        )
545    }
546
547    async fn clear_association(
548        &mut self,
549        request: &fidl_softmac::WlanSoftmacBaseClearAssociationRequest,
550    ) -> Result<(), zx::Status> {
551        let addr: MacAddr = request
552            .peer_addr
553            .ok_or_else(|| {
554                error!("ClearAssociation called with no peer_addr field.");
555                zx::Status::INVALID_ARGS
556            })?
557            .into();
558        if let Some(minstrel) = &self.minstrel {
559            minstrel.lock().remove_peer(&addr);
560        }
561        Self::flatten_and_log_error(
562            "ClearAssociation",
563            self.wlan_softmac_bridge_proxy.clear_association(request).await,
564        )
565    }
566
567    async fn update_wmm_parameters(
568        &mut self,
569        request: &fidl_softmac::WlanSoftmacBaseUpdateWmmParametersRequest,
570    ) -> Result<(), zx::Status> {
571        Self::flatten_and_log_error(
572            "UpdateWmmParameters",
573            self.wlan_softmac_bridge_proxy.update_wmm_parameters(request).await,
574        )
575    }
576
577    fn take_mlme_event_stream(&mut self) -> Option<mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>> {
578        self.event_receiver.take()
579    }
580
581    fn send_mlme_event(&mut self, event: fidl_mlme::MlmeEvent) -> Result<(), anyhow::Error> {
582        self.event_sink.unbounded_send(event).map_err(|e| e.into())
583    }
584
585    fn set_minstrel(&mut self, minstrel: crate::MinstrelWrapper) {
586        self.minstrel.replace(minstrel);
587    }
588
589    fn minstrel(&mut self) -> Option<crate::MinstrelWrapper> {
590        self.minstrel.as_ref().map(Arc::clone)
591    }
592}
593
594pub mod test_utils {
595    use super::*;
596    use crate::ddk_converter;
597    use fuchsia_sync::Mutex;
598    use paste::paste;
599    use std::collections::VecDeque;
600    use {
601        fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
602        fidl_fuchsia_wlan_internal as fidl_internal, fidl_fuchsia_wlan_sme as fidl_sme,
603    };
604
605    pub trait FromMlmeEvent {
606        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self>
607        where
608            Self: std::marker::Sized;
609    }
610
611    impl FromMlmeEvent for fidl_mlme::AuthenticateIndication {
612        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
613            event.into_authenticate_ind()
614        }
615    }
616
617    impl FromMlmeEvent for fidl_mlme::AssociateIndication {
618        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
619            event.into_associate_ind()
620        }
621    }
622
623    impl FromMlmeEvent for fidl_mlme::ConnectConfirm {
624        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
625            event.into_connect_conf()
626        }
627    }
628
629    impl FromMlmeEvent for fidl_mlme::StartConfirm {
630        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
631            event.into_start_conf()
632        }
633    }
634
635    impl FromMlmeEvent for fidl_mlme::StopConfirm {
636        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
637            event.into_stop_conf()
638        }
639    }
640
641    impl FromMlmeEvent for fidl_mlme::ScanResult {
642        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
643            event.into_on_scan_result()
644        }
645    }
646
647    impl FromMlmeEvent for fidl_mlme::ScanEnd {
648        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
649            event.into_on_scan_end()
650        }
651    }
652
653    impl FromMlmeEvent for fidl_mlme::EapolConfirm {
654        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
655            event.into_eapol_conf()
656        }
657    }
658
659    impl FromMlmeEvent for fidl_mlme::EapolIndication {
660        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
661            event.into_eapol_ind()
662        }
663    }
664
665    impl FromMlmeEvent for fidl_mlme::DeauthenticateConfirm {
666        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
667            event.into_deauthenticate_conf()
668        }
669    }
670
671    impl FromMlmeEvent for fidl_mlme::DeauthenticateIndication {
672        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
673            event.into_deauthenticate_ind()
674        }
675    }
676
677    impl FromMlmeEvent for fidl_mlme::DisassociateIndication {
678        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
679            event.into_disassociate_ind()
680        }
681    }
682
683    impl FromMlmeEvent for fidl_mlme::SetKeysConfirm {
684        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
685            event.into_set_keys_conf()
686        }
687    }
688
689    impl FromMlmeEvent for fidl_internal::SignalReportIndication {
690        fn from_event(event: fidl_mlme::MlmeEvent) -> Option<Self> {
691            event.into_signal_report()
692        }
693    }
694
695    pub struct FakeDeviceConfig {
696        mock_query_response: Option<Result<fidl_softmac::WlanSoftmacQueryResponse, zx::Status>>,
697        mock_discovery_support: Option<Result<fidl_softmac::DiscoverySupport, zx::Status>>,
698        mock_mac_sublayer_support: Option<Result<fidl_common::MacSublayerSupport, zx::Status>>,
699        mock_security_support: Option<Result<fidl_common::SecuritySupport, zx::Status>>,
700        mock_spectrum_management_support:
701            Option<Result<fidl_common::SpectrumManagementSupport, zx::Status>>,
702        mock_start_result: Option<Result<fidl::Channel, zx::Status>>,
703        pub start_passive_scan_fails: bool,
704        pub start_active_scan_fails: bool,
705        pub send_wlan_frame_fails: bool,
706    }
707
708    impl Default for FakeDeviceConfig {
709        fn default() -> Self {
710            Self {
711                mock_start_result: None,
712                mock_query_response: None,
713                mock_discovery_support: None,
714                mock_mac_sublayer_support: None,
715                mock_security_support: None,
716                mock_spectrum_management_support: None,
717                start_passive_scan_fails: false,
718                start_active_scan_fails: false,
719                send_wlan_frame_fails: false,
720            }
721        }
722    }
723
724    /// Generates a public [<with_mock_ $mock_name>]() function to specify a mock value for corresponding
725    /// DeviceOps method. When called, the generated function will overwrite whatever mocked value already
726    /// exists, if any, including mocked fields.
727    macro_rules! with_mock_func {
728        ( $mock_name: ident, $mock_type: path ) => {
729            paste! {
730                pub fn [<with_mock_ $mock_name>](
731                    mut self,
732                    mock_value: Result<$mock_type, zx::Status>
733                ) -> Self {
734                    self.[<mock_ $mock_name>] = Some(mock_value);
735                    self
736                }
737            }
738        };
739    }
740
741    impl FakeDeviceConfig {
742        with_mock_func!(query_response, fidl_softmac::WlanSoftmacQueryResponse);
743        with_mock_func!(discovery_support, fidl_softmac::DiscoverySupport);
744        with_mock_func!(mac_sublayer_support, fidl_common::MacSublayerSupport);
745        with_mock_func!(security_support, fidl_common::SecuritySupport);
746        with_mock_func!(spectrum_management_support, fidl_common::SpectrumManagementSupport);
747        with_mock_func!(start_result, fidl::Channel);
748
749        pub fn with_mock_sta_addr(mut self, mock_field: [u8; 6]) -> Self {
750            if let None = self.mock_query_response {
751                let mut mock_value = Self::default_mock_query_response();
752                mock_value.as_mut().unwrap().sta_addr = Some(mock_field);
753                return self.with_mock_query_response(mock_value);
754            }
755            let mock_value = self
756                .mock_query_response
757                .as_mut()
758                .unwrap()
759                .as_mut()
760                .expect("Cannot overwrite an Err value mock");
761            mock_value.sta_addr = Some(mock_field);
762            self
763        }
764
765        pub fn with_mock_mac_role(mut self, mock_field: fidl_common::WlanMacRole) -> Self {
766            if let None = self.mock_query_response {
767                let mut mock_value = Self::default_mock_query_response();
768                mock_value.as_mut().unwrap().mac_role = Some(mock_field);
769                return self.with_mock_query_response(mock_value);
770            }
771            let mock_value = self
772                .mock_query_response
773                .as_mut()
774                .unwrap()
775                .as_mut()
776                .expect("Cannot overwrite an Err value mock");
777            mock_value.mac_role = Some(mock_field);
778            self
779        }
780
781        fn default_mock_query_response()
782        -> Result<fidl_softmac::WlanSoftmacQueryResponse, zx::Status> {
783            Ok(fidl_softmac::WlanSoftmacQueryResponse {
784                sta_addr: Some([7u8; 6]),
785                mac_role: Some(fidl_common::WlanMacRole::Client),
786                supported_phys: Some(vec![
787                    fidl_common::WlanPhyType::Dsss,
788                    fidl_common::WlanPhyType::Hr,
789                    fidl_common::WlanPhyType::Ofdm,
790                    fidl_common::WlanPhyType::Erp,
791                    fidl_common::WlanPhyType::Ht,
792                    fidl_common::WlanPhyType::Vht,
793                ]),
794                hardware_capability: Some(0),
795                band_caps: Some(fake_band_caps()),
796                factory_addr: Some([7u8; 6]),
797                ..Default::default()
798            })
799        }
800
801        pub fn with_mock_probe_response_offload(
802            mut self,
803            mock_field: fidl_softmac::ProbeResponseOffloadExtension,
804        ) -> Self {
805            if let None = self.mock_discovery_support {
806                let mut mock_value = Self::default_mock_discovery_support();
807                mock_value.as_mut().unwrap().probe_response_offload = mock_field;
808                return self.with_mock_discovery_support(mock_value);
809            }
810            let mock_value = self
811                .mock_discovery_support
812                .as_mut()
813                .unwrap()
814                .as_mut()
815                .expect("Cannot overwrite an Err value mock");
816            mock_value.probe_response_offload = mock_field;
817            self
818        }
819
820        fn default_mock_discovery_support() -> Result<fidl_softmac::DiscoverySupport, zx::Status> {
821            Ok(fidl_softmac::DiscoverySupport {
822                scan_offload: fidl_softmac::ScanOffloadExtension {
823                    supported: true,
824                    scan_cancel_supported: false,
825                },
826                probe_response_offload: fidl_softmac::ProbeResponseOffloadExtension {
827                    supported: false,
828                },
829            })
830        }
831
832        pub fn with_mock_mac_implementation_type(
833            mut self,
834            mock_field: fidl_common::MacImplementationType,
835        ) -> Self {
836            if let None = self.mock_mac_sublayer_support {
837                let mut mock_value = Self::default_mock_mac_sublayer_support();
838                mock_value.as_mut().unwrap().device.mac_implementation_type = mock_field;
839                return self.with_mock_mac_sublayer_support(mock_value);
840            }
841            let mock_value = self
842                .mock_mac_sublayer_support
843                .as_mut()
844                .unwrap()
845                .as_mut()
846                .expect("Cannot overwrite an Err value mock");
847            mock_value.device.mac_implementation_type = mock_field;
848            self
849        }
850
851        fn default_mock_mac_sublayer_support() -> Result<fidl_common::MacSublayerSupport, zx::Status>
852        {
853            Ok(fidl_common::MacSublayerSupport {
854                rate_selection_offload: fidl_common::RateSelectionOffloadExtension {
855                    supported: false,
856                },
857                data_plane: fidl_common::DataPlaneExtension {
858                    data_plane_type: fidl_common::DataPlaneType::EthernetDevice,
859                },
860                device: fidl_common::DeviceExtension {
861                    is_synthetic: true,
862                    mac_implementation_type: fidl_common::MacImplementationType::Softmac,
863                    tx_status_report_supported: true,
864                },
865            })
866        }
867    }
868
869    /// Wrapper struct that can share mutable access to the internal
870    /// FakeDeviceState.
871    #[derive(Clone)]
872    pub struct FakeDevice {
873        state: Arc<Mutex<FakeDeviceState>>,
874        mlme_event_sink: mpsc::UnboundedSender<fidl_mlme::MlmeEvent>,
875    }
876
877    pub struct FakeDeviceState {
878        pub config: FakeDeviceConfig,
879        pub minstrel: Option<crate::MinstrelWrapper>,
880        pub eth_queue: Vec<Vec<u8>>,
881        pub wlan_queue: Vec<(Vec<u8>, usize)>,
882        pub wlan_softmac_ifc_bridge_proxy: Option<fidl_softmac::WlanSoftmacIfcBridgeProxy>,
883        pub mlme_event_stream: Option<mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>>,
884        pub mlme_request_sink: mpsc::UnboundedSender<wlan_sme::MlmeRequest>,
885        pub mlme_request_stream: Option<mpsc::UnboundedReceiver<wlan_sme::MlmeRequest>>,
886        pub usme_bootstrap_client_end:
887            Option<fidl::endpoints::ClientEnd<fidl_sme::UsmeBootstrapMarker>>,
888        pub usme_bootstrap_server_end:
889            Option<fidl::endpoints::ServerEnd<fidl_sme::UsmeBootstrapMarker>>,
890        pub wlan_channel: fidl_ieee80211::WlanChannel,
891        pub keys: Vec<fidl_softmac::WlanKeyConfiguration>,
892        pub next_scan_id: u64,
893        pub captured_passive_scan_request:
894            Option<fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest>,
895        pub captured_active_scan_request: Option<fidl_softmac::WlanSoftmacStartActiveScanRequest>,
896
897        pub join_bss_request: Option<fidl_common::JoinBssRequest>,
898        pub beacon_config: Option<(Vec<u8>, usize, TimeUnit)>,
899        pub link_status: LinkStatus,
900        pub assocs: std::collections::HashMap<MacAddr, fidl_softmac::WlanAssociationConfig>,
901        pub install_key_results: VecDeque<Result<(), zx::Status>>,
902        pub captured_update_wmm_parameters_request:
903            Option<fidl_softmac::WlanSoftmacBaseUpdateWmmParametersRequest>,
904    }
905
906    impl FakeDevice {
907        // TODO(https://fxbug.dev/327499461): This function is async to ensure MLME functions will
908        // run in an async context and not call `wlan_common::timer::Timer::now` without an
909        // executor.
910        pub async fn new() -> (FakeDevice, Arc<Mutex<FakeDeviceState>>) {
911            Self::new_with_config(FakeDeviceConfig::default()).await
912        }
913
914        // TODO(https://fxbug.dev/327499461): This function is async to ensure MLME functions will
915        // run in an async context and not call `wlan_common::timer::Timer::now` without an
916        // executor.
917        pub async fn new_with_config(
918            config: FakeDeviceConfig,
919        ) -> (FakeDevice, Arc<Mutex<FakeDeviceState>>) {
920            // Create a channel for SME requests, to be surfaced by start().
921            let (usme_bootstrap_client_end, usme_bootstrap_server_end) =
922                fidl::endpoints::create_endpoints::<fidl_sme::UsmeBootstrapMarker>();
923            let (mlme_event_sink, mlme_event_stream) = mpsc::unbounded();
924            let (mlme_request_sink, mlme_request_stream) = mpsc::unbounded();
925            let state = Arc::new(Mutex::new(FakeDeviceState {
926                config,
927                minstrel: None,
928                eth_queue: vec![],
929                wlan_queue: vec![],
930                wlan_softmac_ifc_bridge_proxy: None,
931                mlme_event_stream: Some(mlme_event_stream),
932                mlme_request_sink,
933                mlme_request_stream: Some(mlme_request_stream),
934                usme_bootstrap_client_end: Some(usme_bootstrap_client_end),
935                usme_bootstrap_server_end: Some(usme_bootstrap_server_end),
936                wlan_channel: fidl_ieee80211::WlanChannel {
937                    primary: 0,
938                    cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
939                    secondary80: 0,
940                },
941                next_scan_id: 0,
942                captured_passive_scan_request: None,
943                captured_active_scan_request: None,
944                keys: vec![],
945                join_bss_request: None,
946                beacon_config: None,
947                link_status: LinkStatus::DOWN,
948                assocs: std::collections::HashMap::new(),
949                install_key_results: VecDeque::new(),
950                captured_update_wmm_parameters_request: None,
951            }));
952            (FakeDevice { state: state.clone(), mlme_event_sink }, state)
953        }
954
955        pub fn state(&self) -> Arc<Mutex<FakeDeviceState>> {
956            self.state.clone()
957        }
958    }
959
960    impl FakeDeviceState {
961        #[track_caller]
962        pub fn next_mlme_msg<T: FromMlmeEvent>(&mut self) -> Result<T, Error> {
963            self.mlme_event_stream
964                .as_mut()
965                .expect("no mlme event stream available")
966                .try_next()
967                .map_err(|e| anyhow::format_err!("Failed to read mlme event stream: {}", e))
968                .and_then(|opt_next| {
969                    opt_next.ok_or_else(|| anyhow::format_err!("No message available"))
970                })
971                .and_then(|evt| {
972                    T::from_event(evt).ok_or_else(|| anyhow::format_err!("Unexpected mlme event"))
973                })
974                .map_err(|e| e.into())
975        }
976
977        pub fn reset(&mut self) {
978            self.eth_queue.clear();
979        }
980    }
981
982    impl DeviceOps for FakeDevice {
983        async fn wlan_softmac_query_response(
984            &mut self,
985        ) -> Result<fidl_softmac::WlanSoftmacQueryResponse, zx::Status> {
986            let state = self.state.lock();
987            match state.config.mock_query_response.as_ref() {
988                Some(query_response) => query_response.clone(),
989                None => FakeDeviceConfig::default_mock_query_response(),
990            }
991        }
992
993        async fn discovery_support(
994            &mut self,
995        ) -> Result<fidl_softmac::DiscoverySupport, zx::Status> {
996            let state = self.state.lock();
997            match state.config.mock_discovery_support.as_ref() {
998                Some(discovery_support) => discovery_support.clone(),
999                None => FakeDeviceConfig::default_mock_discovery_support(),
1000            }
1001        }
1002
1003        async fn mac_sublayer_support(
1004            &mut self,
1005        ) -> Result<fidl_common::MacSublayerSupport, zx::Status> {
1006            let state = self.state.lock();
1007            match state.config.mock_mac_sublayer_support.as_ref() {
1008                Some(mac_sublayer_support) => mac_sublayer_support.clone(),
1009                None => FakeDeviceConfig::default_mock_mac_sublayer_support(),
1010            }
1011        }
1012
1013        async fn security_support(&mut self) -> Result<fidl_common::SecuritySupport, zx::Status> {
1014            let state = self.state.lock();
1015            match state.config.mock_security_support.as_ref() {
1016                Some(security_support) => security_support.clone(),
1017                None => Ok(fidl_common::SecuritySupport {
1018                    mfp: fidl_common::MfpFeature { supported: false },
1019                    sae: fidl_common::SaeFeature {
1020                        driver_handler_supported: false,
1021                        sme_handler_supported: false,
1022                    },
1023                }),
1024            }
1025        }
1026
1027        async fn spectrum_management_support(
1028            &mut self,
1029        ) -> Result<fidl_common::SpectrumManagementSupport, zx::Status> {
1030            let state = self.state.lock();
1031            match state.config.mock_spectrum_management_support.as_ref() {
1032                Some(spectrum_management_support) => spectrum_management_support.clone(),
1033                None => Ok(fidl_common::SpectrumManagementSupport {
1034                    dfs: fidl_common::DfsFeature { supported: true },
1035                }),
1036            }
1037        }
1038
1039        async fn start(
1040            &mut self,
1041            ifc_bridge: fidl::endpoints::ClientEnd<fidl_softmac::WlanSoftmacIfcBridgeMarker>,
1042            _ethernet_tx: EthernetTx,
1043            _wlan_rx: WlanRx,
1044        ) -> Result<fidl::Channel, zx::Status> {
1045            let mut state = self.state.lock();
1046
1047            if let Some(mock_start_result) = state.config.mock_start_result.take() {
1048                return mock_start_result;
1049            }
1050
1051            state.wlan_softmac_ifc_bridge_proxy = Some(ifc_bridge.into_proxy());
1052            Ok(state.usme_bootstrap_server_end.take().unwrap().into_channel())
1053        }
1054
1055        fn deliver_eth_frame(&mut self, packet: &[u8]) -> Result<(), zx::Status> {
1056            self.state.lock().eth_queue.push(packet.to_vec());
1057            Ok(())
1058        }
1059
1060        fn send_wlan_frame(
1061            &mut self,
1062            buffer: ArenaStaticBox<[u8]>,
1063            _tx_flags: fidl_softmac::WlanTxInfoFlags,
1064            _async_id: Option<TraceId>,
1065        ) -> Result<(), zx::Status> {
1066            let mut state = self.state.lock();
1067            if state.config.send_wlan_frame_fails {
1068                return Err(zx::Status::IO);
1069            }
1070            state.wlan_queue.push((buffer.to_vec(), 0));
1071            Ok(())
1072        }
1073
1074        async fn set_ethernet_status(&mut self, status: LinkStatus) -> Result<(), zx::Status> {
1075            self.state.lock().link_status = status;
1076            Ok(())
1077        }
1078
1079        async fn set_channel(
1080            &mut self,
1081            wlan_channel: fidl_ieee80211::WlanChannel,
1082        ) -> Result<(), zx::Status> {
1083            self.state.lock().wlan_channel = wlan_channel;
1084            Ok(())
1085        }
1086
1087        async fn set_mac_address(
1088            &mut self,
1089            _mac_addr: fidl_fuchsia_wlan_ieee80211::MacAddr,
1090        ) -> Result<(), zx::Status> {
1091            Err(zx::Status::NOT_SUPPORTED)
1092        }
1093
1094        async fn start_passive_scan(
1095            &mut self,
1096            request: &fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest,
1097        ) -> Result<fidl_softmac::WlanSoftmacBaseStartPassiveScanResponse, zx::Status> {
1098            let mut state = self.state.lock();
1099            if state.config.start_passive_scan_fails {
1100                return Err(zx::Status::NOT_SUPPORTED);
1101            }
1102            let scan_id = state.next_scan_id;
1103            state.next_scan_id += 1;
1104            state.captured_passive_scan_request.replace(request.clone());
1105            Ok(fidl_softmac::WlanSoftmacBaseStartPassiveScanResponse {
1106                scan_id: Some(scan_id),
1107                ..Default::default()
1108            })
1109        }
1110
1111        async fn start_active_scan(
1112            &mut self,
1113            request: &fidl_softmac::WlanSoftmacStartActiveScanRequest,
1114        ) -> Result<fidl_softmac::WlanSoftmacBaseStartActiveScanResponse, zx::Status> {
1115            let mut state = self.state.lock();
1116            if state.config.start_active_scan_fails {
1117                return Err(zx::Status::NOT_SUPPORTED);
1118            }
1119            let scan_id = state.next_scan_id;
1120            state.next_scan_id += 1;
1121            state.captured_active_scan_request.replace(request.clone());
1122            Ok(fidl_softmac::WlanSoftmacBaseStartActiveScanResponse {
1123                scan_id: Some(scan_id),
1124                ..Default::default()
1125            })
1126        }
1127
1128        async fn cancel_scan(
1129            &mut self,
1130            _request: &fidl_softmac::WlanSoftmacBaseCancelScanRequest,
1131        ) -> Result<(), zx::Status> {
1132            Err(zx::Status::NOT_SUPPORTED)
1133        }
1134
1135        async fn join_bss(
1136            &mut self,
1137            request: &fidl_common::JoinBssRequest,
1138        ) -> Result<(), zx::Status> {
1139            self.state.lock().join_bss_request.replace(request.clone());
1140            Ok(())
1141        }
1142
1143        async fn enable_beaconing(
1144            &mut self,
1145            request: fidl_softmac::WlanSoftmacBaseEnableBeaconingRequest,
1146        ) -> Result<(), zx::Status> {
1147            match (request.packet_template, request.tim_ele_offset, request.beacon_interval) {
1148                (Some(packet_template), Some(tim_ele_offset), Some(beacon_interval)) => Ok({
1149                    self.state.lock().beacon_config = Some((
1150                        packet_template.mac_frame,
1151                        usize::try_from(tim_ele_offset).map_err(|_| zx::Status::INTERNAL)?,
1152                        TimeUnit(beacon_interval),
1153                    ));
1154                }),
1155                _ => Err(zx::Status::INVALID_ARGS),
1156            }
1157        }
1158
1159        async fn disable_beaconing(&mut self) -> Result<(), zx::Status> {
1160            self.state.lock().beacon_config = None;
1161            Ok(())
1162        }
1163
1164        async fn install_key(
1165            &mut self,
1166            key_configuration: &fidl_softmac::WlanKeyConfiguration,
1167        ) -> Result<(), zx::Status> {
1168            let mut state = self.state.lock();
1169            state.keys.push(key_configuration.clone());
1170            state.install_key_results.pop_front().unwrap_or(Ok(()))
1171        }
1172
1173        async fn notify_association_complete(
1174            &mut self,
1175            cfg: fidl_softmac::WlanAssociationConfig,
1176        ) -> Result<(), zx::Status> {
1177            let mut state = self.state.lock();
1178            if let Some(minstrel) = &state.minstrel {
1179                minstrel.lock().add_peer(&cfg)?
1180            }
1181            state.assocs.insert(cfg.bssid.unwrap().into(), cfg);
1182            Ok(())
1183        }
1184
1185        async fn clear_association(
1186            &mut self,
1187            request: &fidl_softmac::WlanSoftmacBaseClearAssociationRequest,
1188        ) -> Result<(), zx::Status> {
1189            let addr: MacAddr = request.peer_addr.unwrap().into();
1190            let mut state = self.state.lock();
1191            if let Some(minstrel) = &state.minstrel {
1192                minstrel.lock().remove_peer(&addr);
1193            }
1194            state.assocs.remove(&addr);
1195            state.join_bss_request = None;
1196            Ok(())
1197        }
1198
1199        async fn update_wmm_parameters(
1200            &mut self,
1201            request: &fidl_softmac::WlanSoftmacBaseUpdateWmmParametersRequest,
1202        ) -> Result<(), zx::Status> {
1203            let mut state = self.state.lock();
1204            state.captured_update_wmm_parameters_request.replace(request.clone());
1205            Ok(())
1206        }
1207
1208        fn take_mlme_event_stream(
1209            &mut self,
1210        ) -> Option<mpsc::UnboundedReceiver<fidl_mlme::MlmeEvent>> {
1211            self.state.lock().mlme_event_stream.take()
1212        }
1213
1214        fn send_mlme_event(&mut self, event: fidl_mlme::MlmeEvent) -> Result<(), anyhow::Error> {
1215            self.mlme_event_sink.unbounded_send(event).map_err(|e| e.into())
1216        }
1217
1218        fn set_minstrel(&mut self, minstrel: crate::MinstrelWrapper) {
1219            self.state.lock().minstrel.replace(minstrel);
1220        }
1221
1222        fn minstrel(&mut self) -> Option<crate::MinstrelWrapper> {
1223            self.state.lock().minstrel.as_ref().map(Arc::clone)
1224        }
1225    }
1226
1227    pub fn fake_band_caps() -> Vec<fidl_softmac::WlanSoftmacBandCapability> {
1228        vec![
1229            fidl_softmac::WlanSoftmacBandCapability {
1230                band: Some(fidl_ieee80211::WlanBand::TwoGhz),
1231                basic_rates: Some(vec![
1232                    0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
1233                ]),
1234                operating_channels: Some(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]),
1235                ht_supported: Some(true),
1236                ht_caps: Some(fidl_ieee80211::HtCapabilities {
1237                    bytes: [
1238                        0x63, 0x00, // HT capability info
1239                        0x17, // AMPDU params
1240                        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1241                        0x00, // Rx MCS bitmask, Supported MCS values: 0-7
1242                        0x01, 0x00, 0x00, 0x00, // Tx parameters
1243                        0x00, 0x00, // HT extended capabilities
1244                        0x00, 0x00, 0x00, 0x00, // TX beamforming capabilities
1245                        0x00, // ASEL capabilities
1246                    ],
1247                }),
1248                vht_supported: Some(false),
1249                vht_caps: Some(fidl_ieee80211::VhtCapabilities { bytes: Default::default() }),
1250                ..Default::default()
1251            },
1252            fidl_softmac::WlanSoftmacBandCapability {
1253                band: Some(fidl_ieee80211::WlanBand::FiveGhz),
1254                basic_rates: Some(vec![0x02, 0x04, 0x0b, 0x16, 0x30, 0x60, 0x7e, 0x7f]),
1255                operating_channels: Some(vec![36, 40, 44, 48, 149, 153, 157, 161]),
1256                ht_supported: Some(true),
1257                ht_caps: Some(fidl_ieee80211::HtCapabilities {
1258                    bytes: [
1259                        0x63, 0x00, // HT capability info
1260                        0x17, // AMPDU params
1261                        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1262                        0x00, // Rx MCS bitmask, Supported MCS values: 0-7
1263                        0x01, 0x00, 0x00, 0x00, // Tx parameters
1264                        0x00, 0x00, // HT extended capabilities
1265                        0x00, 0x00, 0x00, 0x00, // TX beamforming capabilities
1266                        0x00, // ASEL capabilities
1267                    ],
1268                }),
1269                vht_supported: Some(true),
1270                vht_caps: Some(fidl_ieee80211::VhtCapabilities {
1271                    bytes: [0x32, 0x50, 0x80, 0x0f, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00],
1272                }),
1273                ..Default::default()
1274            },
1275        ]
1276    }
1277
1278    pub fn fake_mlme_band_caps() -> Vec<fidl_mlme::BandCapability> {
1279        fake_band_caps()
1280            .into_iter()
1281            .map(ddk_converter::mlme_band_cap_from_softmac)
1282            .collect::<Result<_, _>>()
1283            .expect("Failed to convert softmac driver band capabilities.")
1284    }
1285}
1286
1287#[cfg(test)]
1288mod tests {
1289    use super::*;
1290    use crate::{WlanTxPacketExt as _, ddk_converter};
1291    use assert_matches::assert_matches;
1292    use fdf::Arena;
1293    use ieee80211::Ssid;
1294    use {fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211};
1295
1296    fn make_deauth_confirm_msg() -> fidl_mlme::DeauthenticateConfirm {
1297        fidl_mlme::DeauthenticateConfirm { peer_sta_address: [1; 6] }
1298    }
1299
1300    #[fuchsia::test(allow_stalls = false)]
1301    async fn state_method_returns_correct_pointer() {
1302        let (fake_device, fake_device_state) = FakeDevice::new().await;
1303        assert_eq!(Arc::as_ptr(&fake_device.state()), Arc::as_ptr(&fake_device_state));
1304    }
1305
1306    #[fuchsia::test(allow_stalls = false)]
1307    async fn fake_device_returns_expected_wlan_softmac_query_response() {
1308        let (mut fake_device, _) = FakeDevice::new().await;
1309        let query_response = fake_device.wlan_softmac_query_response().await.unwrap();
1310        assert_eq!(query_response.sta_addr, [7u8; 6].into());
1311        assert_eq!(query_response.factory_addr, [7u8; 6].into());
1312        assert_eq!(query_response.mac_role, Some(fidl_common::WlanMacRole::Client));
1313        assert_eq!(
1314            query_response.supported_phys,
1315            Some(vec![
1316                fidl_common::WlanPhyType::Dsss,
1317                fidl_common::WlanPhyType::Hr,
1318                fidl_common::WlanPhyType::Ofdm,
1319                fidl_common::WlanPhyType::Erp,
1320                fidl_common::WlanPhyType::Ht,
1321                fidl_common::WlanPhyType::Vht,
1322            ]),
1323        );
1324        assert_eq!(query_response.hardware_capability, Some(0));
1325
1326        let expected_band_caps = [
1327            fidl_softmac::WlanSoftmacBandCapability {
1328                band: Some(fidl_ieee80211::WlanBand::TwoGhz),
1329                basic_rates: Some(vec![
1330                    0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
1331                ]),
1332                operating_channels: Some(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]),
1333                ht_supported: Some(true),
1334                ht_caps: Some(fidl_ieee80211::HtCapabilities {
1335                    bytes: [
1336                        0x63, 0x00, // HT capability info
1337                        0x17, // AMPDU params
1338                        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1339                        0x00, // Rx MCS bitmask, Supported MCS values: 0-7
1340                        0x01, 0x00, 0x00, 0x00, // Tx parameters
1341                        0x00, 0x00, // HT extended capabilities
1342                        0x00, 0x00, 0x00, 0x00, // TX beamforming capabilities
1343                        0x00, // ASEL capabilities
1344                    ],
1345                }),
1346                vht_supported: Some(false),
1347                vht_caps: Some(fidl_ieee80211::VhtCapabilities { bytes: Default::default() }),
1348                ..Default::default()
1349            },
1350            fidl_softmac::WlanSoftmacBandCapability {
1351                band: Some(fidl_ieee80211::WlanBand::FiveGhz),
1352                basic_rates: Some(vec![0x02, 0x04, 0x0b, 0x16, 0x30, 0x60, 0x7e, 0x7f]),
1353                operating_channels: Some(vec![36, 40, 44, 48, 149, 153, 157, 161]),
1354                ht_supported: Some(true),
1355                ht_caps: Some(fidl_ieee80211::HtCapabilities {
1356                    bytes: [
1357                        0x63, 0x00, // HT capability info
1358                        0x17, // AMPDU params
1359                        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1360                        0x00, // Rx MCS bitmask, Supported MCS values: 0-7
1361                        0x01, 0x00, 0x00, 0x00, // Tx parameters
1362                        0x00, 0x00, // HT extended capabilities
1363                        0x00, 0x00, 0x00, 0x00, // TX beamforming capabilities
1364                        0x00, // ASEL capabilities
1365                    ],
1366                }),
1367                vht_supported: Some(true),
1368                vht_caps: Some(fidl_ieee80211::VhtCapabilities {
1369                    bytes: [0x32, 0x50, 0x80, 0x0f, 0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00],
1370                }),
1371                ..Default::default()
1372            },
1373        ];
1374        let actual_band_caps = query_response.band_caps.as_ref().unwrap();
1375        for (actual_band_cap, expected_band_cap) in actual_band_caps.iter().zip(&expected_band_caps)
1376        {
1377            assert_eq!(actual_band_cap, expected_band_cap);
1378        }
1379    }
1380
1381    #[fuchsia::test(allow_stalls = false)]
1382    async fn fake_device_returns_expected_discovery_support() {
1383        let (mut fake_device, _) = FakeDevice::new().await;
1384        let discovery_support = fake_device.discovery_support().await.unwrap();
1385        assert_eq!(
1386            discovery_support,
1387            fidl_softmac::DiscoverySupport {
1388                scan_offload: fidl_softmac::ScanOffloadExtension {
1389                    supported: true,
1390                    scan_cancel_supported: false,
1391                },
1392                probe_response_offload: fidl_softmac::ProbeResponseOffloadExtension {
1393                    supported: false,
1394                },
1395            }
1396        );
1397    }
1398
1399    #[fuchsia::test(allow_stalls = false)]
1400    async fn fake_device_returns_expected_mac_sublayer_support() {
1401        let (mut fake_device, _) = FakeDevice::new().await;
1402        let mac_sublayer_support = fake_device.mac_sublayer_support().await.unwrap();
1403        assert_eq!(
1404            mac_sublayer_support,
1405            fidl_common::MacSublayerSupport {
1406                rate_selection_offload: fidl_common::RateSelectionOffloadExtension {
1407                    supported: false,
1408                },
1409                data_plane: fidl_common::DataPlaneExtension {
1410                    data_plane_type: fidl_common::DataPlaneType::EthernetDevice,
1411                },
1412                device: fidl_common::DeviceExtension {
1413                    is_synthetic: true,
1414                    mac_implementation_type: fidl_common::MacImplementationType::Softmac,
1415                    tx_status_report_supported: true,
1416                },
1417            }
1418        );
1419    }
1420
1421    #[fuchsia::test(allow_stalls = false)]
1422    async fn fake_device_returns_expected_security_support() {
1423        let (mut fake_device, _) = FakeDevice::new().await;
1424        let security_support = fake_device.security_support().await.unwrap();
1425        assert_eq!(
1426            security_support,
1427            fidl_common::SecuritySupport {
1428                mfp: fidl_common::MfpFeature { supported: false },
1429                sae: fidl_common::SaeFeature {
1430                    driver_handler_supported: false,
1431                    sme_handler_supported: false,
1432                },
1433            }
1434        );
1435    }
1436
1437    #[fuchsia::test(allow_stalls = false)]
1438    async fn fake_device_returns_expected_spectrum_management_support() {
1439        let (mut fake_device, _) = FakeDevice::new().await;
1440        let spectrum_management_support = fake_device.spectrum_management_support().await.unwrap();
1441        assert_eq!(
1442            spectrum_management_support,
1443            fidl_common::SpectrumManagementSupport {
1444                dfs: fidl_common::DfsFeature { supported: true },
1445            }
1446        );
1447    }
1448
1449    #[fuchsia::test(allow_stalls = false)]
1450    async fn test_can_dynamically_change_fake_device_state() {
1451        let (mut fake_device, fake_device_state) = FakeDevice::new_with_config(
1452            FakeDeviceConfig::default().with_mock_mac_role(fidl_common::WlanMacRole::Client),
1453        )
1454        .await;
1455        let query_response = fake_device.wlan_softmac_query_response().await.unwrap();
1456        assert_eq!(query_response.mac_role, Some(fidl_common::WlanMacRole::Client));
1457
1458        fake_device_state.lock().config =
1459            FakeDeviceConfig::default().with_mock_mac_role(fidl_common::WlanMacRole::Ap);
1460
1461        let query_response = fake_device.wlan_softmac_query_response().await.unwrap();
1462        assert_eq!(query_response.mac_role, Some(fidl_common::WlanMacRole::Ap));
1463    }
1464
1465    #[fuchsia::test(allow_stalls = false)]
1466    async fn send_mlme_message() {
1467        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1468        fake_device
1469            .send_mlme_event(fidl_mlme::MlmeEvent::DeauthenticateConf {
1470                resp: make_deauth_confirm_msg(),
1471            })
1472            .expect("error sending MLME message");
1473
1474        // Read message from channel.
1475        let msg = fake_device_state
1476            .lock()
1477            .next_mlme_msg::<fidl_mlme::DeauthenticateConfirm>()
1478            .expect("error reading message from channel");
1479        assert_eq!(msg, make_deauth_confirm_msg());
1480    }
1481
1482    #[fuchsia::test(allow_stalls = false)]
1483    async fn send_mlme_message_peer_already_closed() {
1484        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1485        fake_device_state.lock().mlme_event_stream.take();
1486
1487        fake_device
1488            .send_mlme_event(fidl_mlme::MlmeEvent::DeauthenticateConf {
1489                resp: make_deauth_confirm_msg(),
1490            })
1491            .expect_err("Mlme event should fail");
1492    }
1493
1494    #[fuchsia::test(allow_stalls = false)]
1495    async fn fake_device_deliver_eth_frame() {
1496        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1497        assert_eq!(fake_device_state.lock().eth_queue.len(), 0);
1498        let first_frame = [5; 32];
1499        let second_frame = [6; 32];
1500        assert_eq!(fake_device.deliver_eth_frame(&first_frame[..]), Ok(()));
1501        assert_eq!(fake_device.deliver_eth_frame(&second_frame[..]), Ok(()));
1502        assert_eq!(fake_device_state.lock().eth_queue.len(), 2);
1503        assert_eq!(&fake_device_state.lock().eth_queue[0], &first_frame);
1504        assert_eq!(&fake_device_state.lock().eth_queue[1], &second_frame);
1505    }
1506
1507    #[fuchsia::test(allow_stalls = false)]
1508    async fn set_channel() {
1509        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1510        fake_device
1511            .set_channel(fidl_ieee80211::WlanChannel {
1512                primary: 2,
1513                cbw: fidl_ieee80211::ChannelBandwidth::Cbw80P80,
1514                secondary80: 4,
1515            })
1516            .await
1517            .expect("set_channel failed?");
1518        // Check the internal state.
1519        assert_eq!(
1520            fake_device_state.lock().wlan_channel,
1521            fidl_ieee80211::WlanChannel {
1522                primary: 2,
1523                cbw: fidl_ieee80211::ChannelBandwidth::Cbw80P80,
1524                secondary80: 4
1525            }
1526        );
1527    }
1528
1529    #[fuchsia::test(allow_stalls = false)]
1530    async fn install_key() {
1531        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1532        fake_device
1533            .install_key(&fidl_softmac::WlanKeyConfiguration {
1534                protection: Some(fidl_softmac::WlanProtection::None),
1535                cipher_oui: Some([3, 4, 5]),
1536                cipher_type: Some(6),
1537                key_type: Some(fidl_ieee80211::KeyType::Pairwise),
1538                peer_addr: Some([8; 6]),
1539                key_idx: Some(9),
1540                key: Some(vec![11; 32]),
1541                rsc: Some(12),
1542                ..Default::default()
1543            })
1544            .await
1545            .expect("error setting key");
1546        assert_eq!(fake_device_state.lock().keys.len(), 1);
1547    }
1548
1549    #[fuchsia::test(allow_stalls = false)]
1550    async fn start_passive_scan() {
1551        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1552
1553        let result = fake_device
1554            .start_passive_scan(&fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest {
1555                channels: Some(vec![1u8, 2, 3]),
1556                min_channel_time: Some(zx::MonotonicDuration::from_millis(0).into_nanos()),
1557                max_channel_time: Some(zx::MonotonicDuration::from_millis(200).into_nanos()),
1558                min_home_time: Some(0),
1559                ..Default::default()
1560            })
1561            .await;
1562        assert!(result.is_ok());
1563
1564        assert_eq!(
1565            fake_device_state.lock().captured_passive_scan_request,
1566            Some(fidl_softmac::WlanSoftmacBaseStartPassiveScanRequest {
1567                channels: Some(vec![1, 2, 3]),
1568                min_channel_time: Some(0),
1569                max_channel_time: Some(200_000_000),
1570                min_home_time: Some(0),
1571                ..Default::default()
1572            }),
1573        );
1574    }
1575
1576    #[fuchsia::test(allow_stalls = false)]
1577    async fn start_active_scan() {
1578        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1579
1580        let result = fake_device
1581            .start_active_scan(&fidl_softmac::WlanSoftmacStartActiveScanRequest {
1582                channels: Some(vec![1u8, 2, 3]),
1583                ssids: Some(vec![
1584                    ddk_converter::cssid_from_ssid_unchecked(
1585                        &Ssid::try_from("foo").unwrap().into(),
1586                    ),
1587                    ddk_converter::cssid_from_ssid_unchecked(
1588                        &Ssid::try_from("bar").unwrap().into(),
1589                    ),
1590                ]),
1591                mac_header: Some(vec![
1592                    0x40u8, 0x00, // Frame Control
1593                    0x00, 0x00, // Duration
1594                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Address 1
1595                    0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // Address 2
1596                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Address 3
1597                    0x70, 0xdc, // Sequence Control
1598                ]),
1599                ies: Some(vec![
1600                    0x01u8, // Element ID for Supported Rates
1601                    0x08,   // Length
1602                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Supported Rates
1603                ]),
1604                min_channel_time: Some(zx::MonotonicDuration::from_millis(0).into_nanos()),
1605                max_channel_time: Some(zx::MonotonicDuration::from_millis(200).into_nanos()),
1606                min_home_time: Some(0),
1607                min_probes_per_channel: Some(1),
1608                max_probes_per_channel: Some(3),
1609                ..Default::default()
1610            })
1611            .await;
1612        assert!(result.is_ok());
1613        assert_eq!(
1614            fake_device_state.lock().captured_active_scan_request,
1615            Some(fidl_softmac::WlanSoftmacStartActiveScanRequest {
1616                channels: Some(vec![1, 2, 3]),
1617                ssids: Some(vec![
1618                    ddk_converter::cssid_from_ssid_unchecked(
1619                        &Ssid::try_from("foo").unwrap().into()
1620                    ),
1621                    ddk_converter::cssid_from_ssid_unchecked(
1622                        &Ssid::try_from("bar").unwrap().into()
1623                    ),
1624                ]),
1625                mac_header: Some(vec![
1626                    0x40, 0x00, // Frame Control
1627                    0x00, 0x00, // Duration
1628                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Address 1
1629                    0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // Address 2
1630                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Address 3
1631                    0x70, 0xdc, // Sequence Control
1632                ]),
1633                ies: Some(vec![
1634                    0x01, // Element ID for Supported Rates
1635                    0x08, // Length
1636                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 // Supported Rates
1637                ]),
1638                min_channel_time: Some(0),
1639                max_channel_time: Some(200_000_000),
1640                min_home_time: Some(0),
1641                min_probes_per_channel: Some(1),
1642                max_probes_per_channel: Some(3),
1643                ..Default::default()
1644            }),
1645            "No active scan argument available."
1646        );
1647    }
1648
1649    #[fuchsia::test(allow_stalls = false)]
1650    async fn join_bss() {
1651        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1652        fake_device
1653            .join_bss(&fidl_common::JoinBssRequest {
1654                bssid: Some([1, 2, 3, 4, 5, 6]),
1655                bss_type: Some(fidl_common::BssType::Personal),
1656                remote: Some(true),
1657                beacon_period: Some(100),
1658                ..Default::default()
1659            })
1660            .await
1661            .expect("error configuring bss");
1662        assert!(fake_device_state.lock().join_bss_request.is_some());
1663    }
1664
1665    #[fuchsia::test(allow_stalls = false)]
1666    async fn enable_disable_beaconing() {
1667        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1668        let arena = Arena::new();
1669        let mut buffer = arena.insert_default_slice::<u8>(4);
1670        buffer.copy_from_slice(&[1, 2, 3, 4][..]);
1671        let mac_frame = buffer.to_vec();
1672
1673        fake_device
1674            .enable_beaconing(fidl_softmac::WlanSoftmacBaseEnableBeaconingRequest {
1675                packet_template: Some(fidl_softmac::WlanTxPacket::template(mac_frame)),
1676                tim_ele_offset: Some(1),
1677                beacon_interval: Some(2),
1678                ..Default::default()
1679            })
1680            .await
1681            .expect("error enabling beaconing");
1682        assert_matches!(
1683        fake_device_state.lock().beacon_config.as_ref(),
1684        Some((buffer, tim_ele_offset, beacon_interval)) => {
1685            assert_eq!(&buffer[..], &[1, 2, 3, 4][..]);
1686            assert_eq!(*tim_ele_offset, 1);
1687            assert_eq!(*beacon_interval, TimeUnit(2));
1688        });
1689        fake_device.disable_beaconing().await.expect("error disabling beaconing");
1690        assert_matches!(fake_device_state.lock().beacon_config.as_ref(), None);
1691    }
1692
1693    #[fuchsia::test(allow_stalls = false)]
1694    async fn set_ethernet_status() {
1695        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1696        fake_device.set_ethernet_up().await.expect("failed setting status");
1697        assert_eq!(fake_device_state.lock().link_status, LinkStatus::UP);
1698
1699        fake_device.set_ethernet_down().await.expect("failed setting status");
1700        assert_eq!(fake_device_state.lock().link_status, LinkStatus::DOWN);
1701    }
1702
1703    #[fuchsia::test(allow_stalls = false)]
1704    async fn notify_association_complete() {
1705        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1706        fake_device
1707            .notify_association_complete(fidl_softmac::WlanAssociationConfig {
1708                bssid: Some([1, 2, 3, 4, 5, 6]),
1709                aid: Some(1),
1710                listen_interval: Some(2),
1711                channel: Some(fidl_ieee80211::WlanChannel {
1712                    primary: 3,
1713                    cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
1714                    secondary80: 0,
1715                }),
1716                qos: Some(false),
1717                wmm_params: None,
1718                rates: None,
1719                capability_info: Some(0x0102),
1720                ht_cap: None,
1721                ht_op: None,
1722                vht_cap: None,
1723                vht_op: None,
1724                ..Default::default()
1725            })
1726            .await
1727            .expect("error configuring assoc");
1728        assert!(fake_device_state.lock().assocs.contains_key(&[1, 2, 3, 4, 5, 6].into()));
1729    }
1730
1731    #[fuchsia::test(allow_stalls = false)]
1732    async fn clear_association() {
1733        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1734        fake_device
1735            .join_bss(&fidl_common::JoinBssRequest {
1736                bssid: Some([1, 2, 3, 4, 5, 6]),
1737                bss_type: Some(fidl_common::BssType::Personal),
1738                remote: Some(true),
1739                beacon_period: Some(100),
1740                ..Default::default()
1741            })
1742            .await
1743            .expect("error configuring bss");
1744
1745        let assoc_cfg = fidl_softmac::WlanAssociationConfig {
1746            bssid: Some([1, 2, 3, 4, 5, 6]),
1747            aid: Some(1),
1748            channel: Some(fidl_ieee80211::WlanChannel {
1749                primary: 149,
1750                cbw: fidl_ieee80211::ChannelBandwidth::Cbw40,
1751                secondary80: 42,
1752            }),
1753            ..Default::default()
1754        };
1755
1756        assert!(fake_device_state.lock().join_bss_request.is_some());
1757        fake_device.notify_association_complete(assoc_cfg).await.expect("error configuring assoc");
1758        assert_eq!(fake_device_state.lock().assocs.len(), 1);
1759        fake_device
1760            .clear_association(&fidl_softmac::WlanSoftmacBaseClearAssociationRequest {
1761                peer_addr: Some([1, 2, 3, 4, 5, 6]),
1762                ..Default::default()
1763            })
1764            .await
1765            .expect("error clearing assoc");
1766        assert_eq!(fake_device_state.lock().assocs.len(), 0);
1767        assert!(fake_device_state.lock().join_bss_request.is_none());
1768    }
1769
1770    #[fuchsia::test(allow_stalls = false)]
1771    async fn fake_device_captures_update_wmm_parameters_request() {
1772        let (mut fake_device, fake_device_state) = FakeDevice::new().await;
1773
1774        let request = fidl_softmac::WlanSoftmacBaseUpdateWmmParametersRequest {
1775            ac: Some(fidl_ieee80211::WlanAccessCategory::Background),
1776            params: Some(fidl_common::WlanWmmParameters {
1777                apsd: true,
1778                ac_be_params: fidl_common::WlanWmmAccessCategoryParameters {
1779                    ecw_min: 10,
1780                    ecw_max: 100,
1781                    aifsn: 1,
1782                    txop_limit: 5,
1783                    acm: true,
1784                },
1785                ac_bk_params: fidl_common::WlanWmmAccessCategoryParameters {
1786                    ecw_min: 11,
1787                    ecw_max: 100,
1788                    aifsn: 1,
1789                    txop_limit: 5,
1790                    acm: true,
1791                },
1792                ac_vi_params: fidl_common::WlanWmmAccessCategoryParameters {
1793                    ecw_min: 12,
1794                    ecw_max: 100,
1795                    aifsn: 1,
1796                    txop_limit: 5,
1797                    acm: true,
1798                },
1799                ac_vo_params: fidl_common::WlanWmmAccessCategoryParameters {
1800                    ecw_min: 13,
1801                    ecw_max: 100,
1802                    aifsn: 1,
1803                    txop_limit: 5,
1804                    acm: true,
1805                },
1806            }),
1807            ..Default::default()
1808        };
1809        let result = fake_device.update_wmm_parameters(&request).await;
1810        assert!(result.is_ok());
1811
1812        assert_eq!(fake_device_state.lock().captured_update_wmm_parameters_request, Some(request),);
1813    }
1814}