Skip to main content

wlan_mlme/
device.rs

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