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