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};
50use core::{
51 fmt::{Debug, Write},
52 marker::PhantomData,
53 mem::MaybeUninit,
54 ptr::addr_of,
55};
56
57use docsplay::Display;
58use enumset::{EnumSet, EnumSetType};
59use esp_config::esp_config_int;
60use esp_hal::system::Cpu;
61#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
62use esp_hal::time::{Duration, Instant};
63use esp_sync::NonReentrantMutex;
64use event::EVENT_CHANNEL;
65use portable_atomic::{AtomicUsize, Ordering};
66use procmacros::BuilderLite;
67
68pub(crate) use self::os_adapter::*;
69#[cfg(all(feature = "sniffer", feature = "unstable"))]
70#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
71use self::sniffer::Sniffer;
72#[cfg(feature = "wifi-eap")]
73use self::sta::eap::EapStationConfig;
74use self::{
75 ap::{AccessPointConfig, AccessPointInfo, convert_ap_info},
76 private::PacketBuffer,
77 scan::{FreeApListOnDrop, ScanConfig, ScanResults, ScanTypeConfig},
78 sta::StationConfig,
79 state::*,
80};
81use crate::{
82 RadioRefGuard,
83 hal::ram,
84 sys::{
85 c_types,
86 include::{self, *},
87 },
88 wifi::event::{EventInfo, WifiEvent},
89};
90pub mod ap;
91
92unstable_module!(
93 #[cfg(all(feature = "csi", wifi_csi_supported))]
94 #[cfg_attr(docsrs, doc(cfg(feature = "csi")))]
95 pub mod csi;
96 pub mod event;
97 #[cfg(feature = "sniffer")]
98 #[cfg_attr(docsrs, doc(cfg(feature = "sniffer")))]
99 pub mod sniffer;
100);
101
102pub mod scan;
103pub mod sta;
104
105pub(crate) mod os_adapter;
106pub(crate) mod state;
107
108mod internal;
109
110const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU");
111
112#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115#[non_exhaustive]
116pub enum AuthenticationMethod {
117 None,
119
120 Wep,
122
123 Wpa,
125
126 #[default]
128 Wpa2Personal,
129
130 WpaWpa2Personal,
132
133 Wpa2Enterprise,
135
136 Wpa3Personal,
138
139 Wpa2Wpa3Personal,
141
142 WapiPersonal,
144
145 Owe,
147
148 Wpa3EntSuiteB192Bit,
150
151 Wpa3ExtPsk,
155
156 Wpa3ExtPskMixed,
160
161 Dpp,
163
164 Wpa3Enterprise,
166
167 Wpa2Wpa3Enterprise,
169
170 WpaEnterprise,
172}
173
174#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
176#[cfg_attr(feature = "defmt", derive(defmt::Format))]
177#[non_exhaustive]
178pub struct Protocols {
179 _2_4: EnumSet<Protocol>,
181 #[cfg(wifi_has_5g)]
183 _5: EnumSet<Protocol>,
184}
185
186impl Default for Protocols {
187 fn default() -> Self {
188 Self {
189 _2_4: Protocol::B | Protocol::G | Protocol::N,
190 #[cfg(wifi_has_5g)]
191 _5: Protocol::AC | Protocol::A | Protocol::AX,
192 }
193 }
194}
195
196impl Protocols {
197 fn to_raw(self) -> wifi_protocols_t {
198 wifi_protocols_t {
199 ghz_2g: to_mask(self._2_4),
200 #[cfg(wifi_has_5g)]
201 ghz_5g: to_mask(self._5),
202 #[cfg(not(wifi_has_5g))]
203 ghz_5g: 0,
204 }
205 }
206}
207
208#[cfg_attr(docsrs, procmacros::doc_replace(
209 "hint_5g" => {
210 cfg(wifi_has_5g) => "The default protocol is AC/A/AX for band mode 5G.",
211 _ => ""
212 },
213))]
214#[derive(Debug, PartialOrd, Hash, EnumSetType)]
219#[cfg_attr(feature = "defmt", derive(defmt::Format))]
220#[non_exhaustive]
221pub enum Protocol {
222 B,
224
225 G,
227
228 N,
230
231 LR,
233
234 A,
236
237 AC,
239
240 AX,
242}
243
244impl Protocol {
245 fn to_mask(self) -> u16 {
246 let mask = match self {
247 Protocol::B => WIFI_PROTOCOL_11B,
248 Protocol::G => WIFI_PROTOCOL_11G,
249 Protocol::N => WIFI_PROTOCOL_11N,
250 Protocol::LR => WIFI_PROTOCOL_LR,
251 Protocol::A => WIFI_PROTOCOL_11A,
252 Protocol::AC => WIFI_PROTOCOL_11AC,
253 Protocol::AX => WIFI_PROTOCOL_11AX,
254 };
255 mask as _
256 }
257}
258
259fn to_mask(protocols: EnumSet<Protocol>) -> u16 {
260 protocols.iter().fold(0, |acc, p| acc | p.to_mask())
261}
262
263#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
265#[cfg_attr(feature = "defmt", derive(defmt::Format))]
266pub enum SecondaryChannel {
267 #[default]
269 None,
270
271 Above,
273
274 Below,
276}
277
278impl SecondaryChannel {
279 fn from_raw(raw: u32) -> Self {
280 match raw {
281 0 => SecondaryChannel::None,
282 1 => SecondaryChannel::Above,
283 2 => SecondaryChannel::Below,
284 _ => panic!("Invalid secondary channel value: {}", raw),
285 }
286 }
287
288 #[cfg(any(feature = "sniffer", feature = "esp-now"))]
289 fn from_raw_or_default(raw: u32) -> Self {
290 match raw {
291 0 => SecondaryChannel::None,
292 1 => SecondaryChannel::Above,
293 2 => SecondaryChannel::Below,
294 _ => SecondaryChannel::None,
295 }
296 }
297}
298
299#[cfg_attr(docsrs, procmacros::doc_replace(
300 "default_band_mode" => {
301 cfg(wifi_has_5g) => "BandMode::Auto",
302 _ => "BandMode::_2_4G"
303 },
304))]
305#[allow(clippy::large_enum_variant)]
309#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
310#[cfg_attr(feature = "defmt", derive(defmt::Format))]
311#[non_exhaustive]
312pub enum BandMode {
313 #[cfg_attr(not(wifi_has_5g), default)]
315 _2_4G,
316 #[cfg(wifi_has_5g)]
318 _5G,
319 #[cfg_attr(wifi_has_5g, default)]
321 #[cfg(wifi_has_5g)]
322 Auto,
323}
324
325impl BandMode {
326 fn to_raw(&self) -> u32 {
327 match self {
328 BandMode::_2_4G => wifi_band_mode_t_WIFI_BAND_MODE_2G_ONLY,
329 #[cfg(wifi_has_5g)]
330 BandMode::_5G => wifi_band_mode_t_WIFI_BAND_MODE_5G_ONLY,
331 #[cfg(wifi_has_5g)]
332 BandMode::Auto => wifi_band_mode_t_WIFI_BAND_MODE_AUTO,
333 }
334 }
335}
336
337#[allow(clippy::large_enum_variant)]
339#[derive(Clone, Debug, PartialEq, Eq, Hash)]
340#[cfg_attr(feature = "defmt", derive(defmt::Format))]
341#[non_exhaustive]
342pub enum Config {
343 Station(StationConfig),
345
346 AccessPoint(AccessPointConfig),
348
349 AccessPointStation(StationConfig, AccessPointConfig),
351
352 #[cfg(feature = "wifi-eap")]
354 EapStation(EapStationConfig),
355}
356
357impl Config {
358 fn validate(&self) -> Result<(), WifiError> {
359 match self {
360 Config::Station(station_configuration) => station_configuration.validate(),
361 Config::AccessPoint(access_point_configuration) => {
362 access_point_configuration.validate()
363 }
364 Config::AccessPointStation(station_configuration, access_point_configuration) => {
365 station_configuration.validate()?;
366 access_point_configuration.validate()
367 }
368 #[cfg(feature = "wifi-eap")]
369 Config::EapStation(eap_station_configuration) => eap_station_configuration.validate(),
370 }
371 }
372}
373
374impl AuthenticationMethod {
375 fn to_raw(self) -> wifi_auth_mode_t {
376 match self {
377 AuthenticationMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
378 AuthenticationMethod::Wep => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
379 AuthenticationMethod::Wpa => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
380 AuthenticationMethod::Wpa2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
381 AuthenticationMethod::WpaWpa2Personal => {
382 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK
383 }
384 AuthenticationMethod::Wpa2Enterprise => {
385 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE
386 }
387 AuthenticationMethod::Wpa3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
388 AuthenticationMethod::Wpa2Wpa3Personal => {
389 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK
390 }
391 AuthenticationMethod::WapiPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
392 AuthenticationMethod::Owe => include::wifi_auth_mode_t_WIFI_AUTH_OWE,
393 AuthenticationMethod::Wpa3EntSuiteB192Bit => {
394 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192
395 }
396 AuthenticationMethod::Wpa3ExtPsk => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK,
397 AuthenticationMethod::Wpa3ExtPskMixed => {
398 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE
399 }
400 AuthenticationMethod::Dpp => include::wifi_auth_mode_t_WIFI_AUTH_DPP,
401 AuthenticationMethod::Wpa3Enterprise => {
402 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE
403 }
404 AuthenticationMethod::Wpa2Wpa3Enterprise => {
405 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE
406 }
407 AuthenticationMethod::WpaEnterprise => {
408 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE
409 }
410 }
411 }
412
413 fn from_raw(raw: wifi_auth_mode_t) -> Self {
414 match raw {
415 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthenticationMethod::None,
416 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthenticationMethod::Wep,
417 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthenticationMethod::Wpa,
418 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthenticationMethod::Wpa2Personal,
419 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => {
420 AuthenticationMethod::WpaWpa2Personal
421 }
422 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => {
423 AuthenticationMethod::Wpa2Enterprise
424 }
425 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthenticationMethod::Wpa3Personal,
426 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => {
427 AuthenticationMethod::Wpa2Wpa3Personal
428 }
429 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthenticationMethod::WapiPersonal,
430 include::wifi_auth_mode_t_WIFI_AUTH_OWE => AuthenticationMethod::Owe,
431 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192 => {
432 AuthenticationMethod::Wpa3EntSuiteB192Bit
433 }
434 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK => AuthenticationMethod::Wpa3ExtPsk,
435 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE => {
436 AuthenticationMethod::Wpa3ExtPskMixed
437 }
438 include::wifi_auth_mode_t_WIFI_AUTH_DPP => AuthenticationMethod::Dpp,
439 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE => {
440 AuthenticationMethod::Wpa3Enterprise
441 }
442 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE => {
443 AuthenticationMethod::Wpa2Wpa3Enterprise
444 }
445 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE => {
446 AuthenticationMethod::WpaEnterprise
447 }
448 _ => AuthenticationMethod::None,
455 }
456 }
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
461#[cfg_attr(feature = "defmt", derive(defmt::Format))]
462#[non_exhaustive]
463enum WifiMode {
464 Station,
466 AccessPoint,
468 AccessPointStation,
470}
471
472impl WifiMode {
473 pub(crate) fn current() -> Result<Self, WifiError> {
474 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
475 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
476
477 Ok(Self::from_raw(mode))
478 }
479
480 fn is_station(&self) -> bool {
482 match self {
483 Self::Station | Self::AccessPointStation => true,
484 Self::AccessPoint => false,
485 }
486 }
487
488 fn is_access_point(&self) -> bool {
490 match self {
491 Self::Station => false,
492 Self::AccessPoint | Self::AccessPointStation => true,
493 }
494 }
495
496 fn from_raw(value: wifi_mode_t) -> Self {
498 #[allow(non_upper_case_globals)]
499 match value {
500 include::wifi_mode_t_WIFI_MODE_STA => Self::Station,
501 include::wifi_mode_t_WIFI_MODE_AP => Self::AccessPoint,
502 include::wifi_mode_t_WIFI_MODE_APSTA => Self::AccessPointStation,
503 _ => panic!("Invalid wifi mode value: {}", value),
504 }
505 }
506}
507
508impl From<&Config> for WifiMode {
509 fn from(config: &Config) -> Self {
510 match config {
511 Config::AccessPoint(_) => Self::AccessPoint,
512 Config::Station(_) => Self::Station,
513 Config::AccessPointStation(_, _) => Self::AccessPointStation,
514 #[cfg(feature = "wifi-eap")]
515 Config::EapStation(_) => Self::Station,
516 }
517 }
518}
519
520#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
522#[cfg_attr(feature = "defmt", derive(defmt::Format))]
523#[non_exhaustive]
524pub enum DisconnectReason {
525 Unspecified,
527 AuthenticationExpired,
529 AuthenticationLeave,
531 DisassociatedDueToInactivity,
533 AssociationTooMany,
535 Class2FrameFromNonAuthenticatedStation,
537 Class3FrameFromNonAssociatedStation,
539 AssociationLeave,
541 AssociationNotAuthenticated,
543 DisassociatedPowerCapabilityBad,
545 DisassociatedUnsupportedChannel,
547 BssTransitionDisassociated,
549 IeInvalid,
551 MicFailure,
553 FourWayHandshakeTimeout,
555 GroupKeyUpdateTimeout,
557 IeIn4wayDiffers,
559 GroupCipherInvalid,
561 PairwiseCipherInvalid,
563 AkmpInvalid,
565 UnsupportedRsnIeVersion,
567 InvalidRsnIeCapabilities,
569 _802_1xAuthenticationFailed,
571 CipherSuiteRejected,
573 TdlsPeerUnreachable,
575 TdlsUnspecified,
577 SspRequestedDisassociation,
579 NoSspRoamingAgreement,
581 BadCipherOrAkm,
583 NotAuthorizedThisLocation,
585 ServiceChangePercludesTs,
587 UnspecifiedQos,
589 NotEnoughBandwidth,
591 MissingAcks,
593 ExceededTxOp,
595 StationLeaving,
597 EndBlockAck,
599 UnknownBlockAck,
601 Timeout,
603 PeerInitiated,
605 AccessPointInitiatedDisassociation,
607 InvalidFtActionFrameCount,
609 InvalidPmkid,
611 InvalidMde,
613 InvalidFte,
615 TransmissionLinkEstablishmentFailed,
617 AlterativeChannelOccupied,
619 BeaconTimeout,
621 NoAccessPointFound,
623 AuthenticationFailed,
625 AssociationFailed,
627 HandshakeTimeout,
629 ConnectionFailed,
631 AccessPointTsfReset,
633 Roaming,
635 AssociationComebackTimeTooLong,
637 SaQueryTimeout,
639 NoAccessPointFoundWithCompatibleSecurity,
641 NoAccessPointFoundInAuthmodeThreshold,
643 NoAccessPointFoundInRssiThreshold,
645}
646
647impl DisconnectReason {
648 fn from_raw(id: u16) -> Self {
649 match id {
650 1 => Self::Unspecified,
651 2 => Self::AuthenticationExpired,
652 3 => Self::AuthenticationLeave,
653 4 => Self::DisassociatedDueToInactivity,
654 5 => Self::AssociationTooMany,
655 6 => Self::Class2FrameFromNonAuthenticatedStation,
656 7 => Self::Class3FrameFromNonAssociatedStation,
657 8 => Self::AssociationLeave,
658 9 => Self::AssociationNotAuthenticated,
659 10 => Self::DisassociatedPowerCapabilityBad,
660 11 => Self::DisassociatedUnsupportedChannel,
661 12 => Self::BssTransitionDisassociated,
662 13 => Self::IeInvalid,
663 14 => Self::MicFailure,
664 15 => Self::FourWayHandshakeTimeout,
665 16 => Self::GroupKeyUpdateTimeout,
666 17 => Self::IeIn4wayDiffers,
667 18 => Self::GroupCipherInvalid,
668 19 => Self::PairwiseCipherInvalid,
669 20 => Self::AkmpInvalid,
670 21 => Self::UnsupportedRsnIeVersion,
671 22 => Self::InvalidRsnIeCapabilities,
672 23 => Self::_802_1xAuthenticationFailed,
673 24 => Self::CipherSuiteRejected,
674 25 => Self::TdlsPeerUnreachable,
675 26 => Self::TdlsUnspecified,
676 27 => Self::SspRequestedDisassociation,
677 28 => Self::NoSspRoamingAgreement,
678 29 => Self::BadCipherOrAkm,
679 30 => Self::NotAuthorizedThisLocation,
680 31 => Self::ServiceChangePercludesTs,
681 32 => Self::UnspecifiedQos,
682 33 => Self::NotEnoughBandwidth,
683 34 => Self::MissingAcks,
684 35 => Self::ExceededTxOp,
685 36 => Self::StationLeaving,
686 37 => Self::EndBlockAck,
687 38 => Self::UnknownBlockAck,
688 39 => Self::Timeout,
689 46 => Self::PeerInitiated,
690 47 => Self::AccessPointInitiatedDisassociation,
691 48 => Self::InvalidFtActionFrameCount,
692 49 => Self::InvalidPmkid,
693 50 => Self::InvalidMde,
694 51 => Self::InvalidFte,
695 67 => Self::TransmissionLinkEstablishmentFailed,
696 68 => Self::AlterativeChannelOccupied,
697 200 => Self::BeaconTimeout,
698 201 => Self::NoAccessPointFound,
699 202 => Self::AuthenticationFailed,
700 203 => Self::AssociationFailed,
701 204 => Self::HandshakeTimeout,
702 205 => Self::ConnectionFailed,
703 206 => Self::AccessPointTsfReset,
704 207 => Self::Roaming,
705 208 => Self::AssociationComebackTimeTooLong,
706 209 => Self::SaQueryTimeout,
707 210 => Self::NoAccessPointFoundWithCompatibleSecurity,
708 211 => Self::NoAccessPointFoundInAuthmodeThreshold,
709 212 => Self::NoAccessPointFoundInRssiThreshold,
710 _ => Self::Unspecified,
711 }
712 }
713}
714
715#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
717#[cfg_attr(feature = "defmt", derive(defmt::Format))]
718pub struct Ssid {
719 ssid: [u8; 32],
720 len: u8,
721}
722
723impl Ssid {
724 pub(crate) fn new(ssid: &str) -> Self {
725 let mut ssid_bytes = [0u8; 32];
726 let bytes = ssid.as_bytes();
727 let len = usize::min(32, bytes.len());
728 ssid_bytes[..len].copy_from_slice(bytes);
729
730 Self::from_raw(&ssid_bytes, len as u8)
731 }
732
733 pub(crate) fn from_raw(ssid: &[u8], len: u8) -> Self {
734 let mut ssid_bytes = [0u8; 32];
735 let len = usize::min(32, len as usize);
736 ssid_bytes[..len].copy_from_slice(&ssid[..len]);
737
738 Self {
739 ssid: ssid_bytes,
740 len: len as u8,
741 }
742 }
743
744 pub(crate) fn as_bytes(&self) -> &[u8] {
745 &self.ssid[..self.len as usize]
746 }
747
748 pub fn len(&self) -> usize {
750 self.len as usize
751 }
752
753 pub fn is_empty(&self) -> bool {
755 self.len == 0
756 }
757
758 pub fn as_str(&self) -> &str {
760 let part = &self.ssid[..self.len as usize];
761 match str::from_utf8(part) {
762 Ok(s) => s,
763 Err(e) => {
764 let (valid, _) = part.split_at(e.valid_up_to());
765 unsafe { str::from_utf8_unchecked(valid) }
766 }
767 }
768 }
769}
770
771impl Debug for Ssid {
772 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
773 f.write_char('"')?;
774 f.write_str(self.as_str())?;
775 f.write_char('"')
776 }
777}
778
779impl From<alloc::string::String> for Ssid {
780 fn from(ssid: alloc::string::String) -> Self {
781 Self::new(&ssid)
782 }
783}
784
785impl From<&str> for Ssid {
786 fn from(ssid: &str) -> Self {
787 Self::new(ssid)
788 }
789}
790
791impl From<&[u8]> for Ssid {
792 fn from(ssid: &[u8]) -> Self {
793 Self::from_raw(ssid, ssid.len() as u8)
794 }
795}
796
797#[derive(Debug, Clone, PartialEq, Eq, Hash)]
799#[cfg_attr(feature = "defmt", derive(defmt::Format))]
800#[non_exhaustive]
801pub struct ConnectedStationInfo {
802 pub ssid: Ssid,
804 pub bssid: [u8; 6],
806 pub channel: u8,
808 pub authmode: AuthenticationMethod,
810 pub aid: u16,
812}
813
814#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
816#[cfg_attr(feature = "defmt", derive(defmt::Format))]
817#[non_exhaustive]
818pub struct DisconnectedStationInfo {
819 pub ssid: Ssid,
821 pub bssid: [u8; 6],
823 pub reason: DisconnectReason,
826 pub rssi: i8,
828}
829
830#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
832#[cfg_attr(feature = "defmt", derive(defmt::Format))]
833#[non_exhaustive]
834pub struct AccessPointStationConnectedInfo {
835 pub mac: [u8; 6],
837 pub aid: u16,
839 pub is_mesh_child: bool,
841}
842
843#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
845#[cfg_attr(feature = "defmt", derive(defmt::Format))]
846#[non_exhaustive]
847pub struct AccessPointStationDisconnectedInfo {
848 pub mac: [u8; 6],
850 pub aid: u16,
852 pub is_mesh_child: bool,
854 pub reason: DisconnectReason,
856}
857
858#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
860#[cfg_attr(feature = "defmt", derive(defmt::Format))]
861pub enum AccessPointStationEventInfo {
862 Connected(AccessPointStationConnectedInfo),
864 Disconnected(AccessPointStationDisconnectedInfo),
866}
867
868static RX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
869static TX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
870
871pub(crate) static DATA_QUEUE_RX_AP: NonReentrantMutex<VecDeque<PacketBuffer>> =
872 NonReentrantMutex::new(VecDeque::new());
873
874pub(crate) static DATA_QUEUE_RX_STA: NonReentrantMutex<VecDeque<PacketBuffer>> =
875 NonReentrantMutex::new(VecDeque::new());
876
877#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)]
879#[cfg_attr(feature = "defmt", derive(defmt::Format))]
880#[non_exhaustive]
881pub enum WifiError {
882 Disconnected(DisconnectedStationInfo),
884
885 Unsupported,
887
888 InvalidArguments,
890
891 Failed,
893
894 OutOfMemory,
896
897 InvalidSsid,
899
900 InvalidPassword,
902
903 NotConnected,
905}
906
907impl WifiError {
908 fn from_error_code(code: i32) -> Self {
909 if crate::sys::include::ESP_FAIL == code {
910 return WifiError::Failed;
911 }
912
913 match code as u32 {
914 crate::sys::include::ESP_ERR_NO_MEM => WifiError::OutOfMemory,
915 crate::sys::include::ESP_ERR_INVALID_ARG => WifiError::InvalidArguments,
916 crate::sys::include::ESP_ERR_WIFI_SSID => WifiError::InvalidSsid,
917 crate::sys::include::ESP_ERR_WIFI_PASSWORD => WifiError::InvalidPassword,
918 crate::sys::include::ESP_ERR_WIFI_NOT_CONNECT => WifiError::NotConnected,
919 _ => panic!("Unknown error code: {}", code),
920 }
921 }
922}
923
924impl core::error::Error for WifiError {}
925
926#[cfg(esp32)]
927fn set_mac_time_update_cb(_wifi: crate::hal::peripherals::WIFI<'_>) {
928 use crate::sys::include::esp_wifi_internal_update_mac_time;
929 unsafe {
930 esp_phy::set_mac_time_update_cb(|duration| {
931 esp_wifi_internal_update_mac_time(duration.as_micros() as u32);
932 });
933 }
934}
935
936pub(crate) fn wifi_init(_wifi: crate::hal::peripherals::WIFI<'_>) -> Result<(), WifiError> {
937 #[cfg(esp32)]
938 set_mac_time_update_cb(_wifi);
939 unsafe {
940 #[cfg(feature = "coex")]
941 esp_wifi_result!(coex_init())?;
942
943 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
944 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
945
946 esp_wifi_result!(esp_supplicant_init())?;
947
948 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
949
950 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
951 esp_interface_t_ESP_IF_WIFI_STA,
952 Some(recv_cb_sta)
953 ))?;
954
955 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
957 esp_interface_t_ESP_IF_WIFI_AP,
958 Some(recv_cb_ap)
959 ))?;
960
961 Ok(())
962 }
963}
964
965#[cfg(feature = "coex")]
966pub(crate) fn coex_initialize() -> i32 {
967 debug!("call coex-initialize");
968 unsafe {
969 let res = crate::sys::include::esp_coex_adapter_register(
970 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
971 );
972 if res != 0 {
973 error!("Error: esp_coex_adapter_register {}", res);
974 return res;
975 }
976 let res = crate::sys::include::coex_pre_init();
977 if res != 0 {
978 error!("Error: coex_pre_init {}", res);
979 return res;
980 }
981 0
982 }
983}
984
985pub(crate) unsafe extern "C" fn coex_init() -> i32 {
986 debug!("coex-init");
987
988 cfg_if::cfg_if! {
989 if #[cfg(feature = "coex")] {
990 unsafe { crate::sys::include::coex_init() }
991 } else {
992 0
993 }
994 }
995}
996
997fn wifi_deinit() -> Result<(), crate::WifiError> {
998 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
999 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1000 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1001 Ok(())
1002}
1003
1004unsafe extern "C" fn recv_cb_sta(
1005 buffer: *mut c_types::c_void,
1006 len: u16,
1007 eb: *mut c_types::c_void,
1008) -> esp_err_t {
1009 let packet = PacketBuffer { buffer, len, eb };
1010 match DATA_QUEUE_RX_STA.with(|queue| {
1017 if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) {
1018 queue.push_back(packet);
1019 Ok(())
1020 } else {
1021 Err(packet)
1022 }
1023 }) {
1024 Ok(()) => {
1025 embassy::STA_RECEIVE_WAKER.wake();
1026 include::ESP_OK as esp_err_t
1027 }
1028 _ => {
1029 debug!("RX QUEUE FULL");
1030 include::ESP_ERR_NO_MEM as esp_err_t
1031 }
1032 }
1033}
1034
1035unsafe extern "C" fn recv_cb_ap(
1036 buffer: *mut c_types::c_void,
1037 len: u16,
1038 eb: *mut c_types::c_void,
1039) -> esp_err_t {
1040 let packet = PacketBuffer { buffer, len, eb };
1041 match DATA_QUEUE_RX_AP.with(|queue| {
1048 if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) {
1049 queue.push_back(packet);
1050 Ok(())
1051 } else {
1052 Err(packet)
1053 }
1054 }) {
1055 Ok(()) => {
1056 embassy::AP_RECEIVE_WAKER.wake();
1057 include::ESP_OK as esp_err_t
1058 }
1059 _ => {
1060 debug!("RX QUEUE FULL");
1061 include::ESP_ERR_NO_MEM as esp_err_t
1062 }
1063 }
1064}
1065
1066pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1067
1068fn decrement_inflight_counter() {
1069 unwrap!(
1070 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1071 Some(x.saturating_sub(1))
1072 })
1073 );
1074}
1075
1076#[ram]
1077unsafe extern "C" fn esp_wifi_tx_done_cb(
1078 _ifidx: u8,
1079 _data: *mut u8,
1080 _data_len: *mut u16,
1081 _tx_status: bool,
1082) {
1083 trace!("esp_wifi_tx_done_cb");
1084
1085 decrement_inflight_counter();
1086
1087 embassy::TRANSMIT_WAKER.wake();
1088}
1089
1090pub(crate) fn wifi_start_scan(
1091 block: bool,
1092 ScanConfig {
1093 ssid,
1094 mut bssid,
1095 channel,
1096 show_hidden,
1097 scan_type,
1098 ..
1099 }: ScanConfig,
1100) -> i32 {
1101 scan_type.validate();
1102 let (scan_time, scan_type) = match scan_type {
1103 ScanTypeConfig::Active { min, max } => (
1104 wifi_scan_time_t {
1105 active: wifi_active_scan_time_t {
1106 min: min.as_millis() as u32,
1107 max: max.as_millis() as u32,
1108 },
1109 passive: 0,
1110 },
1111 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1112 ),
1113 ScanTypeConfig::Passive(dur) => (
1114 wifi_scan_time_t {
1115 active: wifi_active_scan_time_t { min: 0, max: 0 },
1116 passive: dur.as_millis() as u32,
1117 },
1118 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1119 ),
1120 };
1121
1122 let mut ssid_buf = ssid.map(|m| {
1123 let mut buf = Vec::from_iter(m.as_bytes().to_owned());
1124 buf.push(b'\0');
1125 buf
1126 });
1127
1128 let ssid = ssid_buf
1129 .as_mut()
1130 .map(|e| e.as_mut_ptr())
1131 .unwrap_or_else(core::ptr::null_mut);
1132 let bssid = bssid
1133 .as_mut()
1134 .map(|e| e.as_mut_ptr())
1135 .unwrap_or_else(core::ptr::null_mut);
1136
1137 let scan_config = wifi_scan_config_t {
1138 ssid,
1139 bssid,
1140 channel: channel.unwrap_or(0),
1141 show_hidden,
1142 scan_type,
1143 scan_time,
1144 home_chan_dwell_time: 0,
1145 channel_bitmap: wifi_scan_channel_bitmap_t {
1146 ghz_2_channels: 0,
1147 ghz_5_channels: 0,
1148 },
1149 coex_background_scan: false,
1150 };
1151
1152 unsafe { esp_wifi_scan_start(&scan_config, block) }
1153}
1154
1155mod private {
1156 use super::*;
1157
1158 #[derive(Debug)]
1166 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1167 pub struct PacketBuffer {
1168 pub(crate) buffer: *mut c_types::c_void,
1169 pub(crate) len: u16,
1170 pub(crate) eb: *mut c_types::c_void,
1171 }
1172
1173 unsafe impl Send for PacketBuffer {}
1174
1175 impl Drop for PacketBuffer {
1176 fn drop(&mut self) {
1177 trace!("Dropping PacketBuffer, freeing memory");
1178 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1179 }
1180 }
1181
1182 impl PacketBuffer {
1183 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1184 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1185 }
1186 }
1187}
1188
1189#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1191#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1192enum InterfaceType {
1193 Station,
1195 AccessPoint,
1197}
1198
1199impl InterfaceType {
1200 fn mac_address(&self) -> [u8; 6] {
1201 use esp_hal::efuse::InterfaceMacAddress;
1202 let mac = match self {
1203 InterfaceType::Station => {
1204 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::Station)
1205 }
1206 InterfaceType::AccessPoint => {
1207 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::AccessPoint)
1208 }
1209 };
1210
1211 let mut out = [0u8; 6];
1212 out.copy_from_slice(mac.as_bytes());
1213 out
1214 }
1215
1216 fn data_queue_rx(&self) -> &'static NonReentrantMutex<VecDeque<PacketBuffer>> {
1217 match self {
1218 InterfaceType::Station => &DATA_QUEUE_RX_STA,
1219 InterfaceType::AccessPoint => &DATA_QUEUE_RX_AP,
1220 }
1221 }
1222
1223 fn can_send(&self) -> bool {
1224 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE.load(Ordering::Relaxed)
1225 }
1226
1227 fn increase_in_flight_counter(&self) {
1228 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1229 }
1230
1231 fn tx_token(&self) -> Option<WifiTxToken> {
1232 if !self.can_send() {
1233 crate::preempt::yield_task();
1235 }
1236
1237 if self.can_send() {
1238 if self.link_state() == embassy_net_driver::LinkState::Up {
1240 return Some(WifiTxToken { mode: *self });
1241 }
1242 }
1243
1244 None
1245 }
1246
1247 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1248 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1249 if is_empty || !self.can_send() {
1250 crate::preempt::yield_task();
1252 }
1253
1254 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1255
1256 if !is_empty {
1257 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1258 } else {
1259 None
1260 }
1261 }
1262
1263 fn interface(&self) -> wifi_interface_t {
1264 match self {
1265 InterfaceType::Station => wifi_interface_t_WIFI_IF_STA,
1266 InterfaceType::AccessPoint => wifi_interface_t_WIFI_IF_AP,
1267 }
1268 }
1269
1270 fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) {
1271 embassy::TRANSMIT_WAKER.register(cx.waker())
1272 }
1273
1274 fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) {
1275 match self {
1276 InterfaceType::Station => embassy::STA_RECEIVE_WAKER.register(cx.waker()),
1277 InterfaceType::AccessPoint => embassy::AP_RECEIVE_WAKER.register(cx.waker()),
1278 }
1279 }
1280
1281 fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) {
1282 match self {
1283 InterfaceType::Station => embassy::STA_LINK_STATE_WAKER.register(cx.waker()),
1284 InterfaceType::AccessPoint => embassy::AP_LINK_STATE_WAKER.register(cx.waker()),
1285 }
1286 }
1287
1288 fn link_state(&self) -> embassy_net_driver::LinkState {
1289 let is_up = match self {
1290 InterfaceType::Station => {
1291 matches!(station_state(), WifiStationState::Connected)
1292 }
1293 InterfaceType::AccessPoint => {
1294 matches!(access_point_state(), WifiAccessPointState::Started)
1295 }
1296 };
1297
1298 if is_up {
1299 embassy_net_driver::LinkState::Up
1300 } else {
1301 embassy_net_driver::LinkState::Down
1302 }
1303 }
1304}
1305
1306#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1308#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1309#[non_exhaustive]
1310pub struct Interface<'d> {
1311 _phantom: PhantomData<&'d ()>,
1312 mode: InterfaceType,
1313}
1314
1315impl Interface<'_> {
1316 #[procmacros::doc_replace]
1317 pub fn mac_address(&self) -> [u8; 6] {
1332 self.mode.mac_address()
1333 }
1334
1335 #[doc(hidden)]
1336 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1338 self.mode.rx_token()
1339 }
1340
1341 #[doc(hidden)]
1342 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1344 self.mode.tx_token()
1345 }
1346}
1347
1348#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
1350#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1351#[non_exhaustive]
1352pub struct Bandwidths {
1353 _2_4: Bandwidth,
1355 #[cfg(wifi_has_5g)]
1357 _5: Bandwidth,
1358}
1359
1360impl Bandwidths {
1361 fn to_raw(self) -> wifi_bandwidths_t {
1362 wifi_bandwidths_t {
1363 ghz_2g: self._2_4.to_raw(),
1364 #[cfg(wifi_has_5g)]
1365 ghz_5g: self._5.to_raw(),
1366 #[cfg(not(wifi_has_5g))]
1367 ghz_5g: 0,
1368 }
1369 }
1370}
1371
1372#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1374#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1375#[allow(
1376 clippy::enum_variant_names,
1377 reason = "MHz suffix indicates physical unit."
1378)]
1379#[non_exhaustive]
1380pub enum Bandwidth {
1381 _20MHz,
1383 _40MHz,
1385 _80MHz,
1387 _160MHz,
1389 _80_80MHz,
1391}
1392
1393impl Bandwidth {
1394 fn to_raw(self) -> wifi_bandwidth_t {
1395 match self {
1396 Bandwidth::_20MHz => wifi_bandwidth_t_WIFI_BW_HT20,
1397 Bandwidth::_40MHz => wifi_bandwidth_t_WIFI_BW_HT40,
1398 Bandwidth::_80MHz => wifi_bandwidth_t_WIFI_BW80,
1399 Bandwidth::_160MHz => wifi_bandwidth_t_WIFI_BW160,
1400 Bandwidth::_80_80MHz => wifi_bandwidth_t_WIFI_BW80_BW80,
1401 }
1402 }
1403
1404 fn from_raw(raw: wifi_bandwidth_t) -> Self {
1405 match raw {
1406 raw if raw == wifi_bandwidth_t_WIFI_BW_HT20 => Bandwidth::_20MHz,
1407 raw if raw == wifi_bandwidth_t_WIFI_BW_HT40 => Bandwidth::_40MHz,
1408 raw if raw == wifi_bandwidth_t_WIFI_BW80 => Bandwidth::_80MHz,
1409 raw if raw == wifi_bandwidth_t_WIFI_BW160 => Bandwidth::_160MHz,
1410 raw if raw == wifi_bandwidth_t_WIFI_BW80_BW80 => Bandwidth::_80_80MHz,
1411 _ => Bandwidth::_20MHz,
1412 }
1413 }
1414}
1415
1416#[cfg(wifi_mac_version = "1")]
1419#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1420#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1421#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1422#[instability::unstable]
1423pub struct RxControlInfo {
1424 pub rssi: i32,
1426 pub rate: u32,
1429 pub sig_mode: u32,
1432 pub mcs: u32,
1435 pub cwb: u32,
1437 pub smoothing: u32,
1440 pub not_sounding: u32,
1443 pub aggregation: u32,
1445 pub stbc: u32,
1448 pub fec_coding: u32,
1451 pub sgi: u32,
1454 pub ampdu_cnt: u32,
1456 pub channel: u32,
1458 pub secondary_channel: SecondaryChannel,
1460 pub timestamp: Instant,
1463 pub noise_floor: i32,
1465 pub ant: u32,
1468 pub sig_len: u32,
1470 pub rx_state: u32,
1472}
1473
1474#[cfg(wifi_mac_version = "2")]
1477#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1478#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1479#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1480#[instability::unstable]
1481pub struct RxControlInfo {
1482 pub rssi: i32,
1484 pub rate: u32,
1487 pub sig_len: u32,
1489 pub rx_state: u32,
1492 pub dump_len: u32,
1494 pub he_sigb_len: u32,
1496 pub cur_single_mpdu: u32,
1498 pub cur_bb_format: u32,
1500 pub rx_channel_estimate_info_vld: u32,
1502 pub rx_channel_estimate_len: u32,
1504 pub secondary_channel: SecondaryChannel,
1506 pub channel: u32,
1508 pub noise_floor: i32,
1510 pub is_group: u32,
1512 pub rxend_state: u32,
1514 pub rxmatch3: u32,
1516 pub rxmatch2: u32,
1518 pub rxmatch1: u32,
1520 pub rxmatch0: u32,
1522 pub timestamp: Instant,
1525}
1526
1527#[cfg(wifi_mac_version = "3")]
1530#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1531#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1532#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1533#[instability::unstable]
1534pub struct RxControlInfo {
1535 pub rssi: i32,
1537 pub rate: u32,
1540 pub sig_len: u32,
1542 pub rx_state: u32,
1545 pub dump_len: u32,
1547 pub he_sigb_len: u32,
1549 pub cur_bb_format: u32,
1551 pub rx_channel_estimate_info_vld: u32,
1553 pub rx_channel_estimate_len: u32,
1555 pub secondary_channel: SecondaryChannel,
1557 pub channel: u32,
1559 pub noise_floor: i32,
1561 pub is_group: u32,
1563 pub rxend_state: u32,
1565 pub rxmatch3: u32,
1567 pub rxmatch2: u32,
1569 pub rxmatch1: u32,
1571 pub rxmatch0: u32,
1573 pub timestamp: Instant,
1576}
1577
1578#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1579impl RxControlInfo {
1580 pub(super) unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1586 #[cfg(wifi_mac_version = "1")]
1587 let rx_control_info = unsafe {
1588 RxControlInfo {
1589 rssi: (*rx_cntl).rssi(),
1590 rate: (*rx_cntl).rate(),
1591 sig_mode: (*rx_cntl).sig_mode(),
1592 mcs: (*rx_cntl).mcs(),
1593 cwb: (*rx_cntl).cwb(),
1594 smoothing: (*rx_cntl).smoothing(),
1595 not_sounding: (*rx_cntl).not_sounding(),
1596 aggregation: (*rx_cntl).aggregation(),
1597 stbc: (*rx_cntl).stbc(),
1598 fec_coding: (*rx_cntl).fec_coding(),
1599 sgi: (*rx_cntl).sgi(),
1600 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1601 channel: (*rx_cntl).channel(),
1602 secondary_channel: SecondaryChannel::from_raw_or_default(
1603 (*rx_cntl).secondary_channel(),
1604 ),
1605 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1606 noise_floor: (*rx_cntl).noise_floor(),
1607 ant: (*rx_cntl).ant(),
1608 sig_len: (*rx_cntl).sig_len(),
1609 rx_state: (*rx_cntl).rx_state(),
1610 }
1611 };
1612 #[cfg(wifi_mac_version = "2")]
1613 let rx_control_info = unsafe {
1614 RxControlInfo {
1615 rssi: (*rx_cntl).rssi(),
1616 rate: (*rx_cntl).rate(),
1617 sig_len: (*rx_cntl).sig_len(),
1618 rx_state: (*rx_cntl).rx_state(),
1619 dump_len: (*rx_cntl).dump_len(),
1620 he_sigb_len: (*rx_cntl).he_sigb_len(),
1621 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
1622 cur_bb_format: (*rx_cntl).cur_bb_format(),
1623 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1624 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1625 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1626 channel: (*rx_cntl).channel(),
1627 noise_floor: (*rx_cntl).noise_floor() as _,
1628 is_group: (*rx_cntl).is_group(),
1629 rxend_state: (*rx_cntl).rxend_state(),
1630 rxmatch3: (*rx_cntl).rxmatch3(),
1631 rxmatch2: (*rx_cntl).rxmatch2(),
1632 rxmatch1: (*rx_cntl).rxmatch1(),
1633 rxmatch0: (*rx_cntl).rxmatch0(),
1634 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1635 }
1636 };
1637 #[cfg(wifi_mac_version = "3")]
1638 let rx_control_info = unsafe {
1639 RxControlInfo {
1640 rssi: (*rx_cntl).rssi(),
1641 rate: (*rx_cntl).rate(),
1642 sig_len: (*rx_cntl).sig_len(),
1643 rx_state: (*rx_cntl).rx_state(),
1644 dump_len: (*rx_cntl).dump_len(),
1645 he_sigb_len: (*rx_cntl).sigb_len(),
1646 cur_bb_format: (*rx_cntl).cur_bb_format(),
1647 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1648 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1649 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1650 channel: (*rx_cntl).channel(),
1651 noise_floor: (*rx_cntl).noise_floor() as _,
1652 is_group: (*rx_cntl).is_group(),
1653 rxend_state: (*rx_cntl).rxend_state(),
1654 rxmatch3: (*rx_cntl).rxmatch3(),
1655 rxmatch2: (*rx_cntl).rxmatch2(),
1656 rxmatch1: (*rx_cntl).rxmatch1(),
1657 rxmatch0: (*rx_cntl).rxmatch0(),
1658 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1659 }
1660 };
1661 rx_control_info
1662 }
1663}
1664
1665#[doc(hidden)]
1666pub struct WifiRxToken {
1670 mode: InterfaceType,
1671}
1672
1673impl WifiRxToken {
1674 pub fn consume_token<R, F>(self, f: F) -> R
1677 where
1678 F: FnOnce(&mut [u8]) -> R,
1679 {
1680 let mut data = self.mode.data_queue_rx().with(|queue| {
1681 unwrap!(
1682 queue.pop_front(),
1683 "unreachable: transmit()/receive() ensures there is a packet to process"
1684 )
1685 });
1686
1687 let buffer = data.as_slice_mut();
1694 dump_packet_info(buffer);
1695
1696 f(buffer)
1697 }
1698}
1699
1700#[doc(hidden)]
1701pub struct WifiTxToken {
1705 mode: InterfaceType,
1706}
1707
1708impl WifiTxToken {
1709 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
1712 where
1713 F: FnOnce(&mut [u8]) -> R,
1714 {
1715 self.mode.increase_in_flight_counter();
1716
1717 let mut buffer: [u8; MTU] = [0u8; MTU];
1718 let buffer = &mut buffer[..len];
1719
1720 let res = f(buffer);
1721
1722 esp_wifi_send_data(self.mode.interface(), buffer);
1723
1724 res
1725 }
1726}
1727
1728pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
1733 state::locked(|| {
1736 if (interface == wifi_interface_t_WIFI_IF_STA
1738 && !matches!(station_state(), WifiStationState::Connected))
1739 || (interface == wifi_interface_t_WIFI_IF_AP
1740 && !matches!(access_point_state(), WifiAccessPointState::Started))
1741 {
1742 return;
1743 }
1744
1745 trace!("sending... {} bytes", data.len());
1746 dump_packet_info(data);
1747
1748 let len = data.len() as u16;
1749 let ptr = data.as_mut_ptr().cast();
1750
1751 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
1752
1753 if res != include::ESP_OK as i32 {
1754 warn!("esp_wifi_internal_tx returned error: {}", res);
1755 decrement_inflight_counter();
1756 }
1757 })
1758}
1759
1760fn dump_packet_info(_buffer: &mut [u8]) {
1761 #[cfg(dump_packets)]
1762 {
1763 info!("@WIFIFRAME {:?}", _buffer);
1764 }
1765}
1766
1767macro_rules! esp_wifi_result {
1768 ($value:expr) => {{
1769 use num_traits::FromPrimitive;
1770 let result = $value;
1771 if result != $crate::sys::include::ESP_OK as i32 {
1772 let error = unwrap!(FromPrimitive::from_i32(result));
1773 warn!(
1774 "{} returned an error: {:?} ({}). If this error is unmapped, please open an issue at <https://github.com/esp-rs/esp-hal/issues>.",
1775 stringify!($value),
1776 error,
1777 result
1778 );
1779 Err(WifiError::from_error_code(error))
1780 } else {
1781 Ok::<(), WifiError>(())
1782 }
1783 }};
1784}
1785pub(crate) use esp_wifi_result;
1786
1787pub(crate) mod embassy {
1788 use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken};
1789
1790 use super::*;
1791 use crate::asynch::AtomicWaker;
1792
1793 pub(crate) static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
1796
1797 pub(crate) static AP_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
1798 pub(crate) static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
1799
1800 pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new();
1801 pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
1802
1803 impl RxToken for WifiRxToken {
1804 fn consume<R, F>(self, f: F) -> R
1805 where
1806 F: FnOnce(&mut [u8]) -> R,
1807 {
1808 self.consume_token(f)
1809 }
1810 }
1811
1812 impl TxToken for WifiTxToken {
1813 fn consume<R, F>(self, len: usize, f: F) -> R
1814 where
1815 F: FnOnce(&mut [u8]) -> R,
1816 {
1817 self.consume_token(len, f)
1818 }
1819 }
1820
1821 impl Driver for Interface<'_> {
1822 type RxToken<'a>
1823 = WifiRxToken
1824 where
1825 Self: 'a;
1826 type TxToken<'a>
1827 = WifiTxToken
1828 where
1829 Self: 'a;
1830
1831 fn receive(
1832 &mut self,
1833 cx: &mut core::task::Context<'_>,
1834 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
1835 self.mode.register_receive_waker(cx);
1836 self.mode.register_transmit_waker(cx);
1837 self.mode.rx_token()
1838 }
1839
1840 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
1841 self.mode.register_transmit_waker(cx);
1842 self.mode.tx_token()
1843 }
1844
1845 fn link_state(
1846 &mut self,
1847 cx: &mut core::task::Context<'_>,
1848 ) -> embassy_net_driver::LinkState {
1849 self.mode.register_link_state_waker(cx);
1850 self.mode.link_state()
1851 }
1852
1853 fn capabilities(&self) -> Capabilities {
1854 let mut caps = Capabilities::default();
1855 caps.max_transmission_unit = MTU;
1856 caps.max_burst_size =
1857 if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 {
1858 None
1859 } else {
1860 Some(esp_config_int!(
1861 usize,
1862 "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE"
1863 ))
1864 };
1865 caps
1866 }
1867
1868 fn hardware_address(&self) -> HardwareAddress {
1869 HardwareAddress::Ethernet(self.mac_address())
1870 }
1871 }
1872}
1873
1874#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)]
1876#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1877#[instability::unstable]
1878#[non_exhaustive]
1879pub enum PowerSaveMode {
1880 #[default]
1882 None,
1883 Minimum,
1886 Maximum,
1889}
1890
1891pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
1892 esp_wifi_result!(unsafe {
1893 crate::sys::include::esp_wifi_set_ps(match ps {
1894 PowerSaveMode::None => crate::sys::include::wifi_ps_type_t_WIFI_PS_NONE,
1895 PowerSaveMode::Minimum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM,
1896 PowerSaveMode::Maximum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM,
1897 })
1898 })?;
1899 Ok(())
1900}
1901
1902#[derive(Debug)]
1904#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1905#[non_exhaustive]
1906pub struct Interfaces<'d> {
1907 pub station: Interface<'d>,
1909 pub access_point: Interface<'d>,
1911 #[cfg(all(feature = "esp-now", feature = "unstable"))]
1913 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
1914 pub esp_now: crate::esp_now::EspNow<'d>,
1915 #[cfg(all(feature = "sniffer", feature = "unstable"))]
1917 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
1918 pub sniffer: Sniffer<'d>,
1919}
1920
1921#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1925#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1926#[instability::unstable]
1927pub enum OperatingClass {
1928 AllEnvironments,
1931
1932 Outdoors,
1935
1936 Indoors,
1939
1940 NonCountryEntity,
1943
1944 Repr(u8),
1947}
1948
1949impl Default for OperatingClass {
1950 fn default() -> Self {
1951 OperatingClass::Repr(0) }
1953}
1954
1955impl OperatingClass {
1956 fn into_code(self) -> u8 {
1957 match self {
1958 OperatingClass::AllEnvironments => b' ',
1959 OperatingClass::Outdoors => b'O',
1960 OperatingClass::Indoors => b'I',
1961 OperatingClass::NonCountryEntity => b'X',
1962 OperatingClass::Repr(code) => code,
1963 }
1964 }
1965
1966 fn from_code(code: u8) -> Option<Self> {
1967 match code {
1968 b' ' => Some(OperatingClass::AllEnvironments),
1969 b'O' => Some(OperatingClass::Outdoors),
1970 b'I' => Some(OperatingClass::Indoors),
1971 b'X' => Some(OperatingClass::NonCountryEntity),
1972 code => Some(OperatingClass::Repr(code)),
1973 }
1974 }
1975}
1976
1977#[procmacros::doc_replace]
1978#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BuilderLite)]
1997#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1998#[instability::unstable]
1999pub struct CountryInfo {
2000 #[builder_lite(skip)]
2002 country: [u8; 2],
2003
2004 #[builder_lite(unstable)]
2006 operating_class: OperatingClass,
2007}
2008
2009impl From<[u8; 2]> for CountryInfo {
2010 fn from(country: [u8; 2]) -> Self {
2011 Self {
2012 country,
2013 operating_class: OperatingClass::default(),
2014 }
2015 }
2016}
2017
2018impl CountryInfo {
2019 fn into_blob(self) -> wifi_country_t {
2020 wifi_country_t {
2021 cc: [
2022 self.country[0],
2023 self.country[1],
2024 self.operating_class.into_code(),
2025 ],
2026 schan: 1,
2028 nchan: 13,
2029 max_tx_power: 20,
2030 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2031
2032 #[cfg(wifi_has_5g)]
2033 wifi_5g_channel_mask: 0,
2034 }
2035 }
2036
2037 #[cfg_attr(not(feature = "unstable"), expect(dead_code))]
2038 fn try_from_c(info: &wifi_country_t) -> Option<Self> {
2039 let cc = &info.cc;
2040 let operating_class = OperatingClass::from_code(cc[2])?;
2041
2042 Some(Self {
2043 country: [cc[0], cc[1]],
2044 operating_class,
2045 })
2046 }
2047}
2048
2049#[derive(Clone, BuilderLite, Debug, Hash, PartialEq, Eq)]
2051#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2052#[non_exhaustive]
2053pub struct ControllerConfig {
2054 #[builder_lite(into)]
2056 #[builder_lite(unstable)]
2057 country_info: CountryInfo,
2058 #[builder_lite(unstable)]
2060 rx_queue_size: usize,
2061 #[builder_lite(unstable)]
2063 tx_queue_size: usize,
2064
2065 #[builder_lite(unstable)]
2075 static_rx_buf_num: u8,
2076
2077 #[builder_lite(unstable)]
2094 dynamic_rx_buf_num: u16,
2095
2096 #[builder_lite(unstable)]
2108 static_tx_buf_num: u8,
2109
2110 #[builder_lite(unstable)]
2121 dynamic_tx_buf_num: u16,
2122
2123 #[builder_lite(unstable)]
2125 ampdu_rx_enable: bool,
2126
2127 #[builder_lite(unstable)]
2129 ampdu_tx_enable: bool,
2130
2131 #[builder_lite(unstable)]
2133 amsdu_tx_enable: bool,
2134
2135 #[builder_lite(unstable)]
2146 rx_ba_win: u8,
2147
2148 #[builder_lite(reference)]
2150 initial_config: Config,
2151}
2152
2153impl Default for ControllerConfig {
2154 fn default() -> Self {
2155 Self {
2156 rx_queue_size: 5,
2157 tx_queue_size: 3,
2158
2159 static_rx_buf_num: 10,
2160 dynamic_rx_buf_num: 32,
2161
2162 static_tx_buf_num: 0,
2163 dynamic_tx_buf_num: 32,
2164
2165 ampdu_rx_enable: true,
2166 ampdu_tx_enable: true,
2167 amsdu_tx_enable: false,
2168
2169 rx_ba_win: 6,
2170
2171 country_info: CountryInfo::from(*b"CN"),
2172
2173 initial_config: Config::Station(StationConfig::default()),
2174 }
2175 }
2176}
2177
2178impl ControllerConfig {
2179 fn validate(&self) {
2180 if self.rx_ba_win as u16 >= self.dynamic_rx_buf_num {
2181 warn!("RX BA window size should be less than the number of dynamic RX buffers.");
2182 }
2183 if self.rx_ba_win as u16 >= 2 * (self.static_rx_buf_num as u16) {
2184 warn!("RX BA window size should be less than twice the number of static RX buffers.");
2185 }
2186 }
2187}
2188
2189#[procmacros::doc_replace]
2190pub fn new<'d>(
2206 device: crate::hal::peripherals::WIFI<'d>,
2207 config: ControllerConfig,
2208) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
2209 let _guard = RadioRefGuard::new();
2210
2211 config.validate();
2212
2213 event::enable_wifi_events(
2214 WifiEvent::StationStart
2215 | WifiEvent::StationStop
2216 | WifiEvent::StationConnected
2217 | WifiEvent::StationDisconnected
2218 | WifiEvent::AccessPointStart
2219 | WifiEvent::AccessPointStop
2220 | WifiEvent::AccessPointStationConnected
2221 | WifiEvent::AccessPointStationDisconnected
2222 | WifiEvent::ScanDone,
2223 );
2224
2225 unsafe {
2226 internal::G_CONFIG = wifi_init_config_t {
2227 osi_funcs: (&raw const internal::__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(),
2228
2229 wpa_crypto_funcs: g_wifi_default_wpa_crypto_funcs,
2230 static_rx_buf_num: config.static_rx_buf_num as _,
2231 dynamic_rx_buf_num: config.dynamic_rx_buf_num as _,
2232 tx_buf_type: crate::sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32,
2233 static_tx_buf_num: config.static_tx_buf_num as _,
2234 dynamic_tx_buf_num: config.dynamic_tx_buf_num as _,
2235 rx_mgmt_buf_type: crate::sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32,
2236 rx_mgmt_buf_num: crate::sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32,
2237 cache_tx_buf_num: crate::sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32,
2238 csi_enable: cfg!(feature = "csi") as i32,
2239 ampdu_rx_enable: config.ampdu_rx_enable as _,
2240 ampdu_tx_enable: config.ampdu_tx_enable as _,
2241 amsdu_tx_enable: config.amsdu_tx_enable as _,
2242 nvs_enable: 0,
2243 nano_enable: 0,
2244 rx_ba_win: config.rx_ba_win as _,
2245 wifi_task_core_id: Cpu::current() as _,
2246 beacon_max_len: crate::sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32,
2247 mgmt_sbuf_num: crate::sys::include::WIFI_MGMT_SBUF_NUM as i32,
2248 feature_caps: internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS,
2249 sta_disconnected_pm: false,
2250 espnow_max_encrypt_num: crate::sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM
2251 as i32,
2252
2253 tx_hetb_queue_num: 3,
2254 dump_hesigb_enable: false,
2255
2256 magic: WIFI_INIT_CONFIG_MAGIC as i32,
2257 };
2258
2259 RX_QUEUE_SIZE.store(config.rx_queue_size, Ordering::Relaxed);
2260 TX_QUEUE_SIZE.store(config.tx_queue_size, Ordering::Relaxed);
2261 };
2262
2263 crate::wifi::wifi_init(device)?;
2264
2265 #[cfg(all(rng_trng_supported, feature = "unstable"))]
2267 unsafe {
2268 esp_hal::rng::TrngSource::increase_entropy_source_counter()
2269 };
2270
2271 let mut controller = WifiController {
2274 _guard,
2275 _phantom: Default::default(),
2276 };
2277
2278 controller.set_country_info(&config.country_info)?;
2279 controller.set_power_saving(PowerSaveMode::default())?;
2281
2282 controller.set_config(&config.initial_config)?;
2283
2284 Ok((
2285 controller,
2286 Interfaces {
2287 station: Interface {
2288 _phantom: Default::default(),
2289 mode: InterfaceType::Station,
2290 },
2291 access_point: Interface {
2292 _phantom: Default::default(),
2293 mode: InterfaceType::AccessPoint,
2294 },
2295 #[cfg(all(feature = "esp-now", feature = "unstable"))]
2296 esp_now: crate::esp_now::EspNow::new_internal(),
2297 #[cfg(all(feature = "sniffer", feature = "unstable"))]
2298 sniffer: Sniffer::new(),
2299 },
2300 ))
2301}
2302
2303#[derive(Debug)]
2310#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2311#[non_exhaustive]
2312pub struct WifiController<'d> {
2313 _guard: RadioRefGuard,
2314 _phantom: PhantomData<&'d ()>,
2315}
2316
2317impl Drop for WifiController<'_> {
2318 fn drop(&mut self) {
2319 state::locked(|| {
2320 if let Err(e) = crate::wifi::wifi_deinit() {
2321 warn!("Failed to cleanly deinit wifi: {:?}", e);
2322 }
2323
2324 set_access_point_state(WifiAccessPointState::Uninitialized);
2325 set_station_state(WifiStationState::Uninitialized);
2326
2327 #[cfg(all(rng_trng_supported, feature = "unstable"))]
2328 esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe {
2329 esp_hal::Internal::conjure()
2330 });
2331 })
2332 }
2333}
2334
2335impl WifiController<'_> {
2336 #[cfg(all(feature = "csi", feature = "unstable"))]
2338 #[instability::unstable]
2339 pub fn set_csi(
2340 &mut self,
2341 mut csi: csi::CsiConfig,
2342 cb: impl FnMut(crate::wifi::csi::WifiCsiInfo<'_>) + Send,
2343 ) -> Result<(), WifiError> {
2344 csi.apply_config()?;
2345 csi.set_receive_cb(cb)?;
2346 csi.set_csi(true)?;
2347
2348 Ok(())
2349 }
2350
2351 #[procmacros::doc_replace]
2352 #[instability::unstable]
2381 pub fn set_protocols(&mut self, protocols: Protocols) -> Result<(), WifiError> {
2382 let mode = self.mode()?;
2383 if mode.is_station() {
2384 esp_wifi_result!(unsafe {
2385 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_STA, &mut protocols.to_raw())
2386 })?;
2387 }
2388 if mode.is_access_point() {
2389 esp_wifi_result!(unsafe {
2390 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_AP, &mut protocols.to_raw())
2391 })?;
2392 }
2393
2394 Ok(())
2395 }
2396
2397 fn apply_protocols(iface: wifi_interface_t, protocols: &Protocols) -> Result<(), WifiError> {
2398 esp_wifi_result!(unsafe { esp_wifi_set_protocols(iface, &mut protocols.to_raw()) })?;
2399 Ok(())
2400 }
2401
2402 #[procmacros::doc_replace]
2403 #[instability::unstable]
2415 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2416 apply_power_saving(ps)
2417 }
2418
2419 fn set_country_info(&mut self, country: &CountryInfo) -> Result<(), WifiError> {
2420 unsafe {
2421 let country = country.into_blob();
2422 esp_wifi_result!(esp_wifi_set_country(&country))?;
2423 }
2424 Ok(())
2425 }
2426
2427 #[procmacros::doc_replace]
2428 pub fn rssi(&self) -> Result<i32, WifiError> {
2458 if self.mode()?.is_station() {
2459 let mut rssi: i32 = 0;
2460 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
2462 Ok(rssi)
2463 } else {
2464 Err(WifiError::Unsupported)
2465 }
2466 }
2467
2468 #[procmacros::doc_replace]
2469 pub fn ap_info(&self) -> Result<AccessPointInfo, WifiError> {
2499 if self.mode()?.is_station() {
2500 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2501 esp_wifi_result!(unsafe { esp_wifi_sta_get_ap_info(record.as_mut_ptr()) })?;
2502
2503 let record = unsafe { MaybeUninit::assume_init(record) };
2504 let ap_info = convert_ap_info(&record);
2505 Ok(ap_info)
2506 } else {
2507 Err(WifiError::Unsupported)
2508 }
2509 }
2510
2511 #[procmacros::doc_replace]
2512 pub fn set_config(&mut self, conf: &Config) -> Result<(), WifiError> {
2541 struct ResetModeOnDrop;
2545 impl ResetModeOnDrop {
2546 fn defuse(self) {
2548 core::mem::forget(self);
2549 }
2550 }
2551 impl Drop for ResetModeOnDrop {
2552 fn drop(&mut self) {
2553 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
2554 unwrap!(WifiController::stop_impl());
2555 }
2556 }
2557
2558 let reset_mode_on_error = ResetModeOnDrop;
2559
2560 conf.validate()?;
2561
2562 let mut previous_mode = 0u32;
2563 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut previous_mode) })?;
2564
2565 let mode = match conf {
2566 Config::Station(_) => wifi_mode_t_WIFI_MODE_STA,
2567 Config::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
2568 Config::AccessPointStation(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
2569 #[cfg(feature = "wifi-eap")]
2570 Config::EapStation(_) => wifi_mode_t_WIFI_MODE_STA,
2571 };
2572
2573 if previous_mode != mode {
2574 Self::stop_impl()?;
2575 }
2576
2577 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
2578
2579 match conf {
2580 Config::Station(config) => {
2581 self.apply_sta_config(config)?;
2582 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
2583 }
2584 Config::AccessPoint(config) => {
2585 self.apply_ap_config(config)?;
2586 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &config.protocols)?;
2587 }
2588 Config::AccessPointStation(sta_config, ap_config) => {
2589 self.apply_ap_config(ap_config)?;
2590 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &ap_config.protocols)?;
2591 self.apply_sta_config(sta_config)?;
2592 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &sta_config.protocols)?;
2593 }
2594 #[cfg(feature = "wifi-eap")]
2595 Config::EapStation(config) => {
2596 self.apply_sta_eap_config(config)?;
2597 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
2598 }
2599 }
2600
2601 if previous_mode != mode {
2602 set_access_point_state(WifiAccessPointState::Starting);
2603 set_station_state(WifiStationState::Starting);
2604
2605 esp_wifi_result!(unsafe { esp_wifi_start() })?;
2607 }
2608
2609 reset_mode_on_error.defuse();
2610
2611 Ok(())
2612 }
2613
2614 #[cfg_attr(
2619 wifi_has_5g,
2620 doc = r"
2621When the WiFi band mode is set to [`BandMode::_5G`], it operates exclusively on the 5GHz channels.
2622
2623When the WiFi band mode is set to [`BandMode::Auto`], it can operate on both the 2.4GHz and
26245GHz channels.
2625
2626When a WiFi band mode change triggers a band change, if no channel is set for the current
2627band, a default channel will be assigned: channel 1 for 2.4G band and channel 36 for 5G
2628band.
2629"
2630 )]
2631 #[instability::unstable]
2633 pub fn set_band_mode(&mut self, band_mode: BandMode) -> Result<(), WifiError> {
2634 esp_wifi_result!(unsafe { esp_wifi_set_band_mode(band_mode.to_raw()) })
2636 }
2637
2638 #[instability::unstable]
2644 pub fn set_bandwidths(&mut self, bandwidths: Bandwidths) -> Result<(), WifiError> {
2645 let mode = self.mode()?;
2646 if mode.is_station() {
2647 esp_wifi_result!(unsafe {
2648 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bandwidths.to_raw())
2649 })?;
2650 }
2651 if mode.is_access_point() {
2652 esp_wifi_result!(unsafe {
2653 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bandwidths.to_raw())
2654 })?;
2655 }
2656
2657 Ok(())
2658 }
2659
2660 #[instability::unstable]
2667 pub fn bandwidths(&self) -> Result<Bandwidths, WifiError> {
2668 let mut bw = wifi_bandwidths_t {
2669 ghz_2g: 0,
2670 ghz_5g: 0,
2671 };
2672
2673 let mode = self.mode()?;
2674 if mode.is_station() {
2675 esp_wifi_result!(unsafe {
2676 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bw)
2677 })?;
2678 }
2679 if mode.is_access_point() {
2680 esp_wifi_result!(unsafe {
2681 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bw)
2682 })?;
2683 }
2684
2685 Ok(Bandwidths {
2686 _2_4: Bandwidth::from_raw(bw.ghz_2g),
2687 #[cfg(wifi_has_5g)]
2688 _5: Bandwidth::from_raw(bw.ghz_5g),
2689 })
2690 }
2691
2692 #[instability::unstable]
2694 pub fn channel(&self) -> Result<(u8, SecondaryChannel), WifiError> {
2695 let mut primary = 0;
2696 let mut secondary = 0;
2697
2698 esp_wifi_result!(unsafe { esp_wifi_get_channel(&mut primary, &mut secondary) })?;
2699
2700 Ok((primary, SecondaryChannel::from_raw(secondary)))
2701 }
2702
2703 #[cfg_attr(
2705 wifi_has_5g,
2706 doc = r"
2707
2708When operating in 5 GHz band, the second channel is automatically determined by the primary
2709channel according to the 802.11 standard. Any manually configured second channel will be
2710ignored."
2711 )]
2712 #[instability::unstable]
2713 pub fn set_channel(
2714 &mut self,
2715 primary: u8,
2716 secondary: SecondaryChannel,
2717 ) -> Result<(), WifiError> {
2718 esp_wifi_result!(unsafe { esp_wifi_set_channel(primary, secondary as u32) })?;
2719
2720 Ok(())
2721 }
2722
2723 #[instability::unstable]
2727 pub fn set_max_tx_power(&mut self, power: i8) -> Result<(), WifiError> {
2728 esp_wifi_result!(unsafe { esp_wifi_set_max_tx_power(power) })
2729 }
2730
2731 fn stop_impl() -> Result<(), WifiError> {
2732 set_access_point_state(WifiAccessPointState::Stopping);
2733 set_station_state(WifiStationState::Stopping);
2734
2735 esp_wifi_result!(unsafe { esp_wifi_stop() })
2736 }
2737
2738 fn connect_impl(&mut self) -> Result<(), WifiError> {
2739 set_station_state(WifiStationState::Connecting);
2740
2741 esp_wifi_result!(unsafe { esp_wifi_connect_internal() })
2743 }
2744
2745 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
2746 set_station_state(WifiStationState::Disconnecting);
2747
2748 esp_wifi_result!(unsafe { esp_wifi_disconnect_internal() })
2750 }
2751
2752 #[procmacros::doc_replace]
2753 #[instability::unstable]
2768 pub fn is_connected(&self) -> bool {
2769 matches!(
2770 crate::wifi::station_state(),
2771 crate::wifi::WifiStationState::Connected
2772 )
2773 }
2774
2775 fn mode(&self) -> Result<WifiMode, WifiError> {
2776 WifiMode::current()
2777 }
2778
2779 #[procmacros::doc_replace]
2780 pub async fn scan_async(
2802 &mut self,
2803 config: &ScanConfig,
2804 ) -> Result<Vec<AccessPointInfo>, WifiError> {
2805 let mut subscriber = EVENT_CHANNEL
2806 .subscriber()
2807 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2808
2809 esp_wifi_result!(wifi_start_scan(false, *config))?;
2810
2811 let guard = FreeApListOnDrop;
2813
2814 loop {
2815 let event = subscriber.next_message_pure().await;
2816 if let EventInfo::ScanDone {
2817 status: _status,
2818 number: _number,
2819 scan_id: _scan_id,
2820 } = event
2821 {
2822 break;
2823 }
2824 }
2825
2826 guard.defuse();
2827
2828 Ok(ScanResults::new(self)?.collect::<Vec<_>>())
2829 }
2830
2831 #[procmacros::doc_replace]
2832 pub async fn connect_async(&mut self) -> Result<ConnectedStationInfo, WifiError> {
2861 let mut subscriber = EVENT_CHANNEL
2862 .subscriber()
2863 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2864
2865 self.connect_impl()?;
2866
2867 let result = loop {
2868 let event = subscriber.next_message().await;
2869 if let embassy_sync::pubsub::WaitResult::Message(event) = event {
2870 match event {
2871 EventInfo::StationConnected { .. } => {
2872 break event;
2873 }
2874 EventInfo::StationDisconnected { .. } => {
2875 break event;
2876 }
2877 _ => (),
2878 }
2879 }
2880 };
2881
2882 match result {
2883 event::EventInfo::StationConnected {
2884 ssid,
2885 bssid,
2886 channel,
2887 authmode,
2888 aid,
2889 } => Ok(ConnectedStationInfo {
2890 ssid,
2891 bssid,
2892 channel,
2893 authmode: AuthenticationMethod::from_raw(authmode),
2894 aid,
2895 }),
2896 event::EventInfo::StationDisconnected {
2897 ssid,
2898 bssid,
2899 reason,
2900 rssi,
2901 } => Err(WifiError::Disconnected(DisconnectedStationInfo {
2902 ssid,
2903 bssid,
2904 reason: DisconnectReason::from_raw(reason),
2905 rssi,
2906 })),
2907 _ => unreachable!(),
2908 }
2909 }
2910
2911 #[procmacros::doc_replace]
2912 pub async fn disconnect_async(&mut self) -> Result<DisconnectedStationInfo, WifiError> {
2934 if !self.is_connected() {
2937 return Err(WifiError::NotConnected);
2938 }
2939
2940 let mut subscriber = EVENT_CHANNEL
2941 .subscriber()
2942 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2943
2944 self.disconnect_impl()?;
2945
2946 loop {
2947 let event = subscriber.next_message_pure().await;
2948
2949 if let event::EventInfo::StationDisconnected {
2950 ssid,
2951 bssid,
2952 reason,
2953 rssi,
2954 } = event
2955 {
2956 break Ok(DisconnectedStationInfo {
2957 ssid,
2958 bssid,
2959 reason: DisconnectReason::from_raw(reason),
2960 rssi,
2961 });
2962 }
2963 }
2964 }
2965
2966 pub async fn wait_for_disconnect_async(&self) -> Result<DisconnectedStationInfo, WifiError> {
2968 if !self.is_connected() {
2971 return Err(WifiError::NotConnected);
2972 }
2973
2974 let mut subscriber = EVENT_CHANNEL
2975 .subscriber()
2976 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
2977
2978 loop {
2979 let event = subscriber.next_message_pure().await;
2980
2981 if let event::EventInfo::StationDisconnected {
2982 ssid,
2983 bssid,
2984 reason,
2985 rssi,
2986 } = event
2987 {
2988 break Ok(DisconnectedStationInfo {
2989 ssid,
2990 bssid,
2991 reason: DisconnectReason::from_raw(reason),
2992 rssi,
2993 });
2994 }
2995 }
2996 }
2997
2998 pub async fn wait_for_access_point_connected_event_async(
3000 &self,
3001 ) -> Result<AccessPointStationEventInfo, WifiError> {
3002 let mut subscriber = EVENT_CHANNEL
3003 .subscriber()
3004 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3005
3006 loop {
3007 let event = subscriber.next_message_pure().await;
3008
3009 match event {
3010 event::EventInfo::AccessPointStationConnected {
3011 mac,
3012 aid,
3013 is_mesh_child,
3014 } => {
3015 break Ok(AccessPointStationEventInfo::Connected(
3016 AccessPointStationConnectedInfo {
3017 mac,
3018 aid,
3019 is_mesh_child,
3020 },
3021 ));
3022 }
3023 event::EventInfo::AccessPointStationDisconnected {
3024 mac,
3025 aid,
3026 is_mesh_child,
3027 reason,
3028 } => {
3029 break Ok(AccessPointStationEventInfo::Disconnected(
3030 AccessPointStationDisconnectedInfo {
3031 mac,
3032 aid: aid as u16,
3033 is_mesh_child,
3034 reason: DisconnectReason::from_raw(reason),
3035 },
3036 ));
3037 }
3038 _ => (),
3039 }
3040 }
3041 }
3042
3043 #[instability::unstable]
3049 pub fn subscribe<'a>(&'a self) -> Result<event::EventSubscriber<'a>, WifiError> {
3050 if let Ok(subscriber) = EVENT_CHANNEL.subscriber() {
3051 return Ok(event::EventSubscriber::new(subscriber));
3052 }
3053
3054 Err(WifiError::Failed)
3055 }
3056
3057 fn apply_ap_config(&mut self, config: &AccessPointConfig) -> Result<(), WifiError> {
3058 let mut cfg = wifi_config_t {
3059 ap: wifi_ap_config_t {
3060 ssid: [0; 32],
3061 password: [0; 64],
3062 ssid_len: 0,
3063 channel: config.channel,
3064 authmode: config.auth_method.to_raw(),
3065 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
3066 max_connection: config.max_connections as u8,
3067 beacon_interval: 100,
3068 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
3069 ftm_responder: false,
3070 pmf_cfg: wifi_pmf_config_t {
3071 capable: true,
3072 required: false,
3073 },
3074 sae_pwe_h2e: 0,
3075 csa_count: 3,
3076 dtim_period: config.dtim_period,
3077 transition_disable: 0,
3078 sae_ext: 0,
3079 bss_max_idle_cfg: include::wifi_bss_max_idle_config_t {
3080 period: 0,
3081 protected_keep_alive: false,
3082 },
3083 gtk_rekey_interval: 0,
3084 },
3085 };
3086
3087 if config.auth_method == AuthenticationMethod::None && !config.password.is_empty() {
3088 return Err(WifiError::InvalidArguments);
3089 }
3090
3091 unsafe {
3092 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3093 cfg.ap.ssid_len = config.ssid.len() as u8;
3094 cfg.ap.password[0..(config.password.len())].copy_from_slice(config.password.as_bytes());
3095
3096 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
3097 }
3098 }
3099
3100 fn apply_sta_config(&mut self, config: &StationConfig) -> Result<(), WifiError> {
3101 let mut cfg = wifi_config_t {
3102 sta: wifi_sta_config_t {
3103 ssid: [0; 32],
3104 password: [0; 64],
3105 scan_method: config.scan_method as c_types::c_uint,
3106 bssid_set: config.bssid.is_some(),
3107 bssid: config.bssid.unwrap_or_default(),
3108 channel: config.channel.unwrap_or(0),
3109 listen_interval: config.listen_interval,
3110 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3111 threshold: wifi_scan_threshold_t {
3112 rssi: -99,
3113 authmode: config.auth_method.to_raw(),
3114 rssi_5g_adjustment: 0,
3115 },
3116 pmf_cfg: wifi_pmf_config_t {
3117 capable: true,
3118 required: false,
3119 },
3120 sae_pwe_h2e: 3,
3121 _bitfield_align_1: [0; 0],
3122 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3123 failure_retry_cnt: config.failure_retry_cnt,
3124 _bitfield_align_2: [0; 0],
3125 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3126 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3128 },
3129 };
3130
3131 if config.auth_method == AuthenticationMethod::None && !config.password.is_empty() {
3132 return Err(WifiError::InvalidArguments);
3133 }
3134
3135 unsafe {
3136 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3137 cfg.sta.password[0..(config.password.len())]
3138 .copy_from_slice(config.password.as_bytes());
3139
3140 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
3141 }
3142 }
3143
3144 #[cfg(feature = "wifi-eap")]
3145 fn apply_sta_eap_config(&mut self, config: &EapStationConfig) -> Result<(), WifiError> {
3146 let mut cfg = wifi_config_t {
3147 sta: wifi_sta_config_t {
3148 ssid: [0; 32],
3149 password: [0; 64],
3150 scan_method: config.scan_method as c_types::c_uint,
3151 bssid_set: config.bssid.is_some(),
3152 bssid: config.bssid.unwrap_or_default(),
3153 channel: config.channel.unwrap_or(0),
3154 listen_interval: config.listen_interval,
3155 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3156 threshold: wifi_scan_threshold_t {
3157 rssi: -99,
3158 authmode: config.auth_method.to_raw(),
3159 rssi_5g_adjustment: 0,
3160 },
3161 pmf_cfg: wifi_pmf_config_t {
3162 capable: true,
3163 required: false,
3164 },
3165 sae_pwe_h2e: 3,
3166 _bitfield_align_1: [0; 0],
3167 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3168 failure_retry_cnt: config.failure_retry_cnt,
3169 _bitfield_align_2: [0; 0],
3170 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3171 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3173 },
3174 };
3175
3176 unsafe {
3177 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3178 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
3179
3180 if let Some(identity) = &config.identity {
3181 esp_wifi_result!(esp_eap_client_set_identity(
3182 identity.as_str().as_ptr(),
3183 identity.len() as i32
3184 ))?;
3185 } else {
3186 esp_eap_client_clear_identity();
3187 }
3188
3189 if let Some(username) = &config.username {
3190 esp_wifi_result!(esp_eap_client_set_username(
3191 username.as_str().as_ptr(),
3192 username.len() as i32
3193 ))?;
3194 } else {
3195 esp_eap_client_clear_username();
3196 }
3197
3198 if let Some(password) = &config.password {
3199 esp_wifi_result!(esp_eap_client_set_password(
3200 password.as_str().as_ptr(),
3201 password.len() as i32
3202 ))?;
3203 } else {
3204 esp_eap_client_clear_password();
3205 }
3206
3207 if let Some(new_password) = &config.new_password {
3208 esp_wifi_result!(esp_eap_client_set_new_password(
3209 new_password.as_str().as_ptr(),
3210 new_password.len() as i32
3211 ))?;
3212 } else {
3213 esp_eap_client_clear_new_password();
3214 }
3215
3216 if let Some(pac_file) = &config.pac_file {
3217 esp_wifi_result!(esp_eap_client_set_pac_file(
3218 pac_file.as_ptr(),
3219 pac_file.len() as i32
3220 ))?;
3221 }
3222
3223 if let Some(phase2_method) = &config.ttls_phase2_method {
3224 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
3225 phase2_method.to_raw()
3226 ))?;
3227 }
3228
3229 if let Some(ca_cert) = config.ca_cert {
3230 esp_wifi_result!(esp_eap_client_set_ca_cert(
3231 ca_cert.as_ptr(),
3232 ca_cert.len() as i32
3233 ))?;
3234 } else {
3235 esp_eap_client_clear_ca_cert();
3236 }
3237
3238 if let Some((cert, key, password)) = config.certificate_and_key {
3239 let (pwd, pwd_len) = if let Some(pwd) = password {
3240 (pwd.as_ptr(), pwd.len() as i32)
3241 } else {
3242 (core::ptr::null(), 0)
3243 };
3244
3245 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
3246 cert.as_ptr(),
3247 cert.len() as i32,
3248 key.as_ptr(),
3249 key.len() as i32,
3250 pwd,
3251 pwd_len,
3252 ))?;
3253 } else {
3254 esp_eap_client_clear_certificate_and_key();
3255 }
3256
3257 if let Some(cfg) = &config.eap_fast_config {
3258 let params = esp_eap_fast_config {
3259 fast_provisioning: cfg.fast_provisioning as i32,
3260 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
3261 fast_pac_format_binary: cfg.fast_pac_format_binary,
3262 };
3263 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
3264 }
3265
3266 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
3267
3268 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
3275
3276 Ok(())
3277 }
3278 }
3279}