wlan_mlme/
device.rs

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