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