Skip to main content

wlan_mlme/
device.rs

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