Skip to main content

wlan_mlme/
lib.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
5//! This crate implements IEEE Std 802.11-2016 MLME as a library for hardware that supports
6//! SoftMAC. This is distinct from FullMAC, which is implemented by drivers and firmware. The
7//! implementation is broadly divided between client and AP stations, with some shared components
8//! and state machine infrastructure. See the [`client`] and [`ap`] modules.
9//!
10//! [`ap`]: crate::ap
11//! [`client`]: crate::client
12
13mod akm_algorithm;
14pub mod ap;
15pub mod auth;
16mod block_ack;
17pub mod client;
18mod ddk_converter;
19pub mod device;
20pub mod disconnect;
21pub mod error;
22mod minstrel;
23mod probe_sequence;
24
25use anyhow::{Error, bail, format_err};
26pub use ddk_converter::*;
27use device::DeviceOps;
28use fidl_fuchsia_wlan_common as fidl_common;
29pub use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
30use fidl_fuchsia_wlan_softmac as fidl_softmac;
31use fuchsia_sync::Mutex;
32use fuchsia_trace as trace;
33use futures::channel::mpsc::{self, TrySendError};
34use futures::channel::oneshot;
35use futures::{Future, StreamExt, select};
36use log::info;
37use std::sync::Arc;
38use std::time::Duration;
39use std::{cmp, fmt};
40pub use wlan_common as common;
41use wlan_ffi_transport::{EthernetTxEvent, EthernetTxEventSender, WlanRxEvent, WlanRxEventSender};
42use wlan_fidl_ext::{ResponderExt, SendResultExt};
43use wlan_trace as wtrace;
44
45// TODO(https://fxbug.dev/42084990): This trait is migratory and reads both newer and deprecated fields that
46//                         encode the same information (and prioritizes the newer fields). Remove
47//                         this trait and directly access fields once the deprecated fields for
48//                         basic rates and operating channels are removed from the SoftMAC FIDL
49//                         APIs and the platform version for WLAN is bumped to or beyond the
50//                         removal.
51// These extension methods cannot enforce that client code reads fields in a manner that is
52// backwards-compatible, but in exchange churn is greatly reduced (compared to the introduction of
53// additional types, for example).
54/// SDK backwards-compatiblity extensions for band capabilitites.
55trait WlanSoftmacBandCapabilityExt {
56    /// Gets supported basic rates with SDK backwards-compatibility.
57    fn basic_rates(&self) -> Option<&[u8]>;
58
59    /// Gets supported operating channels with SDK backwards-compatibility.
60    fn operating_channels(&self) -> Option<&[u8]>;
61}
62
63impl WlanSoftmacBandCapabilityExt for fidl_softmac::WlanSoftmacBandCapability {
64    fn basic_rates(&self) -> Option<&[u8]> {
65        match (&self.basic_rates, (&self.basic_rate_count, &self.basic_rate_list)) {
66            // Prefer the newer `basic_rates` field in the SoftMAC FIDL API.
67            (Some(basic_rates), _) => Some(basic_rates),
68            (None, (Some(n), Some(basic_rates))) => {
69                Some(&basic_rates[..cmp::min(usize::from(*n), basic_rates.len())])
70            }
71            _ => None,
72        }
73    }
74
75    fn operating_channels(&self) -> Option<&[u8]> {
76        match (
77            &self.operating_channels,
78            (&self.operating_channel_count, &self.operating_channel_list),
79        ) {
80            // Prefer the newer `operating_channels` field in the SoftMAC FIDL API.
81            (Some(operating_channels), _) => Some(operating_channels),
82            (None, (Some(n), Some(operating_channels))) => {
83                Some(&operating_channels[..cmp::min(usize::from(*n), operating_channels.len())])
84            }
85            _ => None,
86        }
87    }
88}
89
90trait WlanTxPacketExt {
91    fn template(mac_frame: Vec<u8>) -> Self;
92}
93
94impl WlanTxPacketExt for fidl_softmac::WlanTxPacket {
95    fn template(mac_frame: Vec<u8>) -> Self {
96        fidl_softmac::WlanTxPacket {
97            mac_frame,
98            // TODO(https://fxbug.dev/42056823): At time of writing, this field is ignored by the `iwlwifi`
99            //                         vendor driver (the only one other than the tap driver used
100            //                         for testing). The data used here is meaningless.
101            info: fidl_softmac::WlanTxInfo {
102                tx_flags: 0,
103                valid_fields: 0,
104                tx_vector_idx: 0,
105                phy: fidl_ieee80211::WlanPhyType::Dsss,
106                channel_bandwidth: fidl_ieee80211::ChannelBandwidth::Cbw20,
107                mcs: 0,
108            },
109        }
110    }
111}
112
113pub trait MlmeImpl {
114    type Config;
115    type Device: DeviceOps;
116    type TimerEvent;
117    fn new(
118        config: Self::Config,
119        device: Self::Device,
120        scheduler: common::timer::Timer<Self::TimerEvent>,
121    ) -> impl Future<Output = Result<Self, Error>>
122    where
123        Self: Sized;
124    fn handle_mlme_request(
125        &mut self,
126        msg: wlan_sme::MlmeRequest,
127    ) -> impl Future<Output = Result<(), Error>>;
128    fn handle_mac_frame_rx(
129        &mut self,
130        bytes: &[u8],
131        rx_info: fidl_softmac::WlanRxInfo,
132        async_id: trace::Id,
133    ) -> impl Future<Output = ()>;
134    fn handle_eth_frame_tx(&mut self, bytes: &[u8], async_id: trace::Id) -> Result<(), Error>;
135    fn handle_scan_complete(
136        &mut self,
137        status: zx::Status,
138        scan_id: u64,
139    ) -> impl Future<Output = ()>;
140    fn handle_timeout(&mut self, event: Self::TimerEvent) -> impl Future<Output = ()>;
141    fn access_device(&mut self) -> &mut Self::Device;
142}
143
144pub struct MinstrelTimer {
145    timer: wlan_common::timer::Timer<()>,
146    current_timer: Option<common::timer::EventHandle>,
147}
148
149impl minstrel::TimerManager for MinstrelTimer {
150    fn schedule(&mut self, from_now: Duration) {
151        self.current_timer.replace(self.timer.schedule_after(from_now.into(), ()));
152    }
153    fn cancel(&mut self) {
154        self.current_timer.take();
155    }
156}
157
158type MinstrelWrapper = Arc<Mutex<minstrel::MinstrelRateSelector<MinstrelTimer>>>;
159
160// DriverEventSink is used by other devices to interact with our main loop thread. All
161// events from our ethernet device or vendor device are converted to DriverEvents
162// and sent through this sink, where they can then be handled serially. Multiple copies of
163// DriverEventSink may be safely passed between threads, including one that is used by our
164// vendor driver as the context for wlan_softmac_ifc_protocol_ops.
165#[derive(Clone)]
166pub struct DriverEventSink(mpsc::UnboundedSender<DriverEvent>);
167
168impl DriverEventSink {
169    pub fn new() -> (Self, mpsc::UnboundedReceiver<DriverEvent>) {
170        let (sink, stream) = mpsc::unbounded();
171        (Self(sink), stream)
172    }
173
174    pub fn unbounded_send(
175        &self,
176        driver_event: DriverEvent,
177    ) -> Result<(), TrySendError<DriverEvent>> {
178        self.0.unbounded_send(driver_event)
179    }
180
181    pub fn disconnect(&mut self) {
182        self.0.disconnect()
183    }
184
185    pub fn unbounded_send_or_respond<R>(
186        &self,
187        driver_event: DriverEvent,
188        responder: R,
189        response: R::Response<'_>,
190    ) -> Result<R, anyhow::Error>
191    where
192        R: ResponderExt,
193    {
194        match self.unbounded_send(driver_event) {
195            Err(e) => {
196                let error_string = e.to_string();
197                let event = e.into_inner();
198                let e = format_err!("Failed to queue {}: {}", event, error_string);
199
200                match responder.send(response).format_send_err() {
201                    Ok(()) => Err(e),
202                    Err(send_error) => Err(send_error.context(e)),
203                }
204            }
205            Ok(()) => Ok(responder),
206        }
207    }
208}
209
210impl EthernetTxEventSender for DriverEventSink {
211    fn unbounded_send(&self, event: EthernetTxEvent) -> Result<(), (String, EthernetTxEvent)> {
212        DriverEventSink::unbounded_send(self, DriverEvent::EthernetTxEvent(event)).map_err(|e| {
213            if let (error, DriverEvent::EthernetTxEvent(event)) =
214                (format!("{:?}", e), e.into_inner())
215            {
216                (error, event)
217            } else {
218                unreachable!();
219            }
220        })
221    }
222}
223
224impl WlanRxEventSender for DriverEventSink {
225    fn unbounded_send(&self, event: WlanRxEvent) -> Result<(), (String, WlanRxEvent)> {
226        DriverEventSink::unbounded_send(self, DriverEvent::WlanRxEvent(event)).map_err(|e| {
227            if let (error, DriverEvent::WlanRxEvent(event)) = (format!("{:?}", e), e.into_inner()) {
228                (error, event)
229            } else {
230                unreachable!();
231            }
232        })
233    }
234}
235
236pub enum DriverEvent {
237    // Indicates that the device is being removed and our main loop should exit.
238    Stop { responder: fidl_softmac::WlanSoftmacIfcBridgeStopBridgedDriverResponder },
239    // Reports a scan is complete.
240    ScanComplete { status: zx::Status, scan_id: u64 },
241    // Reports the result of an attempted frame transmission.
242    TxResultReport { tx_result: fidl_softmac::WlanTxResult },
243    EthernetTxEvent(EthernetTxEvent),
244    WlanRxEvent(WlanRxEvent),
245}
246
247impl fmt::Display for DriverEvent {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        write!(
250            f,
251            "{}",
252            match self {
253                DriverEvent::Stop { .. } => "Stop",
254                DriverEvent::ScanComplete { .. } => "ScanComplete",
255                DriverEvent::TxResultReport { .. } => "TxResultReport",
256                DriverEvent::EthernetTxEvent(EthernetTxEvent { .. }) => "EthernetTxEvent",
257                DriverEvent::WlanRxEvent(WlanRxEvent { .. }) => "WlanRxEvent",
258            }
259        )
260    }
261}
262
263// This Debug implementation intentionally only logs the event name to
264// avoid inadvertenaly logging sensitive content contained in the events
265// themselves, i.e., logging data contained in the MacFrameRx and EthFrameTx
266// events.
267impl fmt::Debug for DriverEvent {
268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269        write!(
270            f,
271            "{}",
272            match self {
273                DriverEvent::Stop { .. } => "Stop",
274                DriverEvent::ScanComplete { .. } => "ScanComplete",
275                DriverEvent::TxResultReport { .. } => "TxResultReport",
276                DriverEvent::EthernetTxEvent(EthernetTxEvent { .. }) => "EthernetTxEvent",
277                DriverEvent::WlanRxEvent(WlanRxEvent { .. }) => "WlanRxEvent",
278            }
279        )
280    }
281}
282
283fn should_enable_minstrel(mac_sublayer: &fidl_common::MacSublayerSupport) -> bool {
284    mac_sublayer
285        .device
286        .as_ref()
287        .and_then(|device| device.tx_status_report_supported)
288        .unwrap_or(false)
289        && !mac_sublayer
290            .rate_selection_offload
291            .as_ref()
292            .and_then(|selection| selection.supported)
293            .unwrap_or(false)
294}
295
296const MINSTREL_UPDATE_INTERVAL: std::time::Duration = std::time::Duration::from_millis(100);
297// Remedy for https://fxbug.dev/42162128 (https://fxbug.dev/42108316)
298// See |DATA_FRAME_INTERVAL_NANOS|
299// in //src/connectivity/wlan/testing/hw-sim/test/rate_selection/src/lib.rs
300// Ensure at least one probe frame (generated every 16 data frames)
301// in every cycle:
302// 16 <= (MINSTREL_UPDATE_INTERVAL_HW_SIM / MINSTREL_DATA_FRAME_INTERVAL_NANOS * 1e6) < 32.
303const MINSTREL_UPDATE_INTERVAL_HW_SIM: std::time::Duration = std::time::Duration::from_millis(83);
304
305pub async fn mlme_main_loop<T: MlmeImpl>(
306    init_sender: oneshot::Sender<()>,
307    config: T::Config,
308    mut device: T::Device,
309    mlme_request_stream: mpsc::UnboundedReceiver<wlan_sme::MlmeRequest>,
310    driver_event_stream: mpsc::UnboundedReceiver<DriverEvent>,
311) -> Result<(), Error> {
312    info!("Starting MLME main loop...");
313    let (minstrel_timer, minstrel_time_stream) = common::timer::create_timer();
314    let minstrel = device.mac_sublayer_support().await.ok().filter(should_enable_minstrel).map(
315        |mac_sublayer_support| {
316            let minstrel = Arc::new(Mutex::new(minstrel::MinstrelRateSelector::new(
317                MinstrelTimer { timer: minstrel_timer, current_timer: None },
318                if mac_sublayer_support
319                    .device
320                    .and_then(|device| device.is_synthetic)
321                    .unwrap_or(false)
322                {
323                    MINSTREL_UPDATE_INTERVAL_HW_SIM
324                } else {
325                    MINSTREL_UPDATE_INTERVAL
326                },
327                probe_sequence::ProbeSequence::random_new(),
328            )));
329            device.set_minstrel(minstrel.clone());
330            minstrel
331        },
332    );
333    let (timer, time_stream) = common::timer::create_timer();
334
335    // Failure to create MLME likely indicates a problem querying the device. There is no recovery
336    // path if this occurs.
337    let mlme_impl = T::new(config, device, timer).await.expect("Failed to create MLME.");
338
339    info!("MLME initialization complete!");
340    init_sender.send(()).map_err(|_| format_err!("Failed to signal init complete."))?;
341
342    main_loop_impl(
343        mlme_impl,
344        minstrel,
345        mlme_request_stream,
346        driver_event_stream,
347        time_stream,
348        minstrel_time_stream,
349    )
350    .await
351}
352
353/// Begin processing MLME events.
354/// Does not return until iface destruction is requested via DriverEvent::Stop, unless
355/// a critical error occurs. Note that MlmeHandle::stop will work in either case.
356async fn main_loop_impl<T: MlmeImpl>(
357    mut mlme_impl: T,
358    minstrel: Option<MinstrelWrapper>,
359    // A stream of requests coming from the parent SME of this MLME.
360    mut mlme_request_stream: mpsc::UnboundedReceiver<wlan_sme::MlmeRequest>,
361    // A stream of events initiated by C++ device drivers and then buffered here
362    // by our MlmeHandle.
363    mut driver_event_stream: mpsc::UnboundedReceiver<DriverEvent>,
364    time_stream: common::timer::EventStream<T::TimerEvent>,
365    minstrel_time_stream: common::timer::EventStream<()>,
366) -> Result<(), Error> {
367    let mut timer_stream = common::timer::make_async_timed_event_stream(time_stream).fuse();
368    let mut minstrel_timer_stream =
369        common::timer::make_async_timed_event_stream(minstrel_time_stream).fuse();
370
371    loop {
372        select! {
373            // Process requests from SME.
374            mlme_request = mlme_request_stream.next() => match mlme_request {
375                Some(req) => {
376                    let method_name = req.name();
377                    if let Err(e) = mlme_impl.handle_mlme_request(req).await {
378                        info!("Failed to handle mlme {} request: {}", method_name, e);
379                    }
380                },
381                None => bail!("MLME request stream terminated unexpectedly."),
382            },
383            // Process requests from our C++ drivers.
384            driver_event = driver_event_stream.next() => match driver_event {
385                Some(event) => match event {
386                    // DriverEvent::Stop indicates a safe shutdown.
387                    DriverEvent::Stop {responder} => {
388                        responder.send().format_send_err_with_context("Stop")?;
389                        return Ok(())
390                    },
391                    DriverEvent::ScanComplete { status, scan_id } => {
392                        mlme_impl.handle_scan_complete(status, scan_id).await
393                    },
394                    DriverEvent::TxResultReport { tx_result } => {
395                        if let Some(minstrel) = minstrel.as_ref() {
396                            minstrel.lock().handle_tx_result_report(&tx_result)
397                        }
398                    }
399                    DriverEvent::EthernetTxEvent(EthernetTxEvent { bytes, async_id, borrowed_operation }) => {
400                        wtrace::duration!("DriverEvent::EthernetTxEvent");
401                        let bytes: &[u8] = unsafe { &*bytes.as_ptr() };
402                        match mlme_impl.handle_eth_frame_tx(&bytes[..], async_id) {
403                            Ok(()) => borrowed_operation.reply(Ok(())),
404                            Err(e) => {
405                                // TODO(https://fxbug.dev/42121991): Keep a counter of these failures.
406                                info!("Failed to handle eth frame: {}", e);
407                                wtrace::async_end_wlansoftmac_tx(async_id, zx::Status::INTERNAL);
408                                borrowed_operation.reply(Err(zx::Status::INTERNAL));
409                            }
410                        }
411                    }
412                    DriverEvent::WlanRxEvent(WlanRxEvent { bytes, rx_info, async_id }) => {
413                        wtrace::duration!("DriverEvent::WlanRxEvent");
414                        mlme_impl.handle_mac_frame_rx(&bytes[..], rx_info, async_id).await;
415                    }
416
417
418                },
419                None => bail!("Driver event stream terminated unexpectedly."),
420            },
421            timed_event = timer_stream.select_next_some() => {
422                mlme_impl.handle_timeout(timed_event.event).await;
423            }
424            _minstrel_timeout = minstrel_timer_stream.select_next_some() => {
425                if let Some(minstrel) = minstrel.as_ref() {
426                    minstrel.lock().handle_timeout()
427                }
428            }
429        }
430    }
431}
432
433#[cfg(test)]
434pub mod test_utils {
435    use super::*;
436    use crate::device::FakeDevice;
437    use fidl_fuchsia_wlan_mlme as fidl_mlme;
438    use ieee80211::{MacAddr, MacAddrBytes};
439    use wlan_common::channel;
440
441    pub struct FakeMlme {
442        device: FakeDevice,
443    }
444
445    impl MlmeImpl for FakeMlme {
446        type Config = ();
447        type Device = FakeDevice;
448        type TimerEvent = ();
449
450        async fn new(
451            _config: Self::Config,
452            device: Self::Device,
453            _scheduler: wlan_common::timer::Timer<Self::TimerEvent>,
454        ) -> Result<Self, Error> {
455            Ok(Self { device })
456        }
457
458        async fn handle_mlme_request(
459            &mut self,
460            _msg: wlan_sme::MlmeRequest,
461        ) -> Result<(), anyhow::Error> {
462            unimplemented!()
463        }
464
465        async fn handle_mac_frame_rx(
466            &mut self,
467            _bytes: &[u8],
468            _rx_info: fidl_softmac::WlanRxInfo,
469            _async_id: trace::Id,
470        ) {
471            unimplemented!()
472        }
473
474        fn handle_eth_frame_tx(
475            &mut self,
476            _bytes: &[u8],
477            _async_id: trace::Id,
478        ) -> Result<(), anyhow::Error> {
479            unimplemented!()
480        }
481
482        async fn handle_scan_complete(&mut self, _status: zx::Status, _scan_id: u64) {
483            unimplemented!()
484        }
485
486        async fn handle_timeout(&mut self, _event: Self::TimerEvent) {
487            unimplemented!()
488        }
489
490        fn access_device(&mut self) -> &mut Self::Device {
491            &mut self.device
492        }
493    }
494
495    pub(crate) fn fake_wlan_channel() -> channel::Channel {
496        channel::Channel { primary: 1, cbw: channel::Cbw::Cbw20 }
497    }
498
499    #[derive(Copy, Clone, Debug)]
500    pub struct MockWlanRxInfo {
501        pub rx_flags: fidl_softmac::WlanRxInfoFlags,
502        pub valid_fields: fidl_softmac::WlanRxInfoValid,
503        pub phy: fidl_ieee80211::WlanPhyType,
504        pub data_rate: u32,
505        pub channel: fidl_ieee80211::WlanChannel,
506        pub mcs: u8,
507        pub rssi_dbm: i8,
508        pub snr_dbh: i16,
509    }
510
511    impl MockWlanRxInfo {
512        pub(crate) fn with_channel(channel: fidl_ieee80211::WlanChannel) -> Self {
513            Self {
514                valid_fields: fidl_softmac::WlanRxInfoValid::CHAN_WIDTH
515                    | fidl_softmac::WlanRxInfoValid::RSSI
516                    | fidl_softmac::WlanRxInfoValid::SNR,
517                channel,
518                rssi_dbm: -40,
519                snr_dbh: 35,
520
521                // Default to 0 for these fields since there are no
522                // other reasonable values to mock.
523                rx_flags: fidl_softmac::WlanRxInfoFlags::empty(),
524                phy: fidl_ieee80211::WlanPhyType::Dsss,
525                data_rate: 0,
526                mcs: 0,
527            }
528        }
529    }
530
531    impl From<MockWlanRxInfo> for fidl_softmac::WlanRxInfo {
532        fn from(mock_rx_info: MockWlanRxInfo) -> fidl_softmac::WlanRxInfo {
533            fidl_softmac::WlanRxInfo {
534                rx_flags: mock_rx_info.rx_flags,
535                valid_fields: mock_rx_info.valid_fields,
536                phy: mock_rx_info.phy,
537                data_rate: mock_rx_info.data_rate,
538                channel: mock_rx_info.channel,
539                mcs: mock_rx_info.mcs,
540                rssi_dbm: mock_rx_info.rssi_dbm,
541                snr_dbh: mock_rx_info.snr_dbh,
542            }
543        }
544    }
545
546    pub(crate) fn fake_key(address: MacAddr) -> fidl_mlme::SetKeyDescriptor {
547        fidl_mlme::SetKeyDescriptor {
548            cipher_suite_oui: [1, 2, 3],
549            cipher_suite_type: fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4),
550            key_type: fidl_mlme::KeyType::Pairwise,
551            address: address.to_array(),
552            key_id: 6,
553            key: vec![1, 2, 3, 4, 5, 6, 7],
554            rsc: 8,
555        }
556    }
557
558    pub(crate) fn fake_set_keys_req(address: MacAddr) -> wlan_sme::MlmeRequest {
559        wlan_sme::MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest {
560            keylist: vec![fake_key(address)],
561        })
562    }
563}
564
565#[cfg(test)]
566mod tests {
567    use super::device::FakeDevice;
568    use super::test_utils::FakeMlme;
569    use super::*;
570    use assert_matches::assert_matches;
571    use fuchsia_async::TestExecutor;
572    use std::task::Poll;
573
574    // The following type definitions emulate the definition of FIDL requests and responder types.
575    // In addition to testing `unbounded_send_or_respond_with_error`, these tests demonstrate how
576    // `unbounded_send_or_respond_with_error` would be used in the context of a FIDL request.
577    //
578    // As such, the `Request` type is superfluous but provides a meaningful example for the reader.
579    enum Request {
580        Ax { responder: RequestAxResponder },
581        Cx { responder: RequestCxResponder },
582    }
583
584    struct RequestAxResponder {}
585    impl RequestAxResponder {
586        fn send(self) -> Result<(), fidl::Error> {
587            Ok(())
588        }
589    }
590
591    struct RequestCxResponder {}
592    impl RequestCxResponder {
593        fn send(self, _result: Result<u64, u64>) -> Result<(), fidl::Error> {
594            Ok(())
595        }
596    }
597
598    impl ResponderExt for RequestAxResponder {
599        type Response<'a> = ();
600        const REQUEST_NAME: &'static str = stringify!(RequestAx);
601
602        fn send(self, _: Self::Response<'_>) -> Result<(), fidl::Error> {
603            Self::send(self)
604        }
605    }
606
607    impl ResponderExt for RequestCxResponder {
608        type Response<'a> = Result<u64, u64>;
609        const REQUEST_NAME: &'static str = stringify!(RequestCx);
610
611        fn send(self, response: Self::Response<'_>) -> Result<(), fidl::Error> {
612            Self::send(self, response)
613        }
614    }
615
616    #[test]
617    fn unbounded_send_or_respond_with_error_simple() {
618        let (driver_event_sink, _driver_event_stream) = DriverEventSink::new();
619        if let Request::Ax { responder } = (Request::Ax { responder: RequestAxResponder {} }) {
620            let _responder: RequestAxResponder = driver_event_sink
621                .unbounded_send_or_respond(
622                    DriverEvent::ScanComplete { status: zx::Status::OK, scan_id: 3 },
623                    responder,
624                    (),
625                )
626                .unwrap();
627        }
628    }
629
630    #[test]
631    fn unbounded_send_or_respond_with_error_simple_with_error() {
632        let (driver_event_sink, _driver_event_stream) = DriverEventSink::new();
633        if let Request::Cx { responder } = (Request::Cx { responder: RequestCxResponder {} }) {
634            let _responder: RequestCxResponder = driver_event_sink
635                .unbounded_send_or_respond(
636                    DriverEvent::ScanComplete { status: zx::Status::IO_REFUSED, scan_id: 0 },
637                    responder,
638                    Err(10),
639                )
640                .unwrap();
641        }
642    }
643
644    #[fuchsia::test(allow_stalls = false)]
645    async fn start_and_stop_main_loop() {
646        let (fake_device, _fake_device_state) = FakeDevice::new().await;
647        let (device_sink, device_stream) = mpsc::unbounded();
648        let (_mlme_request_sink, mlme_request_stream) = mpsc::unbounded();
649        let (init_sender, mut init_receiver) = oneshot::channel();
650        let mut main_loop = Box::pin(mlme_main_loop::<FakeMlme>(
651            init_sender,
652            (),
653            fake_device,
654            mlme_request_stream,
655            device_stream,
656        ));
657        assert_matches!(TestExecutor::poll_until_stalled(&mut main_loop).await, Poll::Pending);
658        assert_eq!(TestExecutor::poll_until_stalled(&mut init_receiver).await, Poll::Ready(Ok(())));
659
660        // Create a `WlanSoftmacIfcBridge` proxy and stream in order to send a `StopBridgedDriver`
661        // message and extract its responder.
662        let (softmac_ifc_bridge_proxy, mut softmac_ifc_bridge_request_stream) =
663            fidl::endpoints::create_proxy_and_stream::<fidl_softmac::WlanSoftmacIfcBridgeMarker>();
664
665        let mut stop_response_fut = softmac_ifc_bridge_proxy.stop_bridged_driver();
666        assert_matches!(
667            TestExecutor::poll_until_stalled(&mut stop_response_fut).await,
668            Poll::Pending
669        );
670        let Some(Ok(fidl_softmac::WlanSoftmacIfcBridgeRequest::StopBridgedDriver { responder })) =
671            softmac_ifc_bridge_request_stream.next().await
672        else {
673            panic!("Did not receive StopBridgedDriver message");
674        };
675
676        device_sink
677            .unbounded_send(DriverEvent::Stop { responder })
678            .expect("Failed to send stop event");
679        assert_matches!(
680            TestExecutor::poll_until_stalled(&mut main_loop).await,
681            Poll::Ready(Ok(()))
682        );
683        assert_matches!(
684            TestExecutor::poll_until_stalled(&mut stop_response_fut).await,
685            Poll::Ready(Ok(()))
686        );
687        assert!(device_sink.is_closed());
688    }
689}