Skip to main content

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