Skip to main content

wlan_fullmac_mlme/
device.rs

1// Copyright 2022 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 anyhow::{Context, format_err};
6use fidl::endpoints::ClientEnd;
7use fidl_fuchsia_wlan_common as fidl_common;
8use fidl_fuchsia_wlan_fullmac as fidl_fullmac;
9use fidl_fuchsia_wlan_mlme as fidl_mlme;
10use fidl_fuchsia_wlan_stats as fidl_stats;
11
12/// This trait abstracts how Device accomplish operations. Test code
13/// can then implement trait methods instead of mocking an underlying DeviceInterface
14/// and FIDL proxy.
15pub trait DeviceOps {
16    fn init(
17        &mut self,
18        fullmac_ifc_client_end: ClientEnd<fidl_fullmac::WlanFullmacImplIfcMarker>,
19    ) -> Result<fidl::Channel, zx::Status>;
20    fn query_device_info(&self) -> anyhow::Result<fidl_fullmac::WlanFullmacImplQueryResponse>;
21    fn query_security_support(&self) -> anyhow::Result<fidl_common::SecuritySupport>;
22    fn query_spectrum_management_support(
23        &self,
24    ) -> anyhow::Result<fidl_common::SpectrumManagementSupport>;
25    fn query_telemetry_support(&self) -> anyhow::Result<Result<fidl_stats::TelemetrySupport, i32>>;
26    fn query_apf_packet_filter_support(
27        &self,
28    ) -> anyhow::Result<Result<fidl_common::ApfPacketFilterSupport, i32>>;
29    fn start_scan(&self, req: fidl_fullmac::WlanFullmacImplStartScanRequest) -> anyhow::Result<()>;
30    fn connect(&self, req: fidl_fullmac::WlanFullmacImplConnectRequest) -> anyhow::Result<()>;
31    fn reconnect(&self, req: fidl_fullmac::WlanFullmacImplReconnectRequest) -> anyhow::Result<()>;
32    fn roam(&self, req: fidl_fullmac::WlanFullmacImplRoamRequest) -> anyhow::Result<()>;
33    fn auth_resp(&self, resp: fidl_fullmac::WlanFullmacImplAuthRespRequest) -> anyhow::Result<()>;
34    fn deauth(&self, req: fidl_fullmac::WlanFullmacImplDeauthRequest) -> anyhow::Result<()>;
35    fn assoc_resp(&self, resp: fidl_fullmac::WlanFullmacImplAssocRespRequest)
36    -> anyhow::Result<()>;
37    fn disassoc(&self, req: fidl_fullmac::WlanFullmacImplDisassocRequest) -> anyhow::Result<()>;
38    fn start_bss(&self, req: fidl_fullmac::WlanFullmacImplStartBssRequest) -> anyhow::Result<()>;
39    fn stop_bss(&self, req: fidl_fullmac::WlanFullmacImplStopBssRequest) -> anyhow::Result<()>;
40    fn set_keys(
41        &self,
42        req: fidl_fullmac::WlanFullmacImplSetKeysRequest,
43    ) -> anyhow::Result<fidl_fullmac::WlanFullmacSetKeysResp>;
44    fn eapol_tx(&self, req: fidl_fullmac::WlanFullmacImplEapolTxRequest) -> anyhow::Result<()>;
45    fn get_iface_stats(&self) -> anyhow::Result<fidl_mlme::GetIfaceStatsResponse>;
46    fn get_iface_histogram_stats(
47        &self,
48    ) -> anyhow::Result<fidl_mlme::GetIfaceHistogramStatsResponse>;
49    fn get_signal_report(&self) -> anyhow::Result<Result<fidl_stats::SignalReport, i32>>;
50    fn sae_handshake_resp(
51        &self,
52        resp: fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest,
53    ) -> anyhow::Result<()>;
54    fn sae_frame_tx(&self, frame: fidl_fullmac::SaeFrame) -> anyhow::Result<()>;
55    fn wmm_status_req(&self) -> anyhow::Result<()>;
56    fn on_link_state_changed(
57        &self,
58        req: fidl_fullmac::WlanFullmacImplOnLinkStateChangedRequest,
59    ) -> anyhow::Result<()>;
60    fn set_mac_address(
61        &self,
62        req: fidl_fullmac::WlanFullmacImplSetMacAddressRequest,
63    ) -> anyhow::Result<Result<(), i32>>;
64    fn install_apf_packet_filter(
65        &self,
66        req: fidl_fullmac::WlanFullmacImplInstallApfPacketFilterRequest,
67    ) -> anyhow::Result<Result<(), i32>>;
68    fn read_apf_packet_filter_data(
69        &self,
70    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplReadApfPacketFilterDataResponse, i32>>;
71    fn set_apf_packet_filter_enabled(
72        &self,
73        req: fidl_fullmac::WlanFullmacImplSetApfPacketFilterEnabledRequest,
74    ) -> anyhow::Result<Result<(), i32>>;
75    fn get_apf_packet_filter_enabled(
76        &self,
77    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplGetApfPacketFilterEnabledResponse, i32>>;
78    fn start_scheduled_scan(
79        &self,
80        req: fidl_fullmac::WlanFullmacImplStartScheduledScanRequest,
81    ) -> anyhow::Result<Result<(), i32>>;
82    fn stop_scheduled_scan(
83        &self,
84        req: fidl_fullmac::WlanFullmacImplStopScheduledScanRequest,
85    ) -> anyhow::Result<Result<(), i32>>;
86    fn get_scheduled_scan_enabled(
87        &self,
88    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplGetScheduledScanEnabledResponse, i32>>;
89}
90
91pub struct FullmacDevice {
92    fullmac_impl_sync_proxy: fidl_fullmac::WlanFullmacImpl_SynchronousProxy,
93}
94
95/// TODO(https://fxbug.dev/368323681): Users should be notified when the WlanFullmacImpl channel
96/// closes.
97impl FullmacDevice {
98    pub fn new(
99        fullmac_impl_sync_proxy: fidl_fullmac::WlanFullmacImpl_SynchronousProxy,
100    ) -> FullmacDevice {
101        FullmacDevice { fullmac_impl_sync_proxy }
102    }
103}
104
105impl DeviceOps for FullmacDevice {
106    fn init(
107        &mut self,
108        fullmac_ifc_client_end: ClientEnd<fidl_fullmac::WlanFullmacImplIfcMarker>,
109    ) -> Result<fidl::Channel, zx::Status> {
110        let req = fidl_fullmac::WlanFullmacImplInitRequest {
111            ifc: Some(fullmac_ifc_client_end),
112            ..Default::default()
113        };
114        let resp = self
115            .fullmac_impl_sync_proxy
116            .init(req, zx::MonotonicInstant::INFINITE)
117            .map_err(|e| {
118                log::error!("FIDL error on Start: {}", e);
119                zx::Status::INTERNAL
120            })?
121            .map_err(|e| zx::Status::from_raw(e))?;
122
123        resp.sme_channel.ok_or(zx::Status::INVALID_ARGS)
124    }
125
126    fn query_device_info(&self) -> anyhow::Result<fidl_fullmac::WlanFullmacImplQueryResponse> {
127        self.fullmac_impl_sync_proxy
128            .query(zx::MonotonicInstant::INFINITE)
129            .context("FIDL error on QueryDeviceInfo")?
130            .map_err(|e| format_err!("Driver returned error on QueryDeviceInfo: {}", e))
131    }
132
133    fn query_security_support(&self) -> anyhow::Result<fidl_common::SecuritySupport> {
134        self.fullmac_impl_sync_proxy
135            .query_security_support(zx::MonotonicInstant::INFINITE)
136            .context("FIDL error on QuerySecuritySupport")?
137            .map_err(|e| format_err!("Driver returned error on QuerySecuritySupport: {}", e))
138            .and_then(|support| {
139                support.resp.ok_or_else(|| {
140                    format_err!("Driver returned empty QuerySecuritySupport response")
141                })
142            })
143    }
144
145    fn query_spectrum_management_support(
146        &self,
147    ) -> anyhow::Result<fidl_common::SpectrumManagementSupport> {
148        self.fullmac_impl_sync_proxy
149            .query_spectrum_management_support(zx::MonotonicInstant::INFINITE)
150            .context("FIDL error on QuerySpectrumManagementSupport")?
151            .map_err(|e| {
152                format_err!("Driver returned error on QuerySpectrumManagementSupport: {}", e)
153            })
154            .and_then(|support| {
155                support.resp.ok_or_else(|| {
156                    format_err!("Driver returned empty QuerySpectrumManagementSupport response")
157                })
158            })
159    }
160
161    fn query_telemetry_support(&self) -> anyhow::Result<Result<fidl_stats::TelemetrySupport, i32>> {
162        self.fullmac_impl_sync_proxy
163            .query_telemetry_support(zx::MonotonicInstant::INFINITE)
164            .context("FIDL error on QueryTelemetrySupport")
165            .and_then(|support| match support {
166                Ok(response) => response
167                    .resp
168                    .ok_or_else(|| {
169                        format_err!("Driver returned empty QueryTelemetrySupport response")
170                    })
171                    .map(Ok),
172                Err(e) => Ok(Err(e)),
173            })
174    }
175
176    fn query_apf_packet_filter_support(
177        &self,
178    ) -> anyhow::Result<Result<fidl_common::ApfPacketFilterSupport, i32>> {
179        self.fullmac_impl_sync_proxy
180            .query_apf_packet_filter_support(zx::MonotonicInstant::INFINITE)
181            .context("FIDL error on QueryApfPacketFilterSupport")
182            .and_then(|support| match support {
183                Ok(response) => response
184                    .resp
185                    .ok_or_else(|| {
186                        format_err!("Driver returned empty QueryApfPacketFilterSupport response")
187                    })
188                    .map(Ok),
189                Err(e) => Ok(Err(e)),
190            })
191    }
192
193    fn start_scan(&self, req: fidl_fullmac::WlanFullmacImplStartScanRequest) -> anyhow::Result<()> {
194        self.fullmac_impl_sync_proxy
195            .start_scan(&req, zx::MonotonicInstant::INFINITE)
196            .context("FIDL error on StartScan")
197    }
198    fn connect(&self, req: fidl_fullmac::WlanFullmacImplConnectRequest) -> anyhow::Result<()> {
199        self.fullmac_impl_sync_proxy
200            .connect(&req, zx::MonotonicInstant::INFINITE)
201            .context("FIDL error on Connect")
202    }
203    fn reconnect(&self, req: fidl_fullmac::WlanFullmacImplReconnectRequest) -> anyhow::Result<()> {
204        self.fullmac_impl_sync_proxy
205            .reconnect(&req, zx::MonotonicInstant::INFINITE)
206            .context("FIDL error on Reconnect")
207    }
208    fn roam(&self, req: fidl_fullmac::WlanFullmacImplRoamRequest) -> anyhow::Result<()> {
209        self.fullmac_impl_sync_proxy
210            .roam(&req, zx::MonotonicInstant::INFINITE)
211            .context("FIDL error on Roam")
212    }
213    fn auth_resp(&self, resp: fidl_fullmac::WlanFullmacImplAuthRespRequest) -> anyhow::Result<()> {
214        self.fullmac_impl_sync_proxy
215            .auth_resp(&resp, zx::MonotonicInstant::INFINITE)
216            .context("FIDL error on AuthResp")
217    }
218    fn deauth(&self, req: fidl_fullmac::WlanFullmacImplDeauthRequest) -> anyhow::Result<()> {
219        self.fullmac_impl_sync_proxy
220            .deauth(&req, zx::MonotonicInstant::INFINITE)
221            .context("FIDL error on Deauth")
222    }
223    fn assoc_resp(
224        &self,
225        resp: fidl_fullmac::WlanFullmacImplAssocRespRequest,
226    ) -> anyhow::Result<()> {
227        self.fullmac_impl_sync_proxy
228            .assoc_resp(&resp, zx::MonotonicInstant::INFINITE)
229            .context("FIDL error on AssocResp")
230    }
231    fn disassoc(&self, req: fidl_fullmac::WlanFullmacImplDisassocRequest) -> anyhow::Result<()> {
232        self.fullmac_impl_sync_proxy
233            .disassoc(&req, zx::MonotonicInstant::INFINITE)
234            .context("FIDL error on Disassoc")
235    }
236    fn start_bss(&self, req: fidl_fullmac::WlanFullmacImplStartBssRequest) -> anyhow::Result<()> {
237        self.fullmac_impl_sync_proxy
238            .start_bss(&req, zx::MonotonicInstant::INFINITE)
239            .context("FIDL error on StartBss")
240    }
241    fn stop_bss(&self, req: fidl_fullmac::WlanFullmacImplStopBssRequest) -> anyhow::Result<()> {
242        self.fullmac_impl_sync_proxy
243            .stop_bss(&req, zx::MonotonicInstant::INFINITE)
244            .context("FIDL error on StopBss")
245    }
246    fn set_keys(
247        &self,
248        req: fidl_fullmac::WlanFullmacImplSetKeysRequest,
249    ) -> anyhow::Result<fidl_fullmac::WlanFullmacSetKeysResp> {
250        self.fullmac_impl_sync_proxy
251            .set_keys(&req, zx::MonotonicInstant::INFINITE)
252            .context("FIDL error on SetKeysReq")
253    }
254    fn eapol_tx(&self, req: fidl_fullmac::WlanFullmacImplEapolTxRequest) -> anyhow::Result<()> {
255        self.fullmac_impl_sync_proxy
256            .eapol_tx(&req, zx::MonotonicInstant::INFINITE)
257            .context("FIDL error on EapolTx")
258    }
259    fn get_iface_stats(&self) -> anyhow::Result<fidl_mlme::GetIfaceStatsResponse> {
260        match self
261            .fullmac_impl_sync_proxy
262            .get_iface_stats(zx::MonotonicInstant::INFINITE)
263            .context("FIDL error on GetIfaceStats")?
264        {
265            Ok(stats) => Ok(fidl_mlme::GetIfaceStatsResponse::Stats(stats)),
266            Err(e) => Ok(fidl_mlme::GetIfaceStatsResponse::ErrorStatus(e)),
267        }
268    }
269    fn get_iface_histogram_stats(
270        &self,
271    ) -> anyhow::Result<fidl_mlme::GetIfaceHistogramStatsResponse> {
272        match self
273            .fullmac_impl_sync_proxy
274            .get_iface_histogram_stats(zx::MonotonicInstant::INFINITE)
275            .context("FIDL error on GetIfaceHistogramStats")?
276        {
277            Ok(stats) => Ok(fidl_mlme::GetIfaceHistogramStatsResponse::Stats(stats)),
278            Err(e) => Ok(fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(e)),
279        }
280    }
281    fn get_signal_report(&self) -> anyhow::Result<Result<fidl_stats::SignalReport, i32>> {
282        self.fullmac_impl_sync_proxy
283            .get_signal_report(zx::MonotonicInstant::INFINITE)
284            .context("FIDL error on GetSignalReport")
285    }
286
287    fn sae_handshake_resp(
288        &self,
289        resp: fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest,
290    ) -> anyhow::Result<()> {
291        self.fullmac_impl_sync_proxy
292            .sae_handshake_resp(&resp, zx::MonotonicInstant::INFINITE)
293            .context("FIDL error on SaeHandshakeResp")
294    }
295    fn sae_frame_tx(&self, frame: fidl_fullmac::SaeFrame) -> anyhow::Result<()> {
296        self.fullmac_impl_sync_proxy
297            .sae_frame_tx(&frame, zx::MonotonicInstant::INFINITE)
298            .context("FIDL error on SaeFrameTx")
299    }
300    fn wmm_status_req(&self) -> anyhow::Result<()> {
301        self.fullmac_impl_sync_proxy
302            .wmm_status_req(zx::MonotonicInstant::INFINITE)
303            .context("FIDL error on WmmStatusReq")
304    }
305    fn on_link_state_changed(
306        &self,
307        req: fidl_fullmac::WlanFullmacImplOnLinkStateChangedRequest,
308    ) -> anyhow::Result<()> {
309        self.fullmac_impl_sync_proxy
310            .on_link_state_changed(&req, zx::MonotonicInstant::INFINITE)
311            .context("FIDL error on OnLinkStateChanged")
312    }
313    fn set_mac_address(
314        &self,
315        req: fidl_fuchsia_wlan_fullmac::WlanFullmacImplSetMacAddressRequest,
316    ) -> anyhow::Result<Result<(), i32>> {
317        self.fullmac_impl_sync_proxy
318            .set_mac_address(&req.mac_addr, zx::MonotonicInstant::INFINITE)
319            .context("FIDL error on SetMacAddress")
320    }
321
322    fn install_apf_packet_filter(
323        &self,
324        req: fidl_fullmac::WlanFullmacImplInstallApfPacketFilterRequest,
325    ) -> anyhow::Result<Result<(), i32>> {
326        self.fullmac_impl_sync_proxy
327            .install_apf_packet_filter(&req, zx::MonotonicInstant::INFINITE)
328            .context("FIDL error on InstallApfPacketFilter")
329    }
330
331    fn read_apf_packet_filter_data(
332        &self,
333    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplReadApfPacketFilterDataResponse, i32>>
334    {
335        self.fullmac_impl_sync_proxy
336            .read_apf_packet_filter_data(zx::MonotonicInstant::INFINITE)
337            .context("FIDL error on ReadApfPacketFilterData")
338    }
339
340    fn set_apf_packet_filter_enabled(
341        &self,
342        req: fidl_fullmac::WlanFullmacImplSetApfPacketFilterEnabledRequest,
343    ) -> anyhow::Result<Result<(), i32>> {
344        self.fullmac_impl_sync_proxy
345            .set_apf_packet_filter_enabled(&req, zx::MonotonicInstant::INFINITE)
346            .context("FIDL error on SetApfPacketFilterEnabled")
347    }
348
349    fn get_apf_packet_filter_enabled(
350        &self,
351    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplGetApfPacketFilterEnabledResponse, i32>>
352    {
353        self.fullmac_impl_sync_proxy
354            .get_apf_packet_filter_enabled(zx::MonotonicInstant::INFINITE)
355            .context("FIDL error on GetApfPacketFilterEnabled")
356    }
357
358    fn start_scheduled_scan(
359        &self,
360        req: fidl_fullmac::WlanFullmacImplStartScheduledScanRequest,
361    ) -> anyhow::Result<Result<(), i32>> {
362        self.fullmac_impl_sync_proxy
363            .start_scheduled_scan(&req, zx::MonotonicInstant::INFINITE)
364            .context("FIDL error on StartScheduledScan")
365    }
366
367    fn stop_scheduled_scan(
368        &self,
369        req: fidl_fullmac::WlanFullmacImplStopScheduledScanRequest,
370    ) -> anyhow::Result<Result<(), i32>> {
371        self.fullmac_impl_sync_proxy
372            .stop_scheduled_scan(&req, zx::MonotonicInstant::INFINITE)
373            .context("FIDL error on StopScheduledScan")
374    }
375
376    fn get_scheduled_scan_enabled(
377        &self,
378    ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplGetScheduledScanEnabledResponse, i32>>
379    {
380        self.fullmac_impl_sync_proxy
381            .get_scheduled_scan_enabled(zx::MonotonicInstant::INFINITE)
382            .context("FIDL error on GetScheduledScanEnabled")
383    }
384}
385
386#[cfg(test)]
387pub mod test_utils {
388    use super::*;
389    use fidl_fuchsia_wlan_sme as fidl_sme;
390    use fuchsia_sync::Mutex;
391    use futures::channel::mpsc;
392    use std::sync::Arc;
393    use wlan_common::sink::UnboundedSink;
394
395    #[derive(Debug)]
396    pub enum DriverCall {
397        StartScan {
398            req: fidl_fullmac::WlanFullmacImplStartScanRequest,
399        },
400        StartScheduledScan {
401            req: fidl_fullmac::WlanFullmacImplStartScheduledScanRequest,
402        },
403        StopScheduledScan {
404            req: fidl_fullmac::WlanFullmacImplStopScheduledScanRequest,
405        },
406        GetScheduledScanEnabled,
407        ConnectReq {
408            req: fidl_fullmac::WlanFullmacImplConnectRequest,
409        },
410        ReconnectReq {
411            req: fidl_fullmac::WlanFullmacImplReconnectRequest,
412        },
413        RoamReq {
414            req: fidl_fullmac::WlanFullmacImplRoamRequest,
415        },
416        AuthResp {
417            resp: fidl_fullmac::WlanFullmacImplAuthRespRequest,
418        },
419        DeauthReq {
420            req: fidl_fullmac::WlanFullmacImplDeauthRequest,
421        },
422        AssocResp {
423            resp: fidl_fullmac::WlanFullmacImplAssocRespRequest,
424        },
425        Disassoc {
426            req: fidl_fullmac::WlanFullmacImplDisassocRequest,
427        },
428        StartBss {
429            req: fidl_fullmac::WlanFullmacImplStartBssRequest,
430        },
431        StopBss {
432            req: fidl_fullmac::WlanFullmacImplStopBssRequest,
433        },
434        SetKeys {
435            req: fidl_fullmac::WlanFullmacImplSetKeysRequest,
436        },
437        EapolTx {
438            req: fidl_fullmac::WlanFullmacImplEapolTxRequest,
439        },
440        QueryTelemetrySupport,
441        QueryApfPacketFilterSupport,
442        GetIfaceStats,
443        GetIfaceHistogramStats,
444        GetSignalReport,
445        SaeHandshakeResp {
446            resp: fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest,
447        },
448        SaeFrameTx {
449            frame: fidl_fullmac::SaeFrame,
450        },
451        WmmStatusReq,
452        OnLinkStateChanged {
453            req: fidl_fullmac::WlanFullmacImplOnLinkStateChangedRequest,
454        },
455        SetMacAddress {
456            req: fidl_fullmac::WlanFullmacImplSetMacAddressRequest,
457        },
458        InstallApfPacketFilter {
459            req: fidl_fullmac::WlanFullmacImplInstallApfPacketFilterRequest,
460        },
461        ReadApfPacketFilterData,
462        SetApfPacketFilterEnabled {
463            req: fidl_fullmac::WlanFullmacImplSetApfPacketFilterEnabledRequest,
464        },
465        GetApfPacketFilterEnabled,
466    }
467
468    pub struct FakeFullmacDeviceMocks {
469        pub start_fn_status_mock: Option<zx::sys::zx_status_t>,
470
471        // Note: anyhow::Error isn't cloneable, so the query mocks are all optionals to make this
472        // easier to work with.
473        //
474        // If any of the query mocks are None, then an Err is returned from DeviceOps with an empty
475        // error message.
476        pub query_device_info_mock: Option<fidl_fullmac::WlanFullmacImplQueryResponse>,
477        pub query_security_support_mock: Option<fidl_common::SecuritySupport>,
478        pub query_spectrum_management_support_mock: Option<fidl_common::SpectrumManagementSupport>,
479        pub query_telemetry_support_mock: Option<Result<fidl_stats::TelemetrySupport, i32>>,
480        pub query_apf_packet_filter_support_mock:
481            Option<Result<fidl_common::ApfPacketFilterSupport, i32>>,
482
483        pub set_keys_resp_mock: Option<fidl_fullmac::WlanFullmacSetKeysResp>,
484        pub get_iface_stats_mock: Option<fidl_mlme::GetIfaceStatsResponse>,
485        pub get_iface_histogram_stats_mock: Option<fidl_mlme::GetIfaceHistogramStatsResponse>,
486        pub get_signal_report_mock: Option<Result<fidl_stats::SignalReport, i32>>,
487        pub install_apf_packet_filter_mock: Option<Result<(), i32>>,
488        pub read_apf_packet_filter_data_mock:
489            Option<Result<fidl_fullmac::WlanFullmacImplReadApfPacketFilterDataResponse, i32>>,
490        pub set_apf_packet_filter_enabled_mock: Option<Result<(), i32>>,
491        pub get_apf_packet_filter_enabled_mock:
492            Option<Result<fidl_fullmac::WlanFullmacImplGetApfPacketFilterEnabledResponse, i32>>,
493        pub get_scheduled_scan_enabled_mock:
494            Option<Result<fidl_fullmac::WlanFullmacImplGetScheduledScanEnabledResponse, i32>>,
495        pub fullmac_ifc_client_end: Option<ClientEnd<fidl_fullmac::WlanFullmacImplIfcMarker>>,
496    }
497
498    unsafe impl Send for FakeFullmacDevice {}
499    pub struct FakeFullmacDevice {
500        pub usme_bootstrap_client_end:
501            Option<fidl::endpoints::ClientEnd<fidl_sme::UsmeBootstrapMarker>>,
502        pub usme_bootstrap_server_end:
503            Option<fidl::endpoints::ServerEnd<fidl_sme::UsmeBootstrapMarker>>,
504        driver_call_sender: UnboundedSink<DriverCall>,
505
506        // This is boxed because tests want a reference to this to check captured calls, but in
507        // production we pass ownership of the DeviceOps to FullmacMlme. This avoids changing
508        // ownership semantics for tests.
509        pub mocks: Arc<Mutex<FakeFullmacDeviceMocks>>,
510    }
511
512    impl FakeFullmacDevice {
513        pub fn new() -> (Self, mpsc::UnboundedReceiver<DriverCall>) {
514            // Create a channel for SME requests, to be surfaced by init().
515            let (usme_bootstrap_client_end, usme_bootstrap_server_end) =
516                fidl::endpoints::create_endpoints::<fidl_sme::UsmeBootstrapMarker>();
517
518            let (driver_call_sender, driver_call_receiver) = mpsc::unbounded();
519
520            let device = Self {
521                usme_bootstrap_client_end: Some(usme_bootstrap_client_end),
522                usme_bootstrap_server_end: Some(usme_bootstrap_server_end),
523                driver_call_sender: UnboundedSink::new(driver_call_sender),
524                mocks: Arc::new(Mutex::new(FakeFullmacDeviceMocks {
525                    fullmac_ifc_client_end: None,
526                    start_fn_status_mock: None,
527                    query_device_info_mock: Some(fidl_fullmac::WlanFullmacImplQueryResponse {
528                        sta_addr: Some([0u8; 6]),
529                        factory_addr: Some([0u8; 6]),
530                        role: Some(fidl_common::WlanMacRole::Client),
531                        band_caps: Some(vec![]),
532                        ..Default::default()
533                    }),
534                    query_security_support_mock: Some(fidl_common::SecuritySupport {
535                        sae: Some(fidl_common::SaeFeature {
536                            driver_handler_supported: Some(false),
537                            sme_handler_supported: Some(true),
538                            hash_to_element_supported: Some(false),
539                            ..Default::default()
540                        }),
541                        mfp: Some(fidl_common::MfpFeature {
542                            supported: Some(false),
543                            ..Default::default()
544                        }),
545                        owe: Some(fidl_common::OweFeature {
546                            supported: Some(false),
547                            ..Default::default()
548                        }),
549                        ..Default::default()
550                    }),
551                    query_spectrum_management_support_mock: Some(
552                        fidl_common::SpectrumManagementSupport {
553                            dfs: Some(fidl_common::DfsFeature {
554                                supported: Some(false),
555                                ..Default::default()
556                            }),
557                            ..Default::default()
558                        },
559                    ),
560                    query_telemetry_support_mock: Some(Ok(fidl_stats::TelemetrySupport {
561                        ..Default::default()
562                    })),
563                    query_apf_packet_filter_support_mock: Some(Ok(
564                        fidl_common::ApfPacketFilterSupport {
565                            supported: Some(false),
566                            ..Default::default()
567                        },
568                    )),
569                    get_signal_report_mock: Some(Ok(fidl_stats::SignalReport {
570                        ..Default::default()
571                    })),
572                    set_keys_resp_mock: None,
573                    get_iface_stats_mock: None,
574                    get_iface_histogram_stats_mock: None,
575                    install_apf_packet_filter_mock: Some(Ok(())),
576                    read_apf_packet_filter_data_mock: Some(Ok(
577                        fidl_fullmac::WlanFullmacImplReadApfPacketFilterDataResponse {
578                            memory: Some(vec![]),
579                            ..Default::default()
580                        },
581                    )),
582                    set_apf_packet_filter_enabled_mock: Some(Ok(())),
583                    get_apf_packet_filter_enabled_mock: Some(Ok(
584                        fidl_fullmac::WlanFullmacImplGetApfPacketFilterEnabledResponse {
585                            enabled: Some(false),
586                            ..Default::default()
587                        },
588                    )),
589                    get_scheduled_scan_enabled_mock: Some(Ok(
590                        fidl_fullmac::WlanFullmacImplGetScheduledScanEnabledResponse {
591                            active_txn_ids: Some(vec![]),
592                            ..Default::default()
593                        },
594                    )),
595                })),
596            };
597
598            (device, driver_call_receiver)
599        }
600    }
601
602    impl DeviceOps for FakeFullmacDevice {
603        fn init(
604            &mut self,
605            fullmac_ifc_client_end: ClientEnd<fidl_fullmac::WlanFullmacImplIfcMarker>,
606        ) -> Result<fidl::Channel, zx::Status> {
607            let mut mocks = self.mocks.lock();
608
609            mocks.fullmac_ifc_client_end = Some(fullmac_ifc_client_end);
610            match mocks.start_fn_status_mock {
611                Some(status) => Err(zx::Status::from_raw(status)),
612
613                // Start can only be called once since this moves usme_bootstrap_server_end.
614                None => Ok(self.usme_bootstrap_server_end.take().unwrap().into_channel()),
615            }
616        }
617
618        fn query_device_info(&self) -> anyhow::Result<fidl_fullmac::WlanFullmacImplQueryResponse> {
619            self.mocks.lock().query_device_info_mock.clone().ok_or_else(|| format_err!(""))
620        }
621
622        fn query_security_support(&self) -> anyhow::Result<fidl_common::SecuritySupport> {
623            self.mocks.lock().query_security_support_mock.clone().ok_or_else(|| format_err!(""))
624        }
625
626        fn query_spectrum_management_support(
627            &self,
628        ) -> anyhow::Result<fidl_common::SpectrumManagementSupport> {
629            self.mocks
630                .lock()
631                .query_spectrum_management_support_mock
632                .clone()
633                .ok_or_else(|| format_err!(""))
634        }
635
636        fn query_telemetry_support(
637            &self,
638        ) -> anyhow::Result<Result<fidl_stats::TelemetrySupport, i32>> {
639            self.driver_call_sender.send(DriverCall::QueryTelemetrySupport);
640            self.mocks.lock().query_telemetry_support_mock.clone().ok_or_else(|| format_err!(""))
641        }
642
643        fn query_apf_packet_filter_support(
644            &self,
645        ) -> anyhow::Result<Result<fidl_common::ApfPacketFilterSupport, i32>> {
646            self.driver_call_sender.send(DriverCall::QueryApfPacketFilterSupport);
647            self.mocks.lock().query_apf_packet_filter_support_mock.clone().ok_or_else(|| {
648                format_err!("query_apf_packet_filter_support_mock is None in FakeFullmacDevice")
649            })
650        }
651
652        // Cannot mark fn unsafe because it has to match fn signature in FullDeviceInterface
653        fn start_scan(
654            &self,
655            req: fidl_fullmac::WlanFullmacImplStartScanRequest,
656        ) -> anyhow::Result<()> {
657            self.driver_call_sender.send(DriverCall::StartScan { req });
658            Ok(())
659        }
660
661        fn connect(&self, req: fidl_fullmac::WlanFullmacImplConnectRequest) -> anyhow::Result<()> {
662            self.driver_call_sender.send(DriverCall::ConnectReq { req });
663            Ok(())
664        }
665        fn reconnect(
666            &self,
667            req: fidl_fullmac::WlanFullmacImplReconnectRequest,
668        ) -> anyhow::Result<()> {
669            self.driver_call_sender.send(DriverCall::ReconnectReq { req });
670            Ok(())
671        }
672        fn roam(&self, req: fidl_fullmac::WlanFullmacImplRoamRequest) -> anyhow::Result<()> {
673            self.driver_call_sender.send(DriverCall::RoamReq { req });
674            Ok(())
675        }
676        fn auth_resp(
677            &self,
678            resp: fidl_fullmac::WlanFullmacImplAuthRespRequest,
679        ) -> anyhow::Result<()> {
680            self.driver_call_sender.send(DriverCall::AuthResp { resp });
681            Ok(())
682        }
683        fn deauth(&self, req: fidl_fullmac::WlanFullmacImplDeauthRequest) -> anyhow::Result<()> {
684            self.driver_call_sender.send(DriverCall::DeauthReq { req });
685            Ok(())
686        }
687        fn assoc_resp(
688            &self,
689            resp: fidl_fullmac::WlanFullmacImplAssocRespRequest,
690        ) -> anyhow::Result<()> {
691            self.driver_call_sender.send(DriverCall::AssocResp { resp });
692            Ok(())
693        }
694        fn disassoc(
695            &self,
696            req: fidl_fullmac::WlanFullmacImplDisassocRequest,
697        ) -> anyhow::Result<()> {
698            self.driver_call_sender.send(DriverCall::Disassoc { req });
699            Ok(())
700        }
701        fn start_bss(
702            &self,
703            req: fidl_fullmac::WlanFullmacImplStartBssRequest,
704        ) -> anyhow::Result<()> {
705            self.driver_call_sender.send(DriverCall::StartBss { req });
706            Ok(())
707        }
708        fn stop_bss(&self, req: fidl_fullmac::WlanFullmacImplStopBssRequest) -> anyhow::Result<()> {
709            self.driver_call_sender.send(DriverCall::StopBss { req });
710            Ok(())
711        }
712        fn set_keys(
713            &self,
714            req: fidl_fullmac::WlanFullmacImplSetKeysRequest,
715        ) -> anyhow::Result<fidl_fullmac::WlanFullmacSetKeysResp> {
716            let num_keys = req.keylist.as_ref().unwrap().len();
717            self.driver_call_sender.send(DriverCall::SetKeys { req });
718            match &self.mocks.lock().set_keys_resp_mock {
719                Some(resp) => Ok(resp.clone()),
720                None => {
721                    Ok(fidl_fullmac::WlanFullmacSetKeysResp { statuslist: vec![0i32; num_keys] })
722                }
723            }
724        }
725        fn eapol_tx(&self, req: fidl_fullmac::WlanFullmacImplEapolTxRequest) -> anyhow::Result<()> {
726            self.driver_call_sender.send(DriverCall::EapolTx { req });
727            Ok(())
728        }
729        fn get_iface_stats(&self) -> anyhow::Result<fidl_mlme::GetIfaceStatsResponse> {
730            self.driver_call_sender.send(DriverCall::GetIfaceStats);
731            Ok(self.mocks.lock().get_iface_stats_mock.clone().unwrap_or(
732                fidl_mlme::GetIfaceStatsResponse::ErrorStatus(zx::sys::ZX_ERR_NOT_SUPPORTED),
733            ))
734        }
735        fn get_iface_histogram_stats(
736            &self,
737        ) -> anyhow::Result<fidl_mlme::GetIfaceHistogramStatsResponse> {
738            self.driver_call_sender.send(DriverCall::GetIfaceHistogramStats);
739            Ok(self.mocks.lock().get_iface_histogram_stats_mock.clone().unwrap_or(
740                fidl_mlme::GetIfaceHistogramStatsResponse::ErrorStatus(
741                    zx::sys::ZX_ERR_NOT_SUPPORTED,
742                ),
743            ))
744        }
745        fn get_signal_report(&self) -> anyhow::Result<Result<fidl_stats::SignalReport, i32>> {
746            self.driver_call_sender.send(DriverCall::GetSignalReport);
747            self.mocks.lock().get_signal_report_mock.clone().ok_or_else(|| format_err!(""))
748        }
749        fn sae_handshake_resp(
750            &self,
751            resp: fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest,
752        ) -> anyhow::Result<()> {
753            self.driver_call_sender.send(DriverCall::SaeHandshakeResp { resp });
754            Ok(())
755        }
756        fn sae_frame_tx(&self, frame: fidl_fullmac::SaeFrame) -> anyhow::Result<()> {
757            self.driver_call_sender.send(DriverCall::SaeFrameTx { frame });
758            Ok(())
759        }
760        fn wmm_status_req(&self) -> anyhow::Result<()> {
761            self.driver_call_sender.send(DriverCall::WmmStatusReq);
762            Ok(())
763        }
764        fn on_link_state_changed(
765            &self,
766            req: fidl_fullmac::WlanFullmacImplOnLinkStateChangedRequest,
767        ) -> anyhow::Result<()> {
768            self.driver_call_sender.send(DriverCall::OnLinkStateChanged { req });
769            Ok(())
770        }
771        fn set_mac_address(
772            &self,
773            req: fidl_fuchsia_wlan_fullmac::WlanFullmacImplSetMacAddressRequest,
774        ) -> anyhow::Result<Result<(), i32>> {
775            self.driver_call_sender.send(DriverCall::SetMacAddress { req });
776            Ok(Ok(()))
777        }
778
779        fn install_apf_packet_filter(
780            &self,
781            req: fidl_fullmac::WlanFullmacImplInstallApfPacketFilterRequest,
782        ) -> anyhow::Result<Result<(), i32>> {
783            self.driver_call_sender.send(DriverCall::InstallApfPacketFilter { req });
784            self.mocks.lock().install_apf_packet_filter_mock.clone().ok_or_else(|| {
785                format_err!("install_apf_packet_filter_mock is None in FakeFullmacDevice")
786            })
787        }
788
789        fn read_apf_packet_filter_data(
790            &self,
791        ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplReadApfPacketFilterDataResponse, i32>>
792        {
793            self.driver_call_sender.send(DriverCall::ReadApfPacketFilterData);
794            self.mocks.lock().read_apf_packet_filter_data_mock.clone().ok_or_else(|| {
795                format_err!("read_apf_packet_filter_data_mock is None in FakeFullmacDevice")
796            })
797        }
798
799        fn set_apf_packet_filter_enabled(
800            &self,
801            req: fidl_fullmac::WlanFullmacImplSetApfPacketFilterEnabledRequest,
802        ) -> anyhow::Result<Result<(), i32>> {
803            self.driver_call_sender.send(DriverCall::SetApfPacketFilterEnabled { req });
804            self.mocks.lock().set_apf_packet_filter_enabled_mock.clone().ok_or_else(|| {
805                format_err!("set_apf_packet_filter_enabled_mock is None in FakeFullmacDevice")
806            })
807        }
808
809        fn get_apf_packet_filter_enabled(
810            &self,
811        ) -> anyhow::Result<
812            Result<fidl_fullmac::WlanFullmacImplGetApfPacketFilterEnabledResponse, i32>,
813        > {
814            self.driver_call_sender.send(DriverCall::GetApfPacketFilterEnabled);
815            self.mocks.lock().get_apf_packet_filter_enabled_mock.clone().ok_or_else(|| {
816                format_err!("get_apf_packet_filter_enabled_mock is None in FakeFullmacDevice")
817            })
818        }
819
820        fn get_scheduled_scan_enabled(
821            &self,
822        ) -> anyhow::Result<Result<fidl_fullmac::WlanFullmacImplGetScheduledScanEnabledResponse, i32>>
823        {
824            self.driver_call_sender.send(DriverCall::GetScheduledScanEnabled);
825            self.mocks.lock().get_scheduled_scan_enabled_mock.clone().ok_or_else(|| {
826                format_err!("get_scheduled_scan_enabled_mock is None in FakeFullmacDevice")
827            })
828        }
829
830        fn start_scheduled_scan(
831            &self,
832            req: fidl_fullmac::WlanFullmacImplStartScheduledScanRequest,
833        ) -> anyhow::Result<Result<(), i32>> {
834            self.driver_call_sender.send(DriverCall::StartScheduledScan { req });
835            Ok(Ok(()))
836        }
837
838        fn stop_scheduled_scan(
839            &self,
840            req: fidl_fullmac::WlanFullmacImplStopScheduledScanRequest,
841        ) -> anyhow::Result<Result<(), i32>> {
842            self.driver_call_sender.send(DriverCall::StopScheduledScan { req });
843            Ok(Ok(()))
844        }
845    }
846}