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