1#![doc = concat!("- Station mode (aka STA mode or Wi-Fi client mode). ", chip_pretty!(), " connects to an access point.")]
8#![doc = concat!("- AP mode (aka Soft-AP mode or Access Point mode). Stations connect to the ", chip_pretty!(),".")]
9#![doc = concat!("- Station/AP-coexistence mode (", chip_pretty!(), " is concurrently an access point and a station connected to another access point).")]
10use alloc::{borrow::ToOwned, collections::vec_deque::VecDeque, str, vec::Vec};
42use core::{
43 fmt::{Debug, Write},
44 marker::PhantomData,
45 mem::MaybeUninit,
46 ptr::addr_of,
47};
48
49use docsplay::Display;
50use embassy_sync::{blocking_mutex::raw::NoopRawMutex, waitqueue::GenericAtomicWaker};
51use enumset::{EnumSet, EnumSetType};
52use esp_config::esp_config_int;
53use esp_hal::system::Cpu;
54#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
55use esp_hal::time::{Duration, Instant};
56use esp_sync::NonReentrantMutex;
57use event::EVENT_CHANNEL;
58use portable_atomic::{AtomicU8, AtomicUsize, Ordering};
59use procmacros::BuilderLite;
60
61pub(crate) use self::os_adapter::*;
62#[cfg(all(feature = "sniffer", feature = "unstable"))]
63#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
64use self::sniffer::Sniffer;
65#[cfg(feature = "wifi-eap")]
66use self::sta::eap::EapStationConfig;
67use self::{
68 ap::{AccessPointConfig, AccessPointInfo, convert_ap_info},
69 private::PacketBuffer,
70 scan::{FreeApListOnDrop, ScanConfig, ScanResults, ScanTypeConfig},
71 sta::StationConfig,
72 state::*,
73};
74use crate::{
75 RadioRefGuard,
76 asynch::AtomicWaker,
77 hal::ram,
78 sys::{
79 c_types,
80 include::{self, *},
81 },
82 wifi::event::{EventInfo, WifiEvent},
83};
84pub mod ap;
85
86unstable_module!(
87 #[cfg(all(feature = "csi", wifi_csi_supported))]
88 #[cfg_attr(docsrs, doc(cfg(feature = "csi")))]
89 pub mod csi;
90 pub mod event;
91 #[cfg(feature = "sniffer")]
92 #[cfg_attr(docsrs, doc(cfg(feature = "sniffer")))]
93 pub mod sniffer;
94);
95
96pub mod scan;
97pub mod sta;
98
99pub(crate) mod os_adapter;
100pub(crate) mod state;
101
102mod internal;
103
104const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU");
105
106#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109enum LinkState {
110 #[default]
112 Down,
113 Up,
115}
116
117#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
119#[cfg_attr(feature = "defmt", derive(defmt::Format))]
120#[non_exhaustive]
121pub enum AuthenticationMethod {
122 None,
124
125 Wep,
127
128 Wpa,
130
131 #[default]
133 Wpa2Personal,
134
135 WpaWpa2Personal,
137
138 Wpa2Enterprise,
140
141 Wpa3Personal,
143
144 Wpa2Wpa3Personal,
146
147 WapiPersonal,
149
150 Owe,
152
153 Wpa3EntSuiteB192Bit,
155
156 Wpa3ExtPsk,
160
161 Wpa3ExtPskMixed,
165
166 Dpp,
168
169 Wpa3Enterprise,
171
172 Wpa2Wpa3Enterprise,
174
175 WpaEnterprise,
177}
178
179#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182#[non_exhaustive]
183pub struct Protocols {
184 _2_4: EnumSet<Protocol>,
186 #[cfg(wifi_has_5g)]
188 _5: EnumSet<Protocol>,
189}
190
191impl Default for Protocols {
192 fn default() -> Self {
193 Self {
194 _2_4: Protocol::B | Protocol::G | Protocol::N,
195 #[cfg(wifi_has_5g)]
196 _5: Protocol::AC | Protocol::A | Protocol::AX,
197 }
198 }
199}
200
201impl Protocols {
202 fn to_raw(self) -> wifi_protocols_t {
203 wifi_protocols_t {
204 ghz_2g: to_mask(self._2_4),
205 #[cfg(wifi_has_5g)]
206 ghz_5g: to_mask(self._5),
207 #[cfg(not(wifi_has_5g))]
208 ghz_5g: 0,
209 }
210 }
211}
212
213#[cfg_attr(docsrs, procmacros::doc_replace(
214 "hint_5g" => {
215 cfg(wifi_has_5g) => "The default protocol is AC/A/AX for band mode 5G.",
216 _ => ""
217 },
218))]
219#[derive(Debug, PartialOrd, Hash, EnumSetType)]
224#[cfg_attr(feature = "defmt", derive(defmt::Format))]
225#[non_exhaustive]
226pub enum Protocol {
227 B,
229
230 G,
232
233 N,
235
236 LR,
238
239 A,
241
242 AC,
244
245 AX,
247}
248
249impl Protocol {
250 fn to_mask(self) -> u16 {
251 let mask = match self {
252 Protocol::B => WIFI_PROTOCOL_11B,
253 Protocol::G => WIFI_PROTOCOL_11G,
254 Protocol::N => WIFI_PROTOCOL_11N,
255 Protocol::LR => WIFI_PROTOCOL_LR,
256 Protocol::A => WIFI_PROTOCOL_11A,
257 Protocol::AC => WIFI_PROTOCOL_11AC,
258 Protocol::AX => WIFI_PROTOCOL_11AX,
259 };
260 mask as _
261 }
262}
263
264fn to_mask(protocols: EnumSet<Protocol>) -> u16 {
265 protocols.iter().fold(0, |acc, p| acc | p.to_mask())
266}
267
268#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
270#[cfg_attr(feature = "defmt", derive(defmt::Format))]
271pub enum SecondaryChannel {
272 #[default]
274 None,
275
276 Above,
278
279 Below,
281}
282
283impl SecondaryChannel {
284 fn from_raw(raw: u32) -> Self {
285 match raw {
286 0 => SecondaryChannel::None,
287 1 => SecondaryChannel::Above,
288 2 => SecondaryChannel::Below,
289 _ => panic!("Invalid secondary channel value: {}", raw),
290 }
291 }
292
293 #[cfg(any(feature = "sniffer", feature = "esp-now"))]
294 fn from_raw_or_default(raw: u32) -> Self {
295 match raw {
296 0 => SecondaryChannel::None,
297 1 => SecondaryChannel::Above,
298 2 => SecondaryChannel::Below,
299 _ => SecondaryChannel::None,
300 }
301 }
302}
303
304#[cfg_attr(docsrs, procmacros::doc_replace(
305 "default_band_mode" => {
306 cfg(wifi_has_5g) => "BandMode::Auto",
307 _ => "BandMode::_2_4G"
308 },
309))]
310#[allow(clippy::large_enum_variant)]
314#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316#[non_exhaustive]
317pub enum BandMode {
318 #[cfg_attr(not(wifi_has_5g), default)]
320 _2_4G,
321 #[cfg(wifi_has_5g)]
323 _5G,
324 #[cfg_attr(wifi_has_5g, default)]
326 #[cfg(wifi_has_5g)]
327 Auto,
328}
329
330impl BandMode {
331 fn to_raw(&self) -> u32 {
332 match self {
333 BandMode::_2_4G => wifi_band_mode_t_WIFI_BAND_MODE_2G_ONLY,
334 #[cfg(wifi_has_5g)]
335 BandMode::_5G => wifi_band_mode_t_WIFI_BAND_MODE_5G_ONLY,
336 #[cfg(wifi_has_5g)]
337 BandMode::Auto => wifi_band_mode_t_WIFI_BAND_MODE_AUTO,
338 }
339 }
340}
341
342#[allow(clippy::large_enum_variant)]
344#[derive(Clone, Debug, PartialEq, Eq, Hash)]
345#[cfg_attr(feature = "defmt", derive(defmt::Format))]
346#[non_exhaustive]
347pub enum Config {
348 Station(StationConfig),
350
351 AccessPoint(AccessPointConfig),
353
354 AccessPointStation(StationConfig, AccessPointConfig),
356
357 #[cfg(feature = "wifi-eap")]
359 EapStation(EapStationConfig),
360}
361
362impl Config {
363 fn validate(&self) -> Result<(), WifiError> {
364 match self {
365 Config::Station(station_configuration) => station_configuration.validate(),
366 Config::AccessPoint(access_point_configuration) => {
367 access_point_configuration.validate()
368 }
369 Config::AccessPointStation(station_configuration, access_point_configuration) => {
370 station_configuration.validate()?;
371 access_point_configuration.validate()
372 }
373 #[cfg(feature = "wifi-eap")]
374 Config::EapStation(eap_station_configuration) => eap_station_configuration.validate(),
375 }
376 }
377}
378
379impl AuthenticationMethod {
380 fn to_raw(self) -> wifi_auth_mode_t {
381 match self {
382 AuthenticationMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
383 AuthenticationMethod::Wep => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
384 AuthenticationMethod::Wpa => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
385 AuthenticationMethod::Wpa2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
386 AuthenticationMethod::WpaWpa2Personal => {
387 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK
388 }
389 AuthenticationMethod::Wpa2Enterprise => {
390 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE
391 }
392 AuthenticationMethod::Wpa3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
393 AuthenticationMethod::Wpa2Wpa3Personal => {
394 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK
395 }
396 AuthenticationMethod::WapiPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
397 AuthenticationMethod::Owe => include::wifi_auth_mode_t_WIFI_AUTH_OWE,
398 AuthenticationMethod::Wpa3EntSuiteB192Bit => {
399 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192
400 }
401 AuthenticationMethod::Wpa3ExtPsk => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK,
402 AuthenticationMethod::Wpa3ExtPskMixed => {
403 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE
404 }
405 AuthenticationMethod::Dpp => include::wifi_auth_mode_t_WIFI_AUTH_DPP,
406 AuthenticationMethod::Wpa3Enterprise => {
407 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE
408 }
409 AuthenticationMethod::Wpa2Wpa3Enterprise => {
410 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE
411 }
412 AuthenticationMethod::WpaEnterprise => {
413 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE
414 }
415 }
416 }
417
418 fn from_raw(raw: wifi_auth_mode_t) -> Self {
419 match raw {
420 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthenticationMethod::None,
421 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthenticationMethod::Wep,
422 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthenticationMethod::Wpa,
423 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthenticationMethod::Wpa2Personal,
424 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => {
425 AuthenticationMethod::WpaWpa2Personal
426 }
427 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => {
428 AuthenticationMethod::Wpa2Enterprise
429 }
430 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthenticationMethod::Wpa3Personal,
431 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => {
432 AuthenticationMethod::Wpa2Wpa3Personal
433 }
434 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthenticationMethod::WapiPersonal,
435 include::wifi_auth_mode_t_WIFI_AUTH_OWE => AuthenticationMethod::Owe,
436 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192 => {
437 AuthenticationMethod::Wpa3EntSuiteB192Bit
438 }
439 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK => AuthenticationMethod::Wpa3ExtPsk,
440 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE => {
441 AuthenticationMethod::Wpa3ExtPskMixed
442 }
443 include::wifi_auth_mode_t_WIFI_AUTH_DPP => AuthenticationMethod::Dpp,
444 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE => {
445 AuthenticationMethod::Wpa3Enterprise
446 }
447 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE => {
448 AuthenticationMethod::Wpa2Wpa3Enterprise
449 }
450 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE => {
451 AuthenticationMethod::WpaEnterprise
452 }
453 _ => AuthenticationMethod::None,
460 }
461 }
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
466#[cfg_attr(feature = "defmt", derive(defmt::Format))]
467#[non_exhaustive]
468enum WifiMode {
469 Station,
471 AccessPoint,
473 AccessPointStation,
475}
476
477impl WifiMode {
478 pub(crate) fn current() -> Result<Self, WifiError> {
479 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
480 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
481
482 Ok(Self::from_raw(mode))
483 }
484
485 fn is_station(&self) -> bool {
487 match self {
488 Self::Station | Self::AccessPointStation => true,
489 Self::AccessPoint => false,
490 }
491 }
492
493 fn is_access_point(&self) -> bool {
495 match self {
496 Self::Station => false,
497 Self::AccessPoint | Self::AccessPointStation => true,
498 }
499 }
500
501 fn from_raw(value: wifi_mode_t) -> Self {
503 #[allow(non_upper_case_globals)]
504 match value {
505 include::wifi_mode_t_WIFI_MODE_STA => Self::Station,
506 include::wifi_mode_t_WIFI_MODE_AP => Self::AccessPoint,
507 include::wifi_mode_t_WIFI_MODE_APSTA => Self::AccessPointStation,
508 _ => panic!("Invalid wifi mode value: {}", value),
509 }
510 }
511}
512
513impl From<&Config> for WifiMode {
514 fn from(config: &Config) -> Self {
515 match config {
516 Config::AccessPoint(_) => Self::AccessPoint,
517 Config::Station(_) => Self::Station,
518 Config::AccessPointStation(_, _) => Self::AccessPointStation,
519 #[cfg(feature = "wifi-eap")]
520 Config::EapStation(_) => Self::Station,
521 }
522 }
523}
524
525#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
527#[cfg_attr(feature = "defmt", derive(defmt::Format))]
528#[non_exhaustive]
529pub enum DisconnectReason {
530 Unspecified,
532 AuthenticationExpired,
534 AuthenticationLeave,
536 DisassociatedDueToInactivity,
538 AssociationTooMany,
540 Class2FrameFromNonAuthenticatedStation,
542 Class3FrameFromNonAssociatedStation,
544 AssociationLeave,
546 AssociationNotAuthenticated,
548 DisassociatedPowerCapabilityBad,
550 DisassociatedUnsupportedChannel,
552 BssTransitionDisassociated,
554 IeInvalid,
556 MicFailure,
558 FourWayHandshakeTimeout,
560 GroupKeyUpdateTimeout,
562 IeIn4wayDiffers,
564 GroupCipherInvalid,
566 PairwiseCipherInvalid,
568 AkmpInvalid,
570 UnsupportedRsnIeVersion,
572 InvalidRsnIeCapabilities,
574 _802_1xAuthenticationFailed,
576 CipherSuiteRejected,
578 TdlsPeerUnreachable,
580 TdlsUnspecified,
582 SspRequestedDisassociation,
584 NoSspRoamingAgreement,
586 BadCipherOrAkm,
588 NotAuthorizedThisLocation,
590 ServiceChangePercludesTs,
592 UnspecifiedQos,
594 NotEnoughBandwidth,
596 MissingAcks,
598 ExceededTxOp,
600 StationLeaving,
602 EndBlockAck,
604 UnknownBlockAck,
606 Timeout,
608 PeerInitiated,
610 AccessPointInitiatedDisassociation,
612 InvalidFtActionFrameCount,
614 InvalidPmkid,
616 InvalidMde,
618 InvalidFte,
620 TransmissionLinkEstablishmentFailed,
622 AlterativeChannelOccupied,
624 BeaconTimeout,
626 NoAccessPointFound,
628 AuthenticationFailed,
630 AssociationFailed,
632 HandshakeTimeout,
634 ConnectionFailed,
636 AccessPointTsfReset,
638 Roaming,
640 AssociationComebackTimeTooLong,
642 SaQueryTimeout,
644 NoAccessPointFoundWithCompatibleSecurity,
646 NoAccessPointFoundInAuthmodeThreshold,
648 NoAccessPointFoundInRssiThreshold,
650}
651
652impl DisconnectReason {
653 fn from_raw(id: u16) -> Self {
654 match id {
655 1 => Self::Unspecified,
656 2 => Self::AuthenticationExpired,
657 3 => Self::AuthenticationLeave,
658 4 => Self::DisassociatedDueToInactivity,
659 5 => Self::AssociationTooMany,
660 6 => Self::Class2FrameFromNonAuthenticatedStation,
661 7 => Self::Class3FrameFromNonAssociatedStation,
662 8 => Self::AssociationLeave,
663 9 => Self::AssociationNotAuthenticated,
664 10 => Self::DisassociatedPowerCapabilityBad,
665 11 => Self::DisassociatedUnsupportedChannel,
666 12 => Self::BssTransitionDisassociated,
667 13 => Self::IeInvalid,
668 14 => Self::MicFailure,
669 15 => Self::FourWayHandshakeTimeout,
670 16 => Self::GroupKeyUpdateTimeout,
671 17 => Self::IeIn4wayDiffers,
672 18 => Self::GroupCipherInvalid,
673 19 => Self::PairwiseCipherInvalid,
674 20 => Self::AkmpInvalid,
675 21 => Self::UnsupportedRsnIeVersion,
676 22 => Self::InvalidRsnIeCapabilities,
677 23 => Self::_802_1xAuthenticationFailed,
678 24 => Self::CipherSuiteRejected,
679 25 => Self::TdlsPeerUnreachable,
680 26 => Self::TdlsUnspecified,
681 27 => Self::SspRequestedDisassociation,
682 28 => Self::NoSspRoamingAgreement,
683 29 => Self::BadCipherOrAkm,
684 30 => Self::NotAuthorizedThisLocation,
685 31 => Self::ServiceChangePercludesTs,
686 32 => Self::UnspecifiedQos,
687 33 => Self::NotEnoughBandwidth,
688 34 => Self::MissingAcks,
689 35 => Self::ExceededTxOp,
690 36 => Self::StationLeaving,
691 37 => Self::EndBlockAck,
692 38 => Self::UnknownBlockAck,
693 39 => Self::Timeout,
694 46 => Self::PeerInitiated,
695 47 => Self::AccessPointInitiatedDisassociation,
696 48 => Self::InvalidFtActionFrameCount,
697 49 => Self::InvalidPmkid,
698 50 => Self::InvalidMde,
699 51 => Self::InvalidFte,
700 67 => Self::TransmissionLinkEstablishmentFailed,
701 68 => Self::AlterativeChannelOccupied,
702 200 => Self::BeaconTimeout,
703 201 => Self::NoAccessPointFound,
704 202 => Self::AuthenticationFailed,
705 203 => Self::AssociationFailed,
706 204 => Self::HandshakeTimeout,
707 205 => Self::ConnectionFailed,
708 206 => Self::AccessPointTsfReset,
709 207 => Self::Roaming,
710 208 => Self::AssociationComebackTimeTooLong,
711 209 => Self::SaQueryTimeout,
712 210 => Self::NoAccessPointFoundWithCompatibleSecurity,
713 211 => Self::NoAccessPointFoundInAuthmodeThreshold,
714 212 => Self::NoAccessPointFoundInRssiThreshold,
715 _ => Self::Unspecified,
716 }
717 }
718}
719
720#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
722#[cfg_attr(feature = "defmt", derive(defmt::Format))]
723pub struct Ssid {
724 ssid: [u8; 32],
725 len: u8,
726}
727
728impl Ssid {
729 pub(crate) fn new(ssid: &str) -> Self {
730 let mut ssid_bytes = [0u8; 32];
731 let bytes = ssid.as_bytes();
732 let len = usize::min(32, bytes.len());
733 ssid_bytes[..len].copy_from_slice(bytes);
734
735 Self::from_raw(&ssid_bytes, len as u8)
736 }
737
738 pub(crate) fn from_raw(ssid: &[u8], len: u8) -> Self {
739 let mut ssid_bytes = [0u8; 32];
740 let len = usize::min(32, len as usize);
741 ssid_bytes[..len].copy_from_slice(&ssid[..len]);
742
743 Self {
744 ssid: ssid_bytes,
745 len: len as u8,
746 }
747 }
748
749 pub(crate) fn as_bytes(&self) -> &[u8] {
750 &self.ssid[..self.len as usize]
751 }
752
753 pub fn len(&self) -> usize {
755 self.len as usize
756 }
757
758 pub fn is_empty(&self) -> bool {
760 self.len == 0
761 }
762
763 pub fn as_str(&self) -> &str {
765 let part = &self.ssid[..self.len as usize];
766 match str::from_utf8(part) {
767 Ok(s) => s,
768 Err(e) => {
769 let (valid, _) = part.split_at(e.valid_up_to());
770 unsafe { str::from_utf8_unchecked(valid) }
771 }
772 }
773 }
774}
775
776impl Debug for Ssid {
777 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
778 f.write_char('"')?;
779 f.write_str(self.as_str())?;
780 f.write_char('"')
781 }
782}
783
784impl From<alloc::string::String> for Ssid {
785 fn from(ssid: alloc::string::String) -> Self {
786 Self::new(&ssid)
787 }
788}
789
790impl From<&str> for Ssid {
791 fn from(ssid: &str) -> Self {
792 Self::new(ssid)
793 }
794}
795
796impl From<&[u8]> for Ssid {
797 fn from(ssid: &[u8]) -> Self {
798 Self::from_raw(ssid, ssid.len() as u8)
799 }
800}
801
802static TX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
803
804struct PacketQueue {
811 queue: VecDeque<PacketBuffer>,
812
813 waker: GenericAtomicWaker<NoopRawMutex>,
815}
816
817impl PacketQueue {
818 const fn new() -> Self {
819 Self {
820 queue: VecDeque::new(),
821 waker: GenericAtomicWaker::new(NoopRawMutex::new()),
822 }
823 }
824
825 fn change_capacity(&mut self, new_capacity: usize) -> Result<(), WifiError> {
826 let new_capacity = new_capacity.max(self.queue.capacity());
829 let additional = new_capacity.saturating_sub(self.queue.capacity());
830 self.queue
831 .try_reserve_exact(additional)
832 .map_err(|_| WifiError::OutOfMemory)
833 }
834
835 fn push_back(&mut self, packet: PacketBuffer) -> Result<(), PacketBuffer> {
836 if self.len() >= self.queue.capacity() {
837 return Err(packet);
838 }
839
840 self.queue.push_back(packet);
841 self.waker.wake();
842
843 Ok(())
844 }
845
846 fn pop_front(&mut self) -> Option<PacketBuffer> {
847 self.queue.pop_front()
848 }
849
850 fn len(&self) -> usize {
851 self.queue.len()
852 }
853
854 fn is_empty(&self) -> bool {
855 self.queue.is_empty()
856 }
857
858 fn register_waker(&mut self, waker: &core::task::Waker) {
859 self.waker.register(waker);
860 }
861}
862
863static DATA_QUEUE_RX_AP: NonReentrantMutex<PacketQueue> =
864 NonReentrantMutex::new(PacketQueue::new());
865
866static DATA_QUEUE_RX_STA: NonReentrantMutex<PacketQueue> =
867 NonReentrantMutex::new(PacketQueue::new());
868
869#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)]
871#[cfg_attr(feature = "defmt", derive(defmt::Format))]
872#[non_exhaustive]
873pub enum WifiError {
874 Disconnected(sta::DisconnectedInfo),
876
877 Unsupported,
879
880 InvalidArguments,
882
883 Failed,
885
886 OutOfMemory,
888
889 InvalidSsid,
891
892 InvalidPassword,
894
895 NotConnected,
897}
898
899impl WifiError {
900 fn from_error_code(code: i32) -> Self {
901 if crate::sys::include::ESP_FAIL == code {
902 return WifiError::Failed;
903 }
904
905 match code as u32 {
906 crate::sys::include::ESP_ERR_NO_MEM => WifiError::OutOfMemory,
907 crate::sys::include::ESP_ERR_INVALID_ARG => WifiError::InvalidArguments,
908 crate::sys::include::ESP_ERR_WIFI_SSID => WifiError::InvalidSsid,
909 crate::sys::include::ESP_ERR_WIFI_PASSWORD => WifiError::InvalidPassword,
910 crate::sys::include::ESP_ERR_WIFI_NOT_CONNECT => WifiError::NotConnected,
911 _ => panic!("Unknown error code: {}", code),
912 }
913 }
914}
915
916impl core::error::Error for WifiError {}
917
918#[cfg(esp32)]
919fn set_mac_time_update_cb(_wifi: crate::hal::peripherals::WIFI<'_>) {
920 use crate::sys::include::esp_wifi_internal_update_mac_time;
921 unsafe {
922 esp_phy::set_mac_time_update_cb(|duration| {
923 esp_wifi_internal_update_mac_time(duration.as_micros() as u32);
924 });
925 }
926}
927
928pub(crate) fn wifi_init(_wifi: crate::hal::peripherals::WIFI<'_>) -> Result<(), WifiError> {
929 #[cfg(esp32)]
930 set_mac_time_update_cb(_wifi);
931 unsafe {
932 #[cfg(feature = "coex")]
933 esp_wifi_result!(coex_init())?;
934
935 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
936 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
937
938 esp_wifi_result!(esp_supplicant_init())?;
939
940 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
941
942 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
943 esp_interface_t_ESP_IF_WIFI_STA,
944 Some(recv_cb_sta)
945 ))?;
946
947 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
949 esp_interface_t_ESP_IF_WIFI_AP,
950 Some(recv_cb_ap)
951 ))?;
952
953 Ok(())
954 }
955}
956
957#[cfg(feature = "coex")]
958pub(crate) fn coex_initialize() -> i32 {
959 debug!("call coex-initialize");
960 unsafe {
961 let res = crate::sys::include::esp_coex_adapter_register(
962 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
963 );
964 if res != 0 {
965 error!("Error: esp_coex_adapter_register {}", res);
966 return res;
967 }
968 let res = crate::sys::include::coex_pre_init();
969 if res != 0 {
970 error!("Error: coex_pre_init {}", res);
971 return res;
972 }
973 0
974 }
975}
976
977pub(crate) unsafe extern "C" fn coex_init() -> i32 {
978 debug!("coex-init");
979
980 cfg_if::cfg_if! {
981 if #[cfg(feature = "coex")] {
982 unsafe { crate::sys::include::coex_init() }
983 } else {
984 0
985 }
986 }
987}
988
989fn wifi_deinit() -> Result<(), crate::WifiError> {
990 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
991
992 while let Some(packet) = DATA_QUEUE_RX_STA.with(|q| q.pop_front()) {
999 drop(packet);
1000 }
1001 while let Some(packet) = DATA_QUEUE_RX_AP.with(|q| q.pop_front()) {
1002 drop(packet);
1003 }
1004
1005 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1006 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1007 Ok(())
1008}
1009
1010unsafe extern "C" fn recv_cb_sta(
1011 buffer: *mut c_types::c_void,
1012 len: u16,
1013 eb: *mut c_types::c_void,
1014) -> esp_err_t {
1015 let packet = PacketBuffer { buffer, len, eb };
1016 match DATA_QUEUE_RX_STA.with(|queue| queue.push_back(packet)) {
1023 Ok(()) => include::ESP_OK as esp_err_t,
1024 _ => {
1025 debug!("RX QUEUE FULL");
1026 include::ESP_ERR_NO_MEM as esp_err_t
1027 }
1028 }
1029}
1030
1031unsafe extern "C" fn recv_cb_ap(
1032 buffer: *mut c_types::c_void,
1033 len: u16,
1034 eb: *mut c_types::c_void,
1035) -> esp_err_t {
1036 let packet = PacketBuffer { buffer, len, eb };
1037 match DATA_QUEUE_RX_AP.with(|queue| queue.push_back(packet)) {
1044 Ok(()) => include::ESP_OK as esp_err_t,
1045 _ => {
1046 debug!("RX QUEUE FULL");
1047 include::ESP_ERR_NO_MEM as esp_err_t
1048 }
1049 }
1050}
1051
1052pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1053
1054fn decrement_inflight_counter() {
1055 unwrap!(
1056 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1057 Some(x.saturating_sub(1))
1058 })
1059 );
1060}
1061
1062#[ram]
1063unsafe extern "C" fn esp_wifi_tx_done_cb(
1064 _ifidx: u8,
1065 _data: *mut u8,
1066 _data_len: *mut u16,
1067 _tx_status: bool,
1068) {
1069 trace!("esp_wifi_tx_done_cb");
1070
1071 decrement_inflight_counter();
1072
1073 TRANSMIT_WAKER.wake();
1074}
1075
1076pub(crate) fn wifi_start_scan(
1077 block: bool,
1078 ScanConfig {
1079 ssid,
1080 mut bssid,
1081 channel,
1082 show_hidden,
1083 scan_type,
1084 ..
1085 }: ScanConfig,
1086) -> i32 {
1087 scan_type.validate();
1088 let (scan_time, scan_type) = match scan_type {
1089 ScanTypeConfig::Active { min, max } => (
1090 wifi_scan_time_t {
1091 active: wifi_active_scan_time_t {
1092 min: min.as_millis() as u32,
1093 max: max.as_millis() as u32,
1094 },
1095 passive: 0,
1096 },
1097 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1098 ),
1099 ScanTypeConfig::Passive(dur) => (
1100 wifi_scan_time_t {
1101 active: wifi_active_scan_time_t { min: 0, max: 0 },
1102 passive: dur.as_millis() as u32,
1103 },
1104 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1105 ),
1106 };
1107
1108 let mut ssid_buf = ssid.map(|m| {
1109 let mut buf = Vec::from_iter(m.as_bytes().to_owned());
1110 buf.push(b'\0');
1111 buf
1112 });
1113
1114 let ssid = ssid_buf
1115 .as_mut()
1116 .map(|e| e.as_mut_ptr())
1117 .unwrap_or_else(core::ptr::null_mut);
1118 let bssid = bssid
1119 .as_mut()
1120 .map(|e| e.as_mut_ptr())
1121 .unwrap_or_else(core::ptr::null_mut);
1122
1123 let scan_config = wifi_scan_config_t {
1124 ssid,
1125 bssid,
1126 channel: channel.unwrap_or(0),
1127 show_hidden,
1128 scan_type,
1129 scan_time,
1130 home_chan_dwell_time: 0,
1131 channel_bitmap: wifi_scan_channel_bitmap_t {
1132 ghz_2_channels: 0,
1133 ghz_5_channels: 0,
1134 },
1135 coex_background_scan: false,
1136 };
1137
1138 unsafe { esp_wifi_scan_start(&scan_config, block) }
1139}
1140
1141mod private {
1142 use super::*;
1143
1144 #[derive(Debug)]
1152 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1153 pub struct PacketBuffer {
1154 pub(crate) buffer: *mut c_types::c_void,
1155 pub(crate) len: u16,
1156 pub(crate) eb: *mut c_types::c_void,
1157 }
1158
1159 unsafe impl Send for PacketBuffer {}
1160
1161 impl Drop for PacketBuffer {
1162 fn drop(&mut self) {
1163 trace!("Dropping PacketBuffer, freeing memory");
1164 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1165 }
1166 }
1167
1168 impl PacketBuffer {
1169 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1170 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1171 }
1172 }
1173}
1174
1175#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1178enum InterfaceType {
1179 Station,
1181 AccessPoint,
1183}
1184
1185impl InterfaceType {
1186 fn mac_address(&self) -> [u8; 6] {
1187 use esp_hal::efuse::InterfaceMacAddress;
1188 let mac = match self {
1189 InterfaceType::Station => {
1190 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::Station)
1191 }
1192 InterfaceType::AccessPoint => {
1193 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::AccessPoint)
1194 }
1195 };
1196
1197 let mut out = [0u8; 6];
1198 out.copy_from_slice(mac.as_bytes());
1199 out
1200 }
1201
1202 fn data_queue_rx(&self) -> &'static NonReentrantMutex<PacketQueue> {
1203 match self {
1204 InterfaceType::Station => &DATA_QUEUE_RX_STA,
1205 InterfaceType::AccessPoint => &DATA_QUEUE_RX_AP,
1206 }
1207 }
1208
1209 fn can_send(&self) -> bool {
1210 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE.load(Ordering::Relaxed)
1211 }
1212
1213 fn increase_in_flight_counter(&self) {
1214 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1215 }
1216
1217 fn tx_token(&self) -> Option<WifiTxToken> {
1218 if !self.can_send() {
1219 crate::preempt::yield_task();
1221 }
1222
1223 if self.can_send() {
1224 if self.link_state() == LinkState::Up {
1226 return Some(WifiTxToken { mode: *self });
1227 }
1228 }
1229
1230 None
1231 }
1232
1233 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1234 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1235 if is_empty || !self.can_send() {
1236 crate::preempt::yield_task();
1238 }
1239
1240 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1241
1242 if !is_empty {
1243 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1244 } else {
1245 None
1246 }
1247 }
1248
1249 fn interface(&self) -> wifi_interface_t {
1250 match self {
1251 InterfaceType::Station => wifi_interface_t_WIFI_IF_STA,
1252 InterfaceType::AccessPoint => wifi_interface_t_WIFI_IF_AP,
1253 }
1254 }
1255
1256 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1257 TRANSMIT_WAKER.register(cx.waker())
1258 }
1259
1260 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1261 self.data_queue_rx().with(|q| q.register_waker(cx.waker()));
1262 }
1263
1264 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1265 match self {
1266 InterfaceType::Station => STA_LINK_STATE_WAKER.register(cx.waker()),
1267 InterfaceType::AccessPoint => AP_LINK_STATE_WAKER.register(cx.waker()),
1268 }
1269 }
1270
1271 fn link_state(&self) -> LinkState {
1272 let is_up = match self {
1273 InterfaceType::Station => {
1274 matches!(station_state(), WifiStationState::Connected)
1275 }
1276 InterfaceType::AccessPoint => {
1277 matches!(access_point_state(), WifiAccessPointState::Started)
1278 }
1279 };
1280
1281 if is_up {
1282 LinkState::Up
1283 } else {
1284 LinkState::Down
1285 }
1286 }
1287}
1288
1289static SINGLETONS: AtomicU8 = AtomicU8::new(0);
1290
1291const STA_BIT: u8 = 1 << 0;
1292const AP_BIT: u8 = 1 << 1;
1293#[cfg(feature = "sniffer")]
1294pub(super) const SNIFFER_BIT: u8 = 1 << 2;
1295
1296pub(super) fn try_acquire(bit: u8) -> bool {
1297 SINGLETONS.fetch_or(bit, Ordering::AcqRel) & bit == 0
1298}
1299
1300pub(super) fn release(bit: u8) {
1301 SINGLETONS.fetch_and(!bit, Ordering::Release);
1302}
1303
1304#[derive(Debug, PartialEq, Eq, Hash)]
1316#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1317pub struct Interface {
1318 mode: InterfaceType,
1319}
1320
1321impl Interface {
1322 pub fn station() -> Self {
1329 Self::try_station().expect("station interface already taken")
1330 }
1331
1332 pub fn try_station() -> Option<Self> {
1336 if try_acquire(STA_BIT) {
1337 Some(Self {
1338 mode: InterfaceType::Station,
1339 })
1340 } else {
1341 None
1342 }
1343 }
1344
1345 pub fn access_point() -> Self {
1352 Self::try_access_point().expect("access point interface already taken")
1353 }
1354
1355 pub fn try_access_point() -> Option<Self> {
1359 if try_acquire(AP_BIT) {
1360 Some(Self {
1361 mode: InterfaceType::AccessPoint,
1362 })
1363 } else {
1364 None
1365 }
1366 }
1367
1368 #[procmacros::doc_replace]
1369 pub fn mac_address(&self) -> [u8; 6] {
1384 self.mode.mac_address()
1385 }
1386
1387 #[doc(hidden)]
1388 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1390 self.mode.rx_token()
1391 }
1392
1393 #[doc(hidden)]
1394 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1396 self.mode.tx_token()
1397 }
1398}
1399
1400impl Drop for Interface {
1401 fn drop(&mut self) {
1402 let bit = match self.mode {
1403 InterfaceType::Station => STA_BIT,
1404 InterfaceType::AccessPoint => AP_BIT,
1405 };
1406 release(bit);
1407 }
1408}
1409
1410#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
1412#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1413#[non_exhaustive]
1414pub struct Bandwidths {
1415 _2_4: Bandwidth,
1417 #[cfg(wifi_has_5g)]
1419 _5: Bandwidth,
1420}
1421
1422impl Bandwidths {
1423 fn to_raw(self) -> wifi_bandwidths_t {
1424 wifi_bandwidths_t {
1425 ghz_2g: self._2_4.to_raw(),
1426 #[cfg(wifi_has_5g)]
1427 ghz_5g: self._5.to_raw(),
1428 #[cfg(not(wifi_has_5g))]
1429 ghz_5g: 0,
1430 }
1431 }
1432}
1433
1434#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1436#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1437#[allow(
1438 clippy::enum_variant_names,
1439 reason = "MHz suffix indicates physical unit."
1440)]
1441#[non_exhaustive]
1442pub enum Bandwidth {
1443 _20MHz,
1445 _40MHz,
1447 _80MHz,
1449 _160MHz,
1451 _80_80MHz,
1453}
1454
1455impl Bandwidth {
1456 fn to_raw(self) -> wifi_bandwidth_t {
1457 match self {
1458 Bandwidth::_20MHz => wifi_bandwidth_t_WIFI_BW_HT20,
1459 Bandwidth::_40MHz => wifi_bandwidth_t_WIFI_BW_HT40,
1460 Bandwidth::_80MHz => wifi_bandwidth_t_WIFI_BW80,
1461 Bandwidth::_160MHz => wifi_bandwidth_t_WIFI_BW160,
1462 Bandwidth::_80_80MHz => wifi_bandwidth_t_WIFI_BW80_BW80,
1463 }
1464 }
1465
1466 fn from_raw(raw: wifi_bandwidth_t) -> Self {
1467 match raw {
1468 raw if raw == wifi_bandwidth_t_WIFI_BW_HT20 => Bandwidth::_20MHz,
1469 raw if raw == wifi_bandwidth_t_WIFI_BW_HT40 => Bandwidth::_40MHz,
1470 raw if raw == wifi_bandwidth_t_WIFI_BW80 => Bandwidth::_80MHz,
1471 raw if raw == wifi_bandwidth_t_WIFI_BW160 => Bandwidth::_160MHz,
1472 raw if raw == wifi_bandwidth_t_WIFI_BW80_BW80 => Bandwidth::_80_80MHz,
1473 _ => Bandwidth::_20MHz,
1474 }
1475 }
1476}
1477
1478#[cfg(wifi_mac_version = "1")]
1481#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1482#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1483#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1484#[instability::unstable]
1485pub struct RxControlInfo {
1486 pub rssi: i32,
1488 pub rate: u32,
1491 pub sig_mode: u32,
1494 pub mcs: u32,
1497 pub cwb: u32,
1499 pub smoothing: u32,
1502 pub not_sounding: u32,
1505 pub aggregation: u32,
1507 pub stbc: u32,
1510 pub fec_coding: u32,
1513 pub sgi: u32,
1516 pub ampdu_cnt: u32,
1518 pub channel: u32,
1520 pub secondary_channel: SecondaryChannel,
1522 pub timestamp: Instant,
1525 pub noise_floor: i32,
1527 pub ant: u32,
1530 pub sig_len: u32,
1532 pub rx_state: u32,
1534}
1535
1536#[cfg(wifi_mac_version = "2")]
1539#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1540#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1541#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1542#[instability::unstable]
1543pub struct RxControlInfo {
1544 pub rssi: i32,
1546 pub rate: u32,
1549 pub sig_len: u32,
1551 pub rx_state: u32,
1554 pub dump_len: u32,
1556 pub he_sigb_len: u32,
1558 pub cur_single_mpdu: u32,
1560 pub cur_bb_format: u32,
1562 pub rx_channel_estimate_info_vld: u32,
1564 pub rx_channel_estimate_len: u32,
1566 pub secondary_channel: SecondaryChannel,
1568 pub channel: u32,
1570 pub noise_floor: i32,
1572 pub is_group: u32,
1574 pub rxend_state: u32,
1576 pub rxmatch3: u32,
1578 pub rxmatch2: u32,
1580 pub rxmatch1: u32,
1582 pub rxmatch0: u32,
1584 pub timestamp: Instant,
1587}
1588
1589#[cfg(wifi_mac_version = "3")]
1592#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1593#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1594#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1595#[instability::unstable]
1596pub struct RxControlInfo {
1597 pub rssi: i32,
1599 pub rate: u32,
1602 pub sig_len: u32,
1604 pub rx_state: u32,
1607 pub dump_len: u32,
1609 pub he_sigb_len: u32,
1611 pub cur_bb_format: u32,
1613 pub rx_channel_estimate_info_vld: u32,
1615 pub rx_channel_estimate_len: u32,
1617 pub secondary_channel: SecondaryChannel,
1619 pub channel: u32,
1621 pub noise_floor: i32,
1623 pub is_group: u32,
1625 pub rxend_state: u32,
1627 pub rxmatch3: u32,
1629 pub rxmatch2: u32,
1631 pub rxmatch1: u32,
1633 pub rxmatch0: u32,
1635 pub timestamp: Instant,
1638}
1639
1640#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1641impl RxControlInfo {
1642 pub(super) unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1648 #[cfg(wifi_mac_version = "1")]
1649 let rx_control_info = unsafe {
1650 RxControlInfo {
1651 rssi: (*rx_cntl).rssi(),
1652 rate: (*rx_cntl).rate(),
1653 sig_mode: (*rx_cntl).sig_mode(),
1654 mcs: (*rx_cntl).mcs(),
1655 cwb: (*rx_cntl).cwb(),
1656 smoothing: (*rx_cntl).smoothing(),
1657 not_sounding: (*rx_cntl).not_sounding(),
1658 aggregation: (*rx_cntl).aggregation(),
1659 stbc: (*rx_cntl).stbc(),
1660 fec_coding: (*rx_cntl).fec_coding(),
1661 sgi: (*rx_cntl).sgi(),
1662 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1663 channel: (*rx_cntl).channel(),
1664 secondary_channel: SecondaryChannel::from_raw_or_default(
1665 (*rx_cntl).secondary_channel(),
1666 ),
1667 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1668 noise_floor: (*rx_cntl).noise_floor(),
1669 ant: (*rx_cntl).ant(),
1670 sig_len: (*rx_cntl).sig_len(),
1671 rx_state: (*rx_cntl).rx_state(),
1672 }
1673 };
1674 #[cfg(wifi_mac_version = "2")]
1675 let rx_control_info = unsafe {
1676 RxControlInfo {
1677 rssi: (*rx_cntl).rssi(),
1678 rate: (*rx_cntl).rate(),
1679 sig_len: (*rx_cntl).sig_len(),
1680 rx_state: (*rx_cntl).rx_state(),
1681 dump_len: (*rx_cntl).dump_len(),
1682 he_sigb_len: (*rx_cntl).he_sigb_len(),
1683 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
1684 cur_bb_format: (*rx_cntl).cur_bb_format(),
1685 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1686 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1687 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1688 channel: (*rx_cntl).channel(),
1689 noise_floor: (*rx_cntl).noise_floor() as _,
1690 is_group: (*rx_cntl).is_group(),
1691 rxend_state: (*rx_cntl).rxend_state(),
1692 rxmatch3: (*rx_cntl).rxmatch3(),
1693 rxmatch2: (*rx_cntl).rxmatch2(),
1694 rxmatch1: (*rx_cntl).rxmatch1(),
1695 rxmatch0: (*rx_cntl).rxmatch0(),
1696 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1697 }
1698 };
1699 #[cfg(wifi_mac_version = "3")]
1700 let rx_control_info = unsafe {
1701 RxControlInfo {
1702 rssi: (*rx_cntl).rssi(),
1703 rate: (*rx_cntl).rate(),
1704 sig_len: (*rx_cntl).sig_len(),
1705 rx_state: (*rx_cntl).rx_state(),
1706 dump_len: (*rx_cntl).dump_len(),
1707 he_sigb_len: (*rx_cntl).sigb_len(),
1708 cur_bb_format: (*rx_cntl).cur_bb_format(),
1709 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1710 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1711 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1712 channel: (*rx_cntl).channel(),
1713 noise_floor: (*rx_cntl).noise_floor() as _,
1714 is_group: (*rx_cntl).is_group(),
1715 rxend_state: (*rx_cntl).rxend_state(),
1716 rxmatch3: (*rx_cntl).rxmatch3(),
1717 rxmatch2: (*rx_cntl).rxmatch2(),
1718 rxmatch1: (*rx_cntl).rxmatch1(),
1719 rxmatch0: (*rx_cntl).rxmatch0(),
1720 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1721 }
1722 };
1723 rx_control_info
1724 }
1725}
1726
1727#[doc(hidden)]
1728pub struct WifiRxToken {
1732 mode: InterfaceType,
1733}
1734
1735impl WifiRxToken {
1736 pub fn consume_token<R, F>(self, f: F) -> R
1739 where
1740 F: FnOnce(&mut [u8]) -> R,
1741 {
1742 let mut data = self.mode.data_queue_rx().with(|queue| {
1743 unwrap!(
1744 queue.pop_front(),
1745 "unreachable: transmit()/receive() ensures there is a packet to process"
1746 )
1747 });
1748
1749 let buffer = data.as_slice_mut();
1756 dump_packet_info(buffer);
1757
1758 f(buffer)
1759 }
1760}
1761
1762#[doc(hidden)]
1763pub struct WifiTxToken {
1767 mode: InterfaceType,
1768}
1769
1770impl WifiTxToken {
1771 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
1774 where
1775 F: FnOnce(&mut [u8]) -> R,
1776 {
1777 self.mode.increase_in_flight_counter();
1778
1779 let mut buffer: [u8; MTU] = [0u8; MTU];
1780 let buffer = &mut buffer[..len];
1781
1782 let res = f(buffer);
1783
1784 esp_wifi_send_data(self.mode.interface(), buffer);
1785
1786 res
1787 }
1788}
1789
1790pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
1795 state::locked(|| {
1798 if (interface == wifi_interface_t_WIFI_IF_STA
1800 && !matches!(station_state(), WifiStationState::Connected))
1801 || (interface == wifi_interface_t_WIFI_IF_AP
1802 && !matches!(access_point_state(), WifiAccessPointState::Started))
1803 {
1804 return;
1805 }
1806
1807 trace!("sending... {} bytes", data.len());
1808 dump_packet_info(data);
1809
1810 let len = data.len() as u16;
1811 let ptr = data.as_mut_ptr().cast();
1812
1813 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
1814
1815 if res != include::ESP_OK as i32 {
1816 warn!("esp_wifi_internal_tx returned error: {}", res);
1817 decrement_inflight_counter();
1818 }
1819 })
1820}
1821
1822fn dump_packet_info(_buffer: &mut [u8]) {
1823 #[cfg(dump_packets)]
1824 {
1825 info!("@WIFIFRAME {:?}", _buffer);
1826 }
1827}
1828
1829macro_rules! esp_wifi_result {
1830 ($value:expr) => {{
1831 use num_traits::FromPrimitive;
1832 let result = $value;
1833 if result != $crate::sys::include::ESP_OK as i32 {
1834 let error = unwrap!(FromPrimitive::from_i32(result));
1835 warn!(
1836 "{} returned an error: {:?} ({}). If this error is unmapped, please open an issue at <https://github.com/esp-rs/esp-hal/issues>.",
1837 stringify!($value),
1838 error,
1839 result
1840 );
1841 Err(WifiError::from_error_code(error))
1842 } else {
1843 Ok::<(), WifiError>(())
1844 }
1845 }};
1846}
1847pub(crate) use esp_wifi_result;
1848
1849static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
1852
1853static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
1854static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
1855
1856pub(crate) mod embassy_02 {
1859 use embassy_net_driver_02::{
1860 Capabilities,
1861 Driver,
1862 HardwareAddress,
1863 LinkState,
1864 RxToken,
1865 TxToken,
1866 };
1867
1868 use super::*;
1869
1870 impl RxToken for WifiRxToken {
1871 fn consume<R, F>(self, f: F) -> R
1872 where
1873 F: FnOnce(&mut [u8]) -> R,
1874 {
1875 self.consume_token(f)
1876 }
1877 }
1878
1879 impl TxToken for WifiTxToken {
1880 fn consume<R, F>(self, len: usize, f: F) -> R
1881 where
1882 F: FnOnce(&mut [u8]) -> R,
1883 {
1884 self.consume_token(len, f)
1885 }
1886 }
1887
1888 impl Driver for Interface {
1889 type RxToken<'a>
1890 = WifiRxToken
1891 where
1892 Self: 'a;
1893 type TxToken<'a>
1894 = WifiTxToken
1895 where
1896 Self: 'a;
1897
1898 fn receive(
1899 &mut self,
1900 cx: &mut core::task::Context<'_>,
1901 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
1902 self.mode.register_receive_waker(cx);
1903 self.mode.register_transmit_waker(cx);
1904 self.mode.rx_token()
1905 }
1906
1907 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
1908 self.mode.register_transmit_waker(cx);
1909 self.mode.tx_token()
1910 }
1911
1912 fn link_state(&mut self, cx: &mut core::task::Context<'_>) -> LinkState {
1913 self.mode.register_link_state_waker(cx);
1914 match self.mode.link_state() {
1915 super::LinkState::Down => LinkState::Down,
1916 super::LinkState::Up => LinkState::Up,
1917 }
1918 }
1919
1920 fn capabilities(&self) -> Capabilities {
1921 let mut caps = Capabilities::default();
1922 caps.max_transmission_unit = MTU;
1923 caps.max_burst_size =
1924 if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 {
1925 None
1926 } else {
1927 Some(esp_config_int!(
1928 usize,
1929 "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE"
1930 ))
1931 };
1932 caps
1933 }
1934
1935 fn hardware_address(&self) -> HardwareAddress {
1936 HardwareAddress::Ethernet(self.mac_address())
1937 }
1938 }
1939}
1940
1941#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)]
1943#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1944#[instability::unstable]
1945#[non_exhaustive]
1946pub enum PowerSaveMode {
1947 #[default]
1949 None,
1950 Minimum,
1953 Maximum,
1956}
1957
1958pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
1959 esp_wifi_result!(unsafe {
1960 crate::sys::include::esp_wifi_set_ps(match ps {
1961 PowerSaveMode::None => crate::sys::include::wifi_ps_type_t_WIFI_PS_NONE,
1962 PowerSaveMode::Minimum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM,
1963 PowerSaveMode::Maximum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM,
1964 })
1965 })?;
1966 Ok(())
1967}
1968
1969#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1973#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1974#[instability::unstable]
1975pub enum OperatingClass {
1976 AllEnvironments,
1979
1980 Outdoors,
1983
1984 Indoors,
1987
1988 NonCountryEntity,
1991
1992 Repr(u8),
1995}
1996
1997impl Default for OperatingClass {
1998 fn default() -> Self {
1999 OperatingClass::Repr(0) }
2001}
2002
2003impl OperatingClass {
2004 fn into_code(self) -> u8 {
2005 match self {
2006 OperatingClass::AllEnvironments => b' ',
2007 OperatingClass::Outdoors => b'O',
2008 OperatingClass::Indoors => b'I',
2009 OperatingClass::NonCountryEntity => b'X',
2010 OperatingClass::Repr(code) => code,
2011 }
2012 }
2013
2014 fn from_code(code: u8) -> Option<Self> {
2015 match code {
2016 b' ' => Some(OperatingClass::AllEnvironments),
2017 b'O' => Some(OperatingClass::Outdoors),
2018 b'I' => Some(OperatingClass::Indoors),
2019 b'X' => Some(OperatingClass::NonCountryEntity),
2020 code => Some(OperatingClass::Repr(code)),
2021 }
2022 }
2023}
2024
2025#[procmacros::doc_replace]
2026#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BuilderLite)]
2045#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2046#[instability::unstable]
2047pub struct CountryInfo {
2048 #[builder_lite(skip)]
2050 country: [u8; 2],
2051
2052 #[builder_lite(unstable)]
2054 operating_class: OperatingClass,
2055}
2056
2057impl From<[u8; 2]> for CountryInfo {
2058 fn from(country: [u8; 2]) -> Self {
2059 Self {
2060 country,
2061 operating_class: OperatingClass::default(),
2062 }
2063 }
2064}
2065
2066impl CountryInfo {
2067 fn into_blob(self) -> wifi_country_t {
2068 wifi_country_t {
2069 cc: [
2070 self.country[0],
2071 self.country[1],
2072 self.operating_class.into_code(),
2073 ],
2074 schan: 1,
2076 nchan: 13,
2077 max_tx_power: 20,
2078 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2079
2080 #[cfg(wifi_has_5g)]
2081 wifi_5g_channel_mask: 0,
2082 }
2083 }
2084
2085 #[cfg_attr(not(feature = "unstable"), expect(dead_code))]
2086 fn try_from_c(info: &wifi_country_t) -> Option<Self> {
2087 let cc = &info.cc;
2088 let operating_class = OperatingClass::from_code(cc[2])?;
2089
2090 Some(Self {
2091 country: [cc[0], cc[1]],
2092 operating_class,
2093 })
2094 }
2095}
2096
2097#[derive(Clone, BuilderLite, Debug, Hash, PartialEq, Eq)]
2099#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2100#[non_exhaustive]
2101pub struct ControllerConfig {
2102 #[builder_lite(into)]
2104 #[builder_lite(unstable)]
2105 country_info: CountryInfo,
2106 #[builder_lite(unstable)]
2108 rx_queue_size: usize,
2109 #[builder_lite(unstable)]
2111 tx_queue_size: usize,
2112
2113 #[builder_lite(unstable)]
2123 static_rx_buf_num: u8,
2124
2125 #[builder_lite(unstable)]
2142 dynamic_rx_buf_num: u16,
2143
2144 #[builder_lite(unstable)]
2156 static_tx_buf_num: u8,
2157
2158 #[builder_lite(unstable)]
2169 dynamic_tx_buf_num: u16,
2170
2171 #[builder_lite(unstable)]
2173 ampdu_rx_enable: bool,
2174
2175 #[builder_lite(unstable)]
2177 ampdu_tx_enable: bool,
2178
2179 #[builder_lite(unstable)]
2181 amsdu_tx_enable: bool,
2182
2183 #[builder_lite(unstable)]
2194 rx_ba_win: u8,
2195
2196 #[builder_lite(reference)]
2198 initial_config: Config,
2199}
2200
2201impl Default for ControllerConfig {
2202 fn default() -> Self {
2203 Self {
2204 rx_queue_size: 5,
2205 tx_queue_size: 3,
2206
2207 static_rx_buf_num: 10,
2208 dynamic_rx_buf_num: 32,
2209
2210 static_tx_buf_num: 0,
2211 dynamic_tx_buf_num: 32,
2212
2213 ampdu_rx_enable: true,
2214 ampdu_tx_enable: true,
2215 amsdu_tx_enable: false,
2216
2217 rx_ba_win: 6,
2218
2219 country_info: CountryInfo::from(*b"CN"),
2220
2221 initial_config: Config::Station(StationConfig::default()),
2222 }
2223 }
2224}
2225
2226impl ControllerConfig {
2227 fn validate(&self) {
2228 if self.rx_ba_win as u16 >= self.dynamic_rx_buf_num {
2229 warn!("RX BA window size should be less than the number of dynamic RX buffers.");
2230 }
2231 if self.rx_ba_win as u16 >= 2 * (self.static_rx_buf_num as u16) {
2232 warn!("RX BA window size should be less than twice the number of static RX buffers.");
2233 }
2234 }
2235}
2236
2237#[derive(Debug)]
2242#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2243pub struct WifiController<'d> {
2244 _guard: RadioRefGuard,
2245 _phantom: PhantomData<&'d ()>,
2246}
2247
2248impl Drop for WifiController<'_> {
2249 fn drop(&mut self) {
2250 state::locked(|| {
2251 set_access_point_state(WifiAccessPointState::Uninitialized);
2252 set_station_state(WifiStationState::Uninitialized);
2253
2254 if let Err(e) = crate::wifi::wifi_deinit() {
2255 warn!("Failed to cleanly deinit wifi: {:?}", e);
2256 }
2257
2258 #[cfg(all(rng_trng_supported, feature = "unstable"))]
2259 esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe {
2260 esp_hal::Internal::conjure()
2261 });
2262 })
2263 }
2264}
2265
2266impl<'d> WifiController<'d> {
2267 #[procmacros::doc_replace]
2268 pub fn new(
2290 device: crate::hal::peripherals::WIFI<'d>,
2291 config: ControllerConfig,
2292 ) -> Result<Self, WifiError> {
2293 let _guard = RadioRefGuard::new();
2294
2295 config.validate();
2296
2297 event::enable_wifi_events(
2298 WifiEvent::StationStart
2299 | WifiEvent::StationStop
2300 | WifiEvent::StationConnected
2301 | WifiEvent::StationDisconnected
2302 | WifiEvent::AccessPointStart
2303 | WifiEvent::AccessPointStop
2304 | WifiEvent::AccessPointStationConnected
2305 | WifiEvent::AccessPointStationDisconnected
2306 | WifiEvent::ScanDone,
2307 );
2308
2309 unsafe {
2310 internal::G_CONFIG = wifi_init_config_t {
2311 osi_funcs: (&raw const internal::__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(),
2312
2313 wpa_crypto_funcs: g_wifi_default_wpa_crypto_funcs,
2314 static_rx_buf_num: config.static_rx_buf_num as _,
2315 dynamic_rx_buf_num: config.dynamic_rx_buf_num as _,
2316 tx_buf_type: crate::sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32,
2317 static_tx_buf_num: config.static_tx_buf_num as _,
2318 dynamic_tx_buf_num: config.dynamic_tx_buf_num as _,
2319 rx_mgmt_buf_type: crate::sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32,
2320 rx_mgmt_buf_num: crate::sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32,
2321 cache_tx_buf_num: crate::sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32,
2322 csi_enable: cfg!(feature = "csi") as i32,
2323 ampdu_rx_enable: config.ampdu_rx_enable as _,
2324 ampdu_tx_enable: config.ampdu_tx_enable as _,
2325 amsdu_tx_enable: config.amsdu_tx_enable as _,
2326 nvs_enable: 0,
2327 nano_enable: 0,
2328 rx_ba_win: config.rx_ba_win as _,
2329 wifi_task_core_id: Cpu::current() as _,
2330 beacon_max_len: crate::sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32,
2331 mgmt_sbuf_num: crate::sys::include::WIFI_MGMT_SBUF_NUM as i32,
2332 feature_caps: internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS,
2333 sta_disconnected_pm: false,
2334 espnow_max_encrypt_num: crate::sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM
2335 as i32,
2336
2337 tx_hetb_queue_num: 3,
2338 dump_hesigb_enable: false,
2339
2340 magic: WIFI_INIT_CONFIG_MAGIC as i32,
2341 };
2342 }
2343
2344 DATA_QUEUE_RX_AP.with(|queue| queue.change_capacity(config.rx_queue_size))?;
2345 DATA_QUEUE_RX_STA.with(|queue| queue.change_capacity(config.rx_queue_size))?;
2346
2347 TX_QUEUE_SIZE.store(config.tx_queue_size, Ordering::Relaxed);
2348
2349 crate::wifi::wifi_init(device)?;
2350
2351 #[cfg(all(rng_trng_supported, feature = "unstable"))]
2353 unsafe {
2354 esp_hal::rng::TrngSource::increase_entropy_source_counter()
2355 };
2356
2357 let mut controller = WifiController {
2361 _guard,
2362 _phantom: Default::default(),
2363 };
2364
2365 controller.set_country_info(&config.country_info)?;
2366 controller.set_power_saving(PowerSaveMode::default())?;
2368
2369 controller.set_config(&config.initial_config)?;
2370
2371 Ok(controller)
2372 }
2373}
2374
2375impl WifiController<'_> {
2376 #[cfg(all(feature = "esp-now", feature = "unstable"))]
2382 #[instability::unstable]
2383 pub fn esp_now(&self) -> crate::esp_now::EspNow<'_> {
2384 crate::esp_now::EspNow::new_internal()
2385 }
2386
2387 #[cfg(all(feature = "sniffer", feature = "unstable"))]
2393 #[instability::unstable]
2394 pub fn sniffer(&self) -> Sniffer<'_> {
2395 Sniffer::new()
2396 }
2397
2398 #[cfg(all(feature = "csi", feature = "unstable"))]
2400 #[instability::unstable]
2401 pub fn set_csi(
2402 &mut self,
2403 mut csi: csi::CsiConfig,
2404 cb: impl FnMut(crate::wifi::csi::WifiCsiInfo<'_>) + Send,
2405 ) -> Result<(), WifiError> {
2406 csi.apply_config()?;
2407 csi.set_receive_cb(cb)?;
2408 csi.set_csi(true)?;
2409
2410 Ok(())
2411 }
2412
2413 #[procmacros::doc_replace]
2414 #[instability::unstable]
2443 pub fn set_protocols(&mut self, protocols: Protocols) -> Result<(), WifiError> {
2444 let mode = self.mode()?;
2445 if mode.is_station() {
2446 esp_wifi_result!(unsafe {
2447 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_STA, &mut protocols.to_raw())
2448 })?;
2449 }
2450 if mode.is_access_point() {
2451 esp_wifi_result!(unsafe {
2452 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_AP, &mut protocols.to_raw())
2453 })?;
2454 }
2455
2456 Ok(())
2457 }
2458
2459 fn apply_protocols(iface: wifi_interface_t, protocols: &Protocols) -> Result<(), WifiError> {
2460 esp_wifi_result!(unsafe { esp_wifi_set_protocols(iface, &mut protocols.to_raw()) })?;
2461 Ok(())
2462 }
2463
2464 #[procmacros::doc_replace]
2465 #[instability::unstable]
2478 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2479 apply_power_saving(ps)
2480 }
2481
2482 fn set_country_info(&mut self, country: &CountryInfo) -> Result<(), WifiError> {
2483 unsafe {
2484 let country = country.into_blob();
2485 esp_wifi_result!(esp_wifi_set_country(&country))?;
2486 }
2487 Ok(())
2488 }
2489
2490 #[procmacros::doc_replace]
2491 pub fn rssi(&self) -> Result<i32, WifiError> {
2521 if self.mode()?.is_station() {
2522 let mut rssi: i32 = 0;
2523 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
2525 Ok(rssi)
2526 } else {
2527 Err(WifiError::Unsupported)
2528 }
2529 }
2530
2531 #[procmacros::doc_replace]
2532 pub fn ap_info(&self) -> Result<AccessPointInfo, WifiError> {
2562 if self.mode()?.is_station() {
2563 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2564 esp_wifi_result!(unsafe { esp_wifi_sta_get_ap_info(record.as_mut_ptr()) })?;
2565
2566 let record = unsafe { MaybeUninit::assume_init(record) };
2567 let ap_info = convert_ap_info(&record);
2568 Ok(ap_info)
2569 } else {
2570 Err(WifiError::Unsupported)
2571 }
2572 }
2573
2574 #[procmacros::doc_replace]
2575 pub fn set_config(&mut self, conf: &Config) -> Result<(), WifiError> {
2604 struct ResetModeOnDrop;
2608 impl ResetModeOnDrop {
2609 fn defuse(self) {
2611 core::mem::forget(self);
2612 }
2613 }
2614 impl Drop for ResetModeOnDrop {
2615 fn drop(&mut self) {
2616 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
2617 unwrap!(WifiController::stop_impl());
2618 }
2619 }
2620
2621 let reset_mode_on_error = ResetModeOnDrop;
2622
2623 conf.validate()?;
2624
2625 let mut previous_mode = 0u32;
2626 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut previous_mode) })?;
2627
2628 let mode = match conf {
2629 Config::Station(_) => wifi_mode_t_WIFI_MODE_STA,
2630 Config::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2631 Config::AccessPointStation(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2632 #[cfg(feature = "wifi-eap")]
2633 Config::EapStation(_) => wifi_mode_t_WIFI_MODE_STA,
2634 };
2635
2636 if previous_mode != mode {
2637 Self::stop_impl()?;
2638 }
2639
2640 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2641
2642 match conf {
2643 Config::Station(config) => {
2644 self.apply_sta_config(config)?;
2645 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
2646 }
2647 Config::AccessPoint(config) => {
2648 self.apply_ap_config(config)?;
2649 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &config.protocols)?;
2650 }
2651 Config::AccessPointStation(sta_config, ap_config) => {
2652 self.apply_ap_config(ap_config)?;
2653 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &ap_config.protocols)?;
2654 self.apply_sta_config(sta_config)?;
2655 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &sta_config.protocols)?;
2656 }
2657 #[cfg(feature = "wifi-eap")]
2658 Config::EapStation(config) => {
2659 self.apply_sta_eap_config(config)?;
2660 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
2661 }
2662 }
2663
2664 if previous_mode != mode {
2665 set_access_point_state(WifiAccessPointState::Starting);
2666 set_station_state(WifiStationState::Starting);
2667
2668 esp_wifi_result!(unsafe { esp_wifi_start() })?;
2670 }
2671
2672 reset_mode_on_error.defuse();
2673
2674 Ok(())
2675 }
2676
2677 #[cfg_attr(
2682 wifi_has_5g,
2683 doc = r"
2684When the WiFi band mode is set to [`BandMode::_5G`], it operates exclusively on the 5GHz channels.
2685
2686When the WiFi band mode is set to [`BandMode::Auto`], it can operate on both the 2.4GHz and
26875GHz channels.
2688
2689When a WiFi band mode change triggers a band change, if no channel is set for the current
2690band, a default channel will be assigned: channel 1 for 2.4G band and channel 36 for 5G
2691band.
2692"
2693 )]
2694 #[instability::unstable]
2696 pub fn set_band_mode(&mut self, band_mode: BandMode) -> Result<(), WifiError> {
2697 esp_wifi_result!(unsafe { esp_wifi_set_band_mode(band_mode.to_raw()) })
2699 }
2700
2701 #[instability::unstable]
2707 pub fn set_bandwidths(&mut self, bandwidths: Bandwidths) -> Result<(), WifiError> {
2708 let mode = self.mode()?;
2709 if mode.is_station() {
2710 esp_wifi_result!(unsafe {
2711 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bandwidths.to_raw())
2712 })?;
2713 }
2714 if mode.is_access_point() {
2715 esp_wifi_result!(unsafe {
2716 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bandwidths.to_raw())
2717 })?;
2718 }
2719
2720 Ok(())
2721 }
2722
2723 #[instability::unstable]
2730 pub fn bandwidths(&self) -> Result<Bandwidths, WifiError> {
2731 let mut bw = wifi_bandwidths_t {
2732 ghz_2g: 0,
2733 ghz_5g: 0,
2734 };
2735
2736 let mode = self.mode()?;
2737 if mode.is_station() {
2738 esp_wifi_result!(unsafe {
2739 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bw)
2740 })?;
2741 }
2742 if mode.is_access_point() {
2743 esp_wifi_result!(unsafe {
2744 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bw)
2745 })?;
2746 }
2747
2748 Ok(Bandwidths {
2749 _2_4: Bandwidth::from_raw(bw.ghz_2g),
2750 #[cfg(wifi_has_5g)]
2751 _5: Bandwidth::from_raw(bw.ghz_5g),
2752 })
2753 }
2754
2755 #[instability::unstable]
2757 pub fn channel(&self) -> Result<(u8, SecondaryChannel), WifiError> {
2758 let mut primary = 0;
2759 let mut secondary = 0;
2760
2761 esp_wifi_result!(unsafe { esp_wifi_get_channel(&mut primary, &mut secondary) })?;
2762
2763 Ok((primary, SecondaryChannel::from_raw(secondary)))
2764 }
2765
2766 #[cfg_attr(
2768 wifi_has_5g,
2769 doc = r"
2770
2771When operating in 5 GHz band, the second channel is automatically determined by the primary
2772channel according to the 802.11 standard. Any manually configured second channel will be
2773ignored."
2774 )]
2775 #[instability::unstable]
2776 pub fn set_channel(
2777 &mut self,
2778 primary: u8,
2779 secondary: SecondaryChannel,
2780 ) -> Result<(), WifiError> {
2781 esp_wifi_result!(unsafe { esp_wifi_set_channel(primary, secondary as u32) })?;
2782
2783 Ok(())
2784 }
2785
2786 #[instability::unstable]
2790 pub fn set_max_tx_power(&mut self, power: i8) -> Result<(), WifiError> {
2791 esp_wifi_result!(unsafe { esp_wifi_set_max_tx_power(power) })
2792 }
2793
2794 fn stop_impl() -> Result<(), WifiError> {
2795 set_access_point_state(WifiAccessPointState::Stopping);
2796 set_station_state(WifiStationState::Stopping);
2797
2798 esp_wifi_result!(unsafe { esp_wifi_stop() })
2799 }
2800
2801 fn connect_impl(&mut self) -> Result<(), WifiError> {
2802 set_station_state(WifiStationState::Connecting);
2803
2804 esp_wifi_result!(unsafe { esp_wifi_connect_internal() })
2806 }
2807
2808 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
2809 set_station_state(WifiStationState::Disconnecting);
2810
2811 esp_wifi_result!(unsafe { esp_wifi_disconnect_internal() })
2813 }
2814
2815 #[procmacros::doc_replace]
2816 #[instability::unstable]
2831 pub fn is_connected(&self) -> bool {
2832 matches!(
2833 crate::wifi::station_state(),
2834 crate::wifi::WifiStationState::Connected
2835 )
2836 }
2837
2838 fn mode(&self) -> Result<WifiMode, WifiError> {
2839 WifiMode::current()
2840 }
2841
2842 #[procmacros::doc_replace]
2843 pub async fn scan_async(
2862 &mut self,
2863 config: &ScanConfig,
2864 ) -> Result<Vec<AccessPointInfo>, WifiError> {
2865 let mut subscriber = EVENT_CHANNEL
2866 .subscriber()
2867 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2868
2869 esp_wifi_result!(wifi_start_scan(false, *config))?;
2870
2871 let guard = FreeApListOnDrop;
2873
2874 loop {
2875 let event = subscriber.next_message_pure().await;
2876 if let EventInfo::ScanDone {
2877 status: _status,
2878 number: _number,
2879 scan_id: _scan_id,
2880 } = event
2881 {
2882 break;
2883 }
2884 }
2885
2886 guard.defuse();
2887
2888 let limit = config.max.unwrap_or(usize::MAX);
2889 Ok(ScanResults::new(self)?.take(limit).collect::<Vec<_>>())
2890 }
2891
2892 #[procmacros::doc_replace]
2893 pub async fn connect_async(&mut self) -> Result<sta::ConnectedInfo, WifiError> {
2922 let mut subscriber = EVENT_CHANNEL
2923 .subscriber()
2924 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2925
2926 self.connect_impl()?;
2927
2928 let result = loop {
2929 let event = subscriber.next_message().await;
2930 if let embassy_sync::pubsub::WaitResult::Message(event) = event {
2931 match event {
2932 EventInfo::StationConnected { .. } => {
2933 break event;
2934 }
2935 EventInfo::StationDisconnected { .. } => {
2936 break event;
2937 }
2938 _ => (),
2939 }
2940 }
2941 };
2942
2943 match result {
2944 event::EventInfo::StationConnected {
2945 ssid,
2946 bssid,
2947 channel,
2948 authmode,
2949 aid,
2950 } => Ok(sta::ConnectedInfo {
2951 ssid,
2952 bssid,
2953 channel,
2954 authmode: AuthenticationMethod::from_raw(authmode),
2955 aid,
2956 }),
2957 event::EventInfo::StationDisconnected {
2958 ssid,
2959 bssid,
2960 reason,
2961 rssi,
2962 } => Err(WifiError::Disconnected(sta::DisconnectedInfo {
2963 ssid,
2964 bssid,
2965 reason: DisconnectReason::from_raw(reason),
2966 rssi,
2967 })),
2968 _ => unreachable!(),
2969 }
2970 }
2971
2972 #[procmacros::doc_replace]
2973 pub async fn disconnect_async(&mut self) -> Result<sta::DisconnectedInfo, WifiError> {
2995 if !self.is_connected() {
2998 return Err(WifiError::NotConnected);
2999 }
3000
3001 let mut subscriber = EVENT_CHANNEL
3002 .subscriber()
3003 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3004
3005 self.disconnect_impl()?;
3006
3007 loop {
3008 let event = subscriber.next_message_pure().await;
3009
3010 if let event::EventInfo::StationDisconnected {
3011 ssid,
3012 bssid,
3013 reason,
3014 rssi,
3015 } = event
3016 {
3017 break Ok(sta::DisconnectedInfo {
3018 ssid,
3019 bssid,
3020 reason: DisconnectReason::from_raw(reason),
3021 rssi,
3022 });
3023 }
3024 }
3025 }
3026
3027 pub async fn wait_for_disconnect_async(&self) -> Result<sta::DisconnectedInfo, WifiError> {
3029 if !self.is_connected() {
3032 return Err(WifiError::NotConnected);
3033 }
3034
3035 let mut subscriber = EVENT_CHANNEL
3036 .subscriber()
3037 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3038
3039 loop {
3040 let event = subscriber.next_message_pure().await;
3041
3042 if let event::EventInfo::StationDisconnected {
3043 ssid,
3044 bssid,
3045 reason,
3046 rssi,
3047 } = event
3048 {
3049 break Ok(sta::DisconnectedInfo {
3050 ssid,
3051 bssid,
3052 reason: DisconnectReason::from_raw(reason),
3053 rssi,
3054 });
3055 }
3056 }
3057 }
3058
3059 pub async fn wait_for_access_point_connected_event_async(
3061 &self,
3062 ) -> Result<ap::EventInfo, WifiError> {
3063 let mut subscriber = EVENT_CHANNEL
3064 .subscriber()
3065 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3066
3067 loop {
3068 let event = subscriber.next_message_pure().await;
3069
3070 match event {
3071 event::EventInfo::AccessPointStationConnected {
3072 mac,
3073 aid,
3074 is_mesh_child,
3075 } => {
3076 break Ok(ap::EventInfo::Connected(ap::ConnectedInfo {
3077 mac,
3078 aid,
3079 is_mesh_child,
3080 }));
3081 }
3082 event::EventInfo::AccessPointStationDisconnected {
3083 mac,
3084 aid,
3085 is_mesh_child,
3086 reason,
3087 } => {
3088 break Ok(ap::EventInfo::Disconnected(ap::DisconnectedInfo {
3089 mac,
3090 aid: aid as u16,
3091 is_mesh_child,
3092 reason: DisconnectReason::from_raw(reason),
3093 }));
3094 }
3095 _ => (),
3096 }
3097 }
3098 }
3099
3100 #[instability::unstable]
3106 pub fn subscribe<'a>(&'a self) -> Result<event::EventSubscriber<'a>, WifiError> {
3107 if let Ok(subscriber) = EVENT_CHANNEL.subscriber() {
3108 return Ok(event::EventSubscriber::new(subscriber));
3109 }
3110
3111 Err(WifiError::Failed)
3112 }
3113
3114 fn apply_ap_config(&mut self, config: &AccessPointConfig) -> Result<(), WifiError> {
3115 let mut cfg = wifi_config_t {
3116 ap: wifi_ap_config_t {
3117 ssid: [0; 32],
3118 password: [0; 64],
3119 ssid_len: 0,
3120 channel: config.channel,
3121 authmode: config.auth_method.to_raw(),
3122 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
3123 max_connection: config.max_connections as u8,
3124 beacon_interval: 100,
3125 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
3126 ftm_responder: false,
3127 pmf_cfg: wifi_pmf_config_t {
3128 capable: true,
3129 required: false,
3130 },
3131 sae_pwe_h2e: 0,
3132 csa_count: 3,
3133 dtim_period: config.dtim_period,
3134 transition_disable: 0,
3135 sae_ext: 0,
3136 bss_max_idle_cfg: include::wifi_bss_max_idle_config_t {
3137 period: 0,
3138 protected_keep_alive: false,
3139 },
3140 gtk_rekey_interval: 0,
3141 },
3142 };
3143
3144 if config.auth_method == AuthenticationMethod::None && !config.password.is_empty() {
3145 return Err(WifiError::InvalidArguments);
3146 }
3147
3148 unsafe {
3149 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3150 cfg.ap.ssid_len = config.ssid.len() as u8;
3151 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
3152
3153 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
3154 }
3155 }
3156
3157 fn apply_sta_config(&mut self, config: &StationConfig) -> Result<(), WifiError> {
3158 let mut cfg = wifi_config_t {
3159 sta: wifi_sta_config_t {
3160 ssid: [0; 32],
3161 password: [0; 64],
3162 scan_method: config.scan_method as c_types::c_uint,
3163 bssid_set: config.bssid.is_some(),
3164 bssid: config.bssid.unwrap_or_default(),
3165 channel: config.channel.unwrap_or(0),
3166 listen_interval: config.listen_interval,
3167 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3168 threshold: wifi_scan_threshold_t {
3169 rssi: -99,
3170 authmode: config.auth_method.to_raw(),
3171 rssi_5g_adjustment: 0,
3172 },
3173 pmf_cfg: wifi_pmf_config_t {
3174 capable: true,
3175 required: false,
3176 },
3177 sae_pwe_h2e: 3,
3178 _bitfield_align_1: [0; 0],
3179 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3180 failure_retry_cnt: config.failure_retry_cnt,
3181 _bitfield_align_2: [0; 0],
3182 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3183 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3185 },
3186 };
3187
3188 if config.auth_method == AuthenticationMethod::None && !config.password.is_empty() {
3189 return Err(WifiError::InvalidArguments);
3190 }
3191
3192 unsafe {
3193 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3194 cfg.sta.password[0..(config.password.len())]
3195 .copy_from_slice(config.password.as_bytes());
3196
3197 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
3198 }
3199 }
3200
3201 #[cfg(feature = "wifi-eap")]
3202 fn apply_sta_eap_config(&mut self, config: &EapStationConfig) -> Result<(), WifiError> {
3203 let mut cfg = wifi_config_t {
3204 sta: wifi_sta_config_t {
3205 ssid: [0; 32],
3206 password: [0; 64],
3207 scan_method: config.scan_method as c_types::c_uint,
3208 bssid_set: config.bssid.is_some(),
3209 bssid: config.bssid.unwrap_or_default(),
3210 channel: config.channel.unwrap_or(0),
3211 listen_interval: config.listen_interval,
3212 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3213 threshold: wifi_scan_threshold_t {
3214 rssi: -99,
3215 authmode: config.auth_method.to_raw(),
3216 rssi_5g_adjustment: 0,
3217 },
3218 pmf_cfg: wifi_pmf_config_t {
3219 capable: true,
3220 required: false,
3221 },
3222 sae_pwe_h2e: 3,
3223 _bitfield_align_1: [0; 0],
3224 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3225 failure_retry_cnt: config.failure_retry_cnt,
3226 _bitfield_align_2: [0; 0],
3227 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3228 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3230 },
3231 };
3232
3233 unsafe {
3234 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3235 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
3236
3237 if let Some(identity) = &config.identity {
3238 esp_wifi_result!(esp_eap_client_set_identity(
3239 identity.as_str().as_ptr(),
3240 identity.len() as i32
3241 ))?;
3242 } else {
3243 esp_eap_client_clear_identity();
3244 }
3245
3246 if let Some(username) = &config.username {
3247 esp_wifi_result!(esp_eap_client_set_username(
3248 username.as_str().as_ptr(),
3249 username.len() as i32
3250 ))?;
3251 } else {
3252 esp_eap_client_clear_username();
3253 }
3254
3255 if let Some(password) = &config.password {
3256 esp_wifi_result!(esp_eap_client_set_password(
3257 password.as_str().as_ptr(),
3258 password.len() as i32
3259 ))?;
3260 } else {
3261 esp_eap_client_clear_password();
3262 }
3263
3264 if let Some(new_password) = &config.new_password {
3265 esp_wifi_result!(esp_eap_client_set_new_password(
3266 new_password.as_str().as_ptr(),
3267 new_password.len() as i32
3268 ))?;
3269 } else {
3270 esp_eap_client_clear_new_password();
3271 }
3272
3273 if let Some(pac_file) = &config.pac_file {
3274 esp_wifi_result!(esp_eap_client_set_pac_file(
3275 pac_file.as_ptr(),
3276 pac_file.len() as i32
3277 ))?;
3278 }
3279
3280 if let Some(phase2_method) = &config.ttls_phase2_method {
3281 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
3282 phase2_method.to_raw()
3283 ))?;
3284 }
3285
3286 if let Some(ca_cert) = config.ca_cert {
3287 esp_wifi_result!(esp_eap_client_set_ca_cert(
3288 ca_cert.as_ptr(),
3289 ca_cert.len() as i32
3290 ))?;
3291 } else {
3292 esp_eap_client_clear_ca_cert();
3293 }
3294
3295 if let Some((cert, key, password)) = config.certificate_and_key {
3296 let (pwd, pwd_len) = if let Some(pwd) = password {
3297 (pwd.as_ptr(), pwd.len() as i32)
3298 } else {
3299 (core::ptr::null(), 0)
3300 };
3301
3302 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
3303 cert.as_ptr(),
3304 cert.len() as i32,
3305 key.as_ptr(),
3306 key.len() as i32,
3307 pwd,
3308 pwd_len,
3309 ))?;
3310 } else {
3311 esp_eap_client_clear_certificate_and_key();
3312 }
3313
3314 if let Some(cfg) = &config.eap_fast_config {
3315 let params = esp_eap_fast_config {
3316 fast_provisioning: cfg.fast_provisioning as i32,
3317 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
3318 fast_pac_format_binary: cfg.fast_pac_format_binary,
3319 };
3320 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
3321 }
3322
3323 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
3324
3325 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
3332
3333 Ok(())
3334 }
3335 }
3336}