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