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};
52use core::{
53 fmt::{Debug, Write},
54 marker::PhantomData,
55 mem::MaybeUninit,
56 ptr::addr_of,
57};
58
59use docsplay::Display;
60use embassy_sync::{blocking_mutex::raw::NoopRawMutex, waitqueue::GenericAtomicWaker};
61use enumset::{EnumSet, EnumSetType};
62use esp_config::esp_config_int;
63use esp_hal::system::Cpu;
64#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
65use esp_hal::time::{Duration, Instant};
66use esp_sync::NonReentrantMutex;
67use event::EVENT_CHANNEL;
68use portable_atomic::{AtomicU8, AtomicUsize, Ordering};
69use procmacros::BuilderLite;
70
71pub(crate) use self::os_adapter::*;
72#[cfg(all(feature = "sniffer", feature = "unstable"))]
73#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
74use self::sniffer::Sniffer;
75#[cfg(feature = "wifi-eap")]
76use self::sta::eap::EapStationConfig;
77use self::{
78 ap::{AccessPointConfig, AccessPointInfo, convert_ap_info},
79 private::PacketBuffer,
80 scan::{FreeApListOnDrop, ScanConfig, ScanResults, ScanTypeConfig},
81 sta::StationConfig,
82 state::*,
83};
84use crate::{
85 RadioRefGuard,
86 asynch::AtomicWaker,
87 hal::ram,
88 refcount::Refcount,
89 sys::{
90 c_types,
91 include::{self, *},
92 },
93 wifi::event::{EventInfo, WifiEvent},
94};
95pub mod ap;
96
97unstable_module!(
98 #[cfg(feature = "csi")]
99 #[cfg_attr(docsrs, doc(cfg(feature = "csi")))]
100 pub mod csi;
101 pub mod event;
102 #[cfg(feature = "sniffer")]
103 #[cfg_attr(docsrs, doc(cfg(feature = "sniffer")))]
104 pub mod sniffer;
105);
106
107pub mod scan;
108pub mod sta;
109
110pub(crate) mod os_adapter;
111pub(crate) mod state;
112
113#[cfg(not(esp32))]
114mod ftm_calibration;
115mod internal;
116
117const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU");
118
119#[cfg(esp32c2)]
123const TOTAL_HW_ENCRYPT_KEYS: u8 = 4;
124#[cfg(not(esp32c2))]
125const TOTAL_HW_ENCRYPT_KEYS: u8 = 17;
126
127#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
129#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130enum LinkState {
131 #[default]
133 Down,
134 Up,
136}
137
138#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141#[non_exhaustive]
142pub enum AuthenticationMethod {
143 None,
145
146 Wep,
148
149 Wpa,
151
152 #[default]
154 Wpa2Personal,
155
156 WpaWpa2Personal,
158
159 Wpa2Enterprise,
161
162 Wpa3Personal,
164
165 Wpa2Wpa3Personal,
167
168 WapiPersonal,
170
171 Owe,
173
174 Wpa3EntSuiteB192Bit,
176
177 Wpa3ExtPsk,
181
182 Wpa3ExtPskMixed,
186
187 Dpp,
189
190 Wpa3Enterprise,
192
193 Wpa2Wpa3Enterprise,
195
196 WpaEnterprise,
198}
199
200#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
202#[cfg_attr(feature = "defmt", derive(defmt::Format))]
203#[non_exhaustive]
204pub struct Protocols {
205 _2_4: EnumSet<Protocol>,
207 #[cfg(wifi_has_5g)]
209 _5: EnumSet<Protocol>,
210}
211
212impl Default for Protocols {
213 fn default() -> Self {
214 Self {
215 _2_4: Protocol::B | Protocol::G | Protocol::N,
216 #[cfg(wifi_has_5g)]
217 _5: Protocol::AC | Protocol::A | Protocol::AX,
218 }
219 }
220}
221
222impl Protocols {
223 fn to_raw(self) -> wifi_protocols_t {
224 wifi_protocols_t {
225 ghz_2g: to_mask(self._2_4),
226 #[cfg(wifi_has_5g)]
227 ghz_5g: to_mask(self._5),
228 #[cfg(not(wifi_has_5g))]
229 ghz_5g: 0,
230 }
231 }
232}
233
234#[cfg_attr(docsrs, procmacros::doc_replace(
235 "hint_5g" => {
236 cfg(wifi_has_5g) => "The default protocol is AC/A/AX for band mode 5G.",
237 _ => ""
238 },
239))]
240#[derive(Debug, PartialOrd, Hash, EnumSetType)]
245#[cfg_attr(feature = "defmt", derive(defmt::Format))]
246#[non_exhaustive]
247pub enum Protocol {
248 B,
250
251 G,
253
254 N,
256
257 LR,
259
260 A,
262
263 AC,
265
266 AX,
268}
269
270impl Protocol {
271 fn to_mask(self) -> u16 {
272 let mask = match self {
273 Protocol::B => WIFI_PROTOCOL_11B,
274 Protocol::G => WIFI_PROTOCOL_11G,
275 Protocol::N => WIFI_PROTOCOL_11N,
276 Protocol::LR => WIFI_PROTOCOL_LR,
277 Protocol::A => WIFI_PROTOCOL_11A,
278 Protocol::AC => WIFI_PROTOCOL_11AC,
279 Protocol::AX => WIFI_PROTOCOL_11AX,
280 };
281 mask as _
282 }
283}
284
285fn to_mask(protocols: EnumSet<Protocol>) -> u16 {
286 protocols.iter().fold(0, |acc, p| acc | p.to_mask())
287}
288
289#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Hash)]
291#[cfg_attr(feature = "defmt", derive(defmt::Format))]
292pub enum SecondaryChannel {
293 #[default]
295 None,
296
297 Above,
299
300 Below,
302}
303
304impl SecondaryChannel {
305 fn from_raw(raw: u32) -> Self {
306 match raw {
307 0 => SecondaryChannel::None,
308 1 => SecondaryChannel::Above,
309 2 => SecondaryChannel::Below,
310 _ => panic!("Invalid secondary channel value: {}", raw),
311 }
312 }
313
314 #[cfg(any(feature = "sniffer", feature = "esp-now"))]
315 fn from_raw_or_default(raw: u32) -> Self {
316 match raw {
317 0 => SecondaryChannel::None,
318 1 => SecondaryChannel::Above,
319 2 => SecondaryChannel::Below,
320 _ => SecondaryChannel::None,
321 }
322 }
323}
324
325#[cfg_attr(docsrs, procmacros::doc_replace(
326 "default_band_mode" => {
327 cfg(wifi_has_5g) => "BandMode::Auto",
328 _ => "BandMode::_2_4G"
329 },
330))]
331#[allow(clippy::large_enum_variant)]
335#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
336#[cfg_attr(feature = "defmt", derive(defmt::Format))]
337#[non_exhaustive]
338pub enum BandMode {
339 #[cfg_attr(not(wifi_has_5g), default)]
341 _2_4G,
342 #[cfg(wifi_has_5g)]
344 _5G,
345 #[cfg_attr(wifi_has_5g, default)]
347 #[cfg(wifi_has_5g)]
348 Auto,
349}
350
351impl BandMode {
352 fn to_raw(&self) -> u32 {
353 match self {
354 BandMode::_2_4G => wifi_band_mode_t_WIFI_BAND_MODE_2G_ONLY,
355 #[cfg(wifi_has_5g)]
356 BandMode::_5G => wifi_band_mode_t_WIFI_BAND_MODE_5G_ONLY,
357 #[cfg(wifi_has_5g)]
358 BandMode::Auto => wifi_band_mode_t_WIFI_BAND_MODE_AUTO,
359 }
360 }
361}
362
363#[allow(clippy::large_enum_variant)]
365#[derive(Clone, Debug, PartialEq, Eq, Hash)]
366#[cfg_attr(feature = "defmt", derive(defmt::Format))]
367#[non_exhaustive]
368pub enum Config {
369 Station(StationConfig),
371
372 AccessPoint(AccessPointConfig),
374
375 AccessPointStation(StationConfig, AccessPointConfig),
377
378 #[cfg(feature = "wifi-eap")]
380 EapStation(EapStationConfig),
381}
382
383impl Config {
384 fn validate(&self) -> Result<(), WifiError> {
385 match self {
386 Config::Station(station_configuration) => station_configuration.validate(),
387 Config::AccessPoint(access_point_configuration) => {
388 access_point_configuration.validate()
389 }
390 Config::AccessPointStation(station_configuration, access_point_configuration) => {
391 station_configuration.validate()?;
392 access_point_configuration.validate()
393 }
394 #[cfg(feature = "wifi-eap")]
395 Config::EapStation(eap_station_configuration) => eap_station_configuration.validate(),
396 }
397 }
398}
399
400impl AuthenticationMethod {
401 fn to_raw(self) -> wifi_auth_mode_t {
402 match self {
403 AuthenticationMethod::None => include::wifi_auth_mode_t_WIFI_AUTH_OPEN,
404 AuthenticationMethod::Wep => include::wifi_auth_mode_t_WIFI_AUTH_WEP,
405 AuthenticationMethod::Wpa => include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK,
406 AuthenticationMethod::Wpa2Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK,
407 AuthenticationMethod::WpaWpa2Personal => {
408 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK
409 }
410 AuthenticationMethod::Wpa2Enterprise => {
411 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE
412 }
413 AuthenticationMethod::Wpa3Personal => include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK,
414 AuthenticationMethod::Wpa2Wpa3Personal => {
415 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK
416 }
417 AuthenticationMethod::WapiPersonal => include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK,
418 AuthenticationMethod::Owe => include::wifi_auth_mode_t_WIFI_AUTH_OWE,
419 AuthenticationMethod::Wpa3EntSuiteB192Bit => {
420 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192
421 }
422 AuthenticationMethod::Wpa3ExtPsk | AuthenticationMethod::Wpa3ExtPskMixed => {
424 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK
425 }
426 AuthenticationMethod::Dpp => include::wifi_auth_mode_t_WIFI_AUTH_DPP,
427 AuthenticationMethod::Wpa3Enterprise => {
428 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE
429 }
430 AuthenticationMethod::Wpa2Wpa3Enterprise => {
431 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE
432 }
433 AuthenticationMethod::WpaEnterprise => {
434 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE
435 }
436 }
437 }
438
439 fn from_raw(raw: wifi_auth_mode_t) -> Self {
440 match raw {
441 include::wifi_auth_mode_t_WIFI_AUTH_OPEN => AuthenticationMethod::None,
442 include::wifi_auth_mode_t_WIFI_AUTH_WEP => AuthenticationMethod::Wep,
443 include::wifi_auth_mode_t_WIFI_AUTH_WPA_PSK => AuthenticationMethod::Wpa,
444 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_PSK => AuthenticationMethod::Wpa2Personal,
445 include::wifi_auth_mode_t_WIFI_AUTH_WPA_WPA2_PSK => {
446 AuthenticationMethod::WpaWpa2Personal
447 }
448 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_ENTERPRISE => {
449 AuthenticationMethod::Wpa2Enterprise
450 }
451 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_PSK => AuthenticationMethod::Wpa3Personal,
452 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_PSK => {
453 AuthenticationMethod::Wpa2Wpa3Personal
454 }
455 include::wifi_auth_mode_t_WIFI_AUTH_WAPI_PSK => AuthenticationMethod::WapiPersonal,
456 include::wifi_auth_mode_t_WIFI_AUTH_OWE => AuthenticationMethod::Owe,
457 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENT_192 => {
458 AuthenticationMethod::Wpa3EntSuiteB192Bit
459 }
460 include::wifi_auth_mode_t_WIFI_AUTH_DUMMY_1
462 | include::wifi_auth_mode_t_WIFI_AUTH_DUMMY_2 => AuthenticationMethod::Wpa3Personal,
463 include::wifi_auth_mode_t_WIFI_AUTH_DPP => AuthenticationMethod::Dpp,
464 include::wifi_auth_mode_t_WIFI_AUTH_WPA3_ENTERPRISE => {
465 AuthenticationMethod::Wpa3Enterprise
466 }
467 include::wifi_auth_mode_t_WIFI_AUTH_WPA2_WPA3_ENTERPRISE => {
468 AuthenticationMethod::Wpa2Wpa3Enterprise
469 }
470 include::wifi_auth_mode_t_WIFI_AUTH_WPA_ENTERPRISE => {
471 AuthenticationMethod::WpaEnterprise
472 }
473 _ => AuthenticationMethod::None,
480 }
481 }
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
486#[cfg_attr(feature = "defmt", derive(defmt::Format))]
487#[non_exhaustive]
488enum WifiMode {
489 Station,
491 AccessPoint,
493 AccessPointStation,
495}
496
497impl WifiMode {
498 pub(crate) fn current() -> Result<Self, WifiError> {
499 let mut mode = wifi_mode_t_WIFI_MODE_NULL;
500 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?;
501
502 Ok(Self::from_raw(mode))
503 }
504
505 fn is_station(&self) -> bool {
507 match self {
508 Self::Station | Self::AccessPointStation => true,
509 Self::AccessPoint => false,
510 }
511 }
512
513 fn is_access_point(&self) -> bool {
515 match self {
516 Self::Station => false,
517 Self::AccessPoint | Self::AccessPointStation => true,
518 }
519 }
520
521 fn from_raw(value: wifi_mode_t) -> Self {
523 #[allow(non_upper_case_globals)]
524 match value {
525 include::wifi_mode_t_WIFI_MODE_STA => Self::Station,
526 include::wifi_mode_t_WIFI_MODE_AP => Self::AccessPoint,
527 include::wifi_mode_t_WIFI_MODE_APSTA => Self::AccessPointStation,
528 _ => panic!("Invalid wifi mode value: {}", value),
529 }
530 }
531}
532
533impl From<&Config> for WifiMode {
534 fn from(config: &Config) -> Self {
535 match config {
536 Config::AccessPoint(_) => Self::AccessPoint,
537 Config::Station(_) => Self::Station,
538 Config::AccessPointStation(_, _) => Self::AccessPointStation,
539 #[cfg(feature = "wifi-eap")]
540 Config::EapStation(_) => Self::Station,
541 }
542 }
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
547#[cfg_attr(feature = "defmt", derive(defmt::Format))]
548#[non_exhaustive]
549pub enum DisconnectReason {
550 Unspecified,
552 AuthenticationExpired,
554 AuthenticationLeave,
556 DisassociatedDueToInactivity,
558 AssociationTooMany,
560 Class2FrameFromNonAuthenticatedStation,
562 Class3FrameFromNonAssociatedStation,
564 AssociationLeave,
566 AssociationNotAuthenticated,
568 DisassociatedPowerCapabilityBad,
570 DisassociatedUnsupportedChannel,
572 BssTransitionDisassociated,
574 IeInvalid,
576 MicFailure,
578 FourWayHandshakeTimeout,
580 GroupKeyUpdateTimeout,
582 IeIn4wayDiffers,
584 GroupCipherInvalid,
586 PairwiseCipherInvalid,
588 AkmpInvalid,
590 UnsupportedRsnIeVersion,
592 InvalidRsnIeCapabilities,
594 _802_1xAuthenticationFailed,
596 CipherSuiteRejected,
598 TdlsPeerUnreachable,
600 TdlsUnspecified,
602 SspRequestedDisassociation,
604 NoSspRoamingAgreement,
606 BadCipherOrAkm,
608 NotAuthorizedThisLocation,
610 ServiceChangePercludesTs,
612 UnspecifiedQos,
614 NotEnoughBandwidth,
616 MissingAcks,
618 ExceededTxOp,
620 StationLeaving,
622 EndBlockAck,
624 UnknownBlockAck,
626 Timeout,
628 PeerInitiated,
630 AccessPointInitiatedDisassociation,
632 InvalidFtActionFrameCount,
634 InvalidPmkid,
636 InvalidMde,
638 InvalidFte,
640 TransmissionLinkEstablishmentFailed,
642 AlterativeChannelOccupied,
644 BeaconTimeout,
646 NoAccessPointFound,
648 AuthenticationFailed,
650 AssociationFailed,
652 HandshakeTimeout,
654 ConnectionFailed,
656 AccessPointTsfReset,
658 Roaming,
660 AssociationComebackTimeTooLong,
662 SaQueryTimeout,
664 NoAccessPointFoundWithCompatibleSecurity,
666 NoAccessPointFoundInAuthmodeThreshold,
668 NoAccessPointFoundInRssiThreshold,
670}
671
672impl DisconnectReason {
673 fn from_raw(id: u16) -> Self {
674 match id {
675 1 => Self::Unspecified,
676 2 => Self::AuthenticationExpired,
677 3 => Self::AuthenticationLeave,
678 4 => Self::DisassociatedDueToInactivity,
679 5 => Self::AssociationTooMany,
680 6 => Self::Class2FrameFromNonAuthenticatedStation,
681 7 => Self::Class3FrameFromNonAssociatedStation,
682 8 => Self::AssociationLeave,
683 9 => Self::AssociationNotAuthenticated,
684 10 => Self::DisassociatedPowerCapabilityBad,
685 11 => Self::DisassociatedUnsupportedChannel,
686 12 => Self::BssTransitionDisassociated,
687 13 => Self::IeInvalid,
688 14 => Self::MicFailure,
689 15 => Self::FourWayHandshakeTimeout,
690 16 => Self::GroupKeyUpdateTimeout,
691 17 => Self::IeIn4wayDiffers,
692 18 => Self::GroupCipherInvalid,
693 19 => Self::PairwiseCipherInvalid,
694 20 => Self::AkmpInvalid,
695 21 => Self::UnsupportedRsnIeVersion,
696 22 => Self::InvalidRsnIeCapabilities,
697 23 => Self::_802_1xAuthenticationFailed,
698 24 => Self::CipherSuiteRejected,
699 25 => Self::TdlsPeerUnreachable,
700 26 => Self::TdlsUnspecified,
701 27 => Self::SspRequestedDisassociation,
702 28 => Self::NoSspRoamingAgreement,
703 29 => Self::BadCipherOrAkm,
704 30 => Self::NotAuthorizedThisLocation,
705 31 => Self::ServiceChangePercludesTs,
706 32 => Self::UnspecifiedQos,
707 33 => Self::NotEnoughBandwidth,
708 34 => Self::MissingAcks,
709 35 => Self::ExceededTxOp,
710 36 => Self::StationLeaving,
711 37 => Self::EndBlockAck,
712 38 => Self::UnknownBlockAck,
713 39 => Self::Timeout,
714 46 => Self::PeerInitiated,
715 47 => Self::AccessPointInitiatedDisassociation,
716 48 => Self::InvalidFtActionFrameCount,
717 49 => Self::InvalidPmkid,
718 50 => Self::InvalidMde,
719 51 => Self::InvalidFte,
720 67 => Self::TransmissionLinkEstablishmentFailed,
721 68 => Self::AlterativeChannelOccupied,
722 200 => Self::BeaconTimeout,
723 201 => Self::NoAccessPointFound,
724 202 => Self::AuthenticationFailed,
725 203 => Self::AssociationFailed,
726 204 => Self::HandshakeTimeout,
727 205 => Self::ConnectionFailed,
728 206 => Self::AccessPointTsfReset,
729 207 => Self::Roaming,
730 208 => Self::AssociationComebackTimeTooLong,
731 209 => Self::SaQueryTimeout,
732 210 => Self::NoAccessPointFoundWithCompatibleSecurity,
733 211 => Self::NoAccessPointFoundInAuthmodeThreshold,
734 212 => Self::NoAccessPointFoundInRssiThreshold,
735 _ => Self::Unspecified,
736 }
737 }
738}
739
740#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
744pub struct Ssid {
745 ssid: [u8; 32],
746 len: u8,
747}
748
749impl Ssid {
750 pub(crate) fn new(ssid: &str) -> Result<Self, WifiError> {
751 let mut ssid_bytes = [0u8; 32];
752 let bytes = ssid.as_bytes();
753
754 if bytes.len() > 32 {
755 warn!("SSID is longer than 32 bytes");
756 return Err(WifiError::InvalidSsid);
757 }
758
759 let len = bytes.len();
760 ssid_bytes[..len].copy_from_slice(bytes);
761
762 Self::from_raw(&ssid_bytes, len as u8)
763 }
764
765 pub(crate) fn from_raw(ssid: &[u8], len: u8) -> Result<Self, WifiError> {
766 let len = len as usize;
767 if len > 32 {
768 warn!("SSID is longer than 32 bytes");
769 return Err(WifiError::InvalidSsid);
770 }
771 if ssid.len() < len {
772 warn!("SSID buffer shorter than reported length");
773 return Err(WifiError::InvalidSsid);
774 }
775
776 let mut ssid_bytes = [0u8; 32];
777 ssid_bytes[..len].copy_from_slice(&ssid[..len]);
778
779 Ok(Self {
780 ssid: ssid_bytes,
781 len: len as u8,
782 })
783 }
784
785 pub(crate) fn as_bytes(&self) -> &[u8] {
786 &self.ssid[..self.len as usize]
787 }
788
789 pub fn len(&self) -> usize {
791 self.len as usize
792 }
793
794 pub fn is_empty(&self) -> bool {
796 self.len == 0
797 }
798
799 pub fn as_str(&self) -> &str {
801 let part = &self.ssid[..self.len as usize];
802 match str::from_utf8(part) {
803 Ok(s) => s,
804 Err(e) => {
805 let (valid, _) = part.split_at(e.valid_up_to());
806 unsafe { str::from_utf8_unchecked(valid) }
807 }
808 }
809 }
810}
811
812impl Debug for Ssid {
813 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
814 f.write_char('"')?;
815 f.write_str(self.as_str())?;
816 f.write_char('"')
817 }
818}
819
820#[cfg(feature = "defmt")]
821impl defmt::Format for Ssid {
822 fn format(&self, fmt: defmt::Formatter<'_>) {
823 defmt::write!(fmt, "{}", self.as_str())
824 }
825}
826
827impl TryFrom<alloc::string::String> for Ssid {
828 type Error = WifiError;
829
830 fn try_from(ssid: alloc::string::String) -> Result<Self, Self::Error> {
831 Self::new(&ssid)
832 }
833}
834
835impl TryFrom<&str> for Ssid {
836 type Error = WifiError;
837
838 fn try_from(ssid: &str) -> Result<Self, Self::Error> {
839 Self::new(ssid)
840 }
841}
842
843impl TryFrom<&[u8]> for Ssid {
844 type Error = WifiError;
845
846 fn try_from(ssid: &[u8]) -> Result<Self, Self::Error> {
847 if ssid.len() > 32 {
848 warn!("SSID is longer than 32 bytes");
849 return Err(WifiError::InvalidSsid);
850 }
851
852 Self::from_raw(ssid, ssid.len() as u8)
853 }
854}
855
856#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
858#[cfg_attr(feature = "defmt", derive(defmt::Format))]
859#[non_exhaustive]
860pub enum AuthenticationMethodConfig {
861 Open,
863
864 Wep(Password),
866
867 Wpa(Password),
869
870 Wpa2Personal(Password),
872
873 WpaWpa2Personal(Password),
875}
876
877impl AuthenticationMethodConfig {
878 fn auth_method(&self) -> AuthenticationMethod {
879 match self {
880 AuthenticationMethodConfig::Open => AuthenticationMethod::None,
881 AuthenticationMethodConfig::Wep(_) => AuthenticationMethod::Wep,
882 AuthenticationMethodConfig::Wpa(_) => AuthenticationMethod::Wpa,
883 AuthenticationMethodConfig::Wpa2Personal(_) => AuthenticationMethod::Wpa2Personal,
884 AuthenticationMethodConfig::WpaWpa2Personal(_) => AuthenticationMethod::WpaWpa2Personal,
885 }
886 }
887
888 fn password(&self) -> Option<&[u8]> {
889 match self {
890 AuthenticationMethodConfig::Open => None,
891 AuthenticationMethodConfig::Wep(password)
892 | AuthenticationMethodConfig::Wpa(password)
893 | AuthenticationMethodConfig::Wpa2Personal(password)
894 | AuthenticationMethodConfig::WpaWpa2Personal(password) => Some(password.as_bytes()),
895 }
896 }
897}
898
899#[derive(Clone, Copy, PartialEq, Eq, Hash)]
908pub struct Password {
909 password: [u8; 64],
910 len: u8,
911}
912
913impl Password {
914 pub(crate) fn new(password: &str) -> Result<Self, WifiError> {
915 Self::from_raw(password.as_bytes())
916 }
917
918 pub(crate) fn from_raw(password: &[u8]) -> Result<Self, WifiError> {
919 if password.len() > 64 {
920 warn!("Password is longer than 64 bytes");
921 return Err(WifiError::InvalidPassword);
922 }
923
924 let mut pwd_bytes = [0u8; 64];
925 let len = password.len();
926 pwd_bytes[..len].copy_from_slice(password);
927
928 Ok(Self {
929 password: pwd_bytes,
930 len: len as u8,
931 })
932 }
933
934 pub(crate) fn as_bytes(&self) -> &[u8] {
935 &self.password[..self.len as usize]
936 }
937
938 pub fn len(&self) -> usize {
940 self.len as usize
941 }
942
943 pub fn is_empty(&self) -> bool {
945 self.len == 0
946 }
947}
948
949impl Default for Password {
950 fn default() -> Self {
951 Self {
952 password: [0u8; 64],
953 len: 0,
954 }
955 }
956}
957
958impl Debug for Password {
959 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
960 f.write_str("**REDACTED**")
961 }
962}
963
964#[cfg(feature = "defmt")]
965impl defmt::Format for Password {
966 fn format(&self, fmt: defmt::Formatter<'_>) {
967 defmt::write!(fmt, "**REDACTED**")
968 }
969}
970
971impl TryFrom<alloc::string::String> for Password {
972 type Error = WifiError;
973
974 fn try_from(password: alloc::string::String) -> Result<Self, Self::Error> {
975 Self::new(&password)
976 }
977}
978
979impl TryFrom<&str> for Password {
980 type Error = WifiError;
981
982 fn try_from(password: &str) -> Result<Self, Self::Error> {
983 Self::new(password)
984 }
985}
986
987impl TryFrom<&[u8]> for Password {
988 type Error = WifiError;
989
990 fn try_from(password: &[u8]) -> Result<Self, Self::Error> {
991 Self::from_raw(password)
992 }
993}
994
995static TX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0);
996
997struct PacketQueue {
1004 queue: VecDeque<PacketBuffer>,
1005
1006 waker: GenericAtomicWaker<NoopRawMutex>,
1008}
1009
1010impl PacketQueue {
1011 const fn new() -> Self {
1012 Self {
1013 queue: VecDeque::new(),
1014 waker: GenericAtomicWaker::new(NoopRawMutex::new()),
1015 }
1016 }
1017
1018 fn change_capacity(&mut self, new_capacity: usize) -> Result<(), WifiError> {
1019 let new_capacity = new_capacity.max(self.queue.capacity());
1022 let additional = new_capacity.saturating_sub(self.queue.capacity());
1023 self.queue
1024 .try_reserve_exact(additional)
1025 .map_err(|_| WifiError::OutOfMemory)
1026 }
1027
1028 fn push_back(&mut self, packet: PacketBuffer) -> Result<(), PacketBuffer> {
1029 if self.len() >= self.queue.capacity() {
1030 return Err(packet);
1031 }
1032
1033 self.queue.push_back(packet);
1034 self.waker.wake();
1035
1036 Ok(())
1037 }
1038
1039 fn pop_front(&mut self) -> Option<PacketBuffer> {
1040 self.queue.pop_front()
1041 }
1042
1043 fn len(&self) -> usize {
1044 self.queue.len()
1045 }
1046
1047 fn is_empty(&self) -> bool {
1048 self.queue.is_empty()
1049 }
1050
1051 fn register_waker(&mut self, waker: &core::task::Waker) {
1052 self.waker.register(waker);
1053 }
1054}
1055
1056static DATA_QUEUE_RX_AP: NonReentrantMutex<PacketQueue> =
1057 NonReentrantMutex::new(PacketQueue::new());
1058
1059static DATA_QUEUE_RX_STA: NonReentrantMutex<PacketQueue> =
1060 NonReentrantMutex::new(PacketQueue::new());
1061
1062#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1064#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1065#[non_exhaustive]
1066pub enum WifiError {
1067 Unsupported,
1069
1070 InvalidArguments,
1072
1073 Other,
1075
1076 OutOfMemory,
1078
1079 InvalidSsid,
1081
1082 InvalidPassword,
1084
1085 NotConnected,
1087}
1088
1089impl WifiError {
1090 fn from_error_code(code: i32) -> Self {
1091 use crate::sys::include::*;
1092
1093 if code == ESP_FAIL {
1096 return WifiError::Other;
1097 }
1098
1099 match code as u32 {
1100 ESP_ERR_NO_MEM => WifiError::OutOfMemory,
1103 ESP_ERR_INVALID_ARG => WifiError::InvalidArguments,
1104 ESP_ERR_WIFI_SSID => WifiError::InvalidSsid,
1105 ESP_ERR_WIFI_PASSWORD => WifiError::InvalidPassword,
1106 ESP_ERR_WIFI_NOT_CONNECT => WifiError::NotConnected,
1107
1108 ESP_ERR_WIFI_NOT_INIT
1111 | ESP_ERR_WIFI_NOT_STARTED
1112 | ESP_ERR_WIFI_STATE
1113 | ESP_ERR_WIFI_CONN
1114 | ESP_ERR_WIFI_STOP_STATE
1115 | ESP_ERR_WIFI_TIMEOUT => WifiError::Other,
1116
1117 _ => {
1121 warn!(
1122 "Unmapped Wi-Fi error code: {}. Please open an issue at <https://github.com/esp-rs/esp-hal/issues>.",
1123 code
1124 );
1125 WifiError::Other
1126 }
1127 }
1128 }
1129}
1130
1131impl core::error::Error for WifiError {}
1132
1133#[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1135#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1136#[non_exhaustive]
1137pub enum ConnectionError {
1138 Failed(sta::DisconnectedInfo),
1140
1141 WifiError(WifiError),
1143}
1144
1145impl From<WifiError> for ConnectionError {
1146 fn from(error: WifiError) -> Self {
1147 ConnectionError::WifiError(error)
1148 }
1149}
1150
1151impl core::error::Error for ConnectionError {}
1152
1153#[cfg(esp32)]
1154fn set_mac_time_update_cb(_wifi: crate::hal::peripherals::WIFI<'_>) {
1155 use crate::sys::include::esp_wifi_internal_update_mac_time;
1156 unsafe {
1157 esp_phy::set_mac_time_update_cb(|duration| {
1158 esp_wifi_internal_update_mac_time(duration.as_micros() as u32);
1159 });
1160 }
1161}
1162
1163pub(crate) fn wifi_init(_wifi: crate::hal::peripherals::WIFI<'_>) -> Result<(), WifiError> {
1164 #[cfg(esp32)]
1165 set_mac_time_update_cb(_wifi);
1166 unsafe {
1167 #[cfg(feature = "coex")]
1168 esp_wifi_result!(coex_init())?;
1169
1170 esp_wifi_result!(esp_wifi_init_internal(addr_of!(internal::G_CONFIG)))?;
1171 esp_wifi_result!(esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL))?;
1172
1173 esp_wifi_result!(esp_supplicant_init())?;
1174
1175 esp_wifi_result!(esp_wifi_set_tx_done_cb(Some(esp_wifi_tx_done_cb)))?;
1176
1177 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1178 wifi_interface_t_WIFI_IF_STA,
1179 Some(recv_cb_sta)
1180 ))?;
1181
1182 esp_wifi_result!(esp_wifi_internal_reg_rxcb(
1184 wifi_interface_t_WIFI_IF_AP,
1185 Some(recv_cb_ap)
1186 ))?;
1187
1188 Ok(())
1189 }
1190}
1191
1192#[cfg(feature = "coex")]
1193pub(crate) fn coex_initialize() -> i32 {
1194 debug!("call coex-initialize");
1195 unsafe {
1196 let res = crate::sys::include::esp_coex_adapter_register(
1197 core::ptr::addr_of_mut!(internal::G_COEX_ADAPTER_FUNCS).cast(),
1198 );
1199 if res != 0 {
1200 error!("Error: esp_coex_adapter_register {}", res);
1201 return res;
1202 }
1203 let res = crate::sys::include::coex_pre_init();
1204 if res != 0 {
1205 error!("Error: coex_pre_init {}", res);
1206 return res;
1207 }
1208 0
1209 }
1210}
1211
1212pub(crate) unsafe extern "C" fn coex_init() -> i32 {
1213 debug!("coex-init");
1214
1215 cfg_select! {
1216 feature = "coex" => unsafe { crate::sys::include::coex_init() },
1217 _ => 0,
1218 }
1219}
1220
1221fn wifi_deinit() -> Result<(), WifiError> {
1222 esp_wifi_result!(unsafe { esp_wifi_stop() })?;
1223
1224 while let Some(packet) = DATA_QUEUE_RX_STA.with(|q| q.pop_front()) {
1231 drop(packet);
1232 }
1233 while let Some(packet) = DATA_QUEUE_RX_AP.with(|q| q.pop_front()) {
1234 drop(packet);
1235 }
1236
1237 esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
1238 esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
1239 Ok(())
1240}
1241
1242unsafe extern "C" fn recv_cb_sta(
1243 buffer: *mut c_types::c_void,
1244 len: u16,
1245 eb: *mut c_types::c_void,
1246) -> esp_err_t {
1247 let packet = PacketBuffer { buffer, len, eb };
1248 match DATA_QUEUE_RX_STA.with(|queue| queue.push_back(packet)) {
1255 Ok(()) => include::ESP_OK as esp_err_t,
1256 _ => {
1257 debug!("RX QUEUE FULL");
1258 include::ESP_ERR_NO_MEM as esp_err_t
1259 }
1260 }
1261}
1262
1263unsafe extern "C" fn recv_cb_ap(
1264 buffer: *mut c_types::c_void,
1265 len: u16,
1266 eb: *mut c_types::c_void,
1267) -> esp_err_t {
1268 let packet = PacketBuffer { buffer, len, eb };
1269 match DATA_QUEUE_RX_AP.with(|queue| queue.push_back(packet)) {
1276 Ok(()) => include::ESP_OK as esp_err_t,
1277 _ => {
1278 debug!("RX QUEUE FULL");
1279 include::ESP_ERR_NO_MEM as esp_err_t
1280 }
1281 }
1282}
1283
1284pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0);
1285
1286fn decrement_inflight_counter() {
1287 unwrap!(
1288 WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
1289 Some(x.saturating_sub(1))
1290 })
1291 );
1292}
1293
1294#[ram]
1295unsafe extern "C" fn esp_wifi_tx_done_cb(
1296 _ifidx: u8,
1297 _data: *mut u8,
1298 _data_len: *mut u16,
1299 _tx_status: bool,
1300) {
1301 trace!("esp_wifi_tx_done_cb");
1302
1303 decrement_inflight_counter();
1304
1305 TRANSMIT_WAKER.wake();
1306}
1307
1308pub(crate) fn wifi_start_scan(
1309 block: bool,
1310 ScanConfig {
1311 ssid,
1312 mut bssid,
1313 channel,
1314 show_hidden,
1315 scan_type,
1316 ..
1317 }: ScanConfig,
1318) -> i32 {
1319 scan_type.validate();
1320 let (scan_time, scan_type) = match scan_type {
1321 ScanTypeConfig::Active { min, max } => (
1322 wifi_scan_time_t {
1323 active: wifi_active_scan_time_t {
1324 min: min.as_millis() as u32,
1325 max: max.as_millis() as u32,
1326 },
1327 passive: 0,
1328 },
1329 wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE,
1330 ),
1331 ScanTypeConfig::Passive(dur) => (
1332 wifi_scan_time_t {
1333 active: wifi_active_scan_time_t { min: 0, max: 0 },
1334 passive: dur.as_millis() as u32,
1335 },
1336 wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE,
1337 ),
1338 };
1339
1340 let mut ssid_buf = ssid.map(|m| {
1341 let mut buf = Vec::from_iter(m.as_bytes().to_owned());
1342 buf.push(b'\0');
1343 buf
1344 });
1345
1346 let ssid = ssid_buf
1347 .as_mut()
1348 .map(|e| e.as_mut_ptr())
1349 .unwrap_or_else(core::ptr::null_mut);
1350 let bssid = bssid
1351 .as_mut()
1352 .map(|e| e.as_mut_ptr())
1353 .unwrap_or_else(core::ptr::null_mut);
1354
1355 let scan_config = wifi_scan_config_t {
1356 ssid,
1357 bssid,
1358 channel: channel.unwrap_or(0),
1359 show_hidden,
1360 scan_type,
1361 scan_time,
1362 home_chan_dwell_time: 0,
1363 channel_bitmap: wifi_scan_channel_bitmap_t {
1364 ghz_2_channels: 0,
1365 ghz_5_channels: 0,
1366 },
1367 coex_background_scan: false,
1368 };
1369
1370 unsafe { esp_wifi_scan_start(&scan_config, block) }
1371}
1372
1373mod private {
1374 use super::*;
1375
1376 #[derive(Debug)]
1384 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1385 pub struct PacketBuffer {
1386 pub(crate) buffer: *mut c_types::c_void,
1387 pub(crate) len: u16,
1388 pub(crate) eb: *mut c_types::c_void,
1389 }
1390
1391 unsafe impl Send for PacketBuffer {}
1392
1393 impl Drop for PacketBuffer {
1394 fn drop(&mut self) {
1395 trace!("Dropping PacketBuffer, freeing memory");
1396 unsafe { esp_wifi_internal_free_rx_buffer(self.eb) };
1397 }
1398 }
1399
1400 impl PacketBuffer {
1401 pub fn as_slice_mut(&mut self) -> &mut [u8] {
1402 unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) }
1403 }
1404 }
1405}
1406
1407#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
1409#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1410enum InterfaceType {
1411 Station,
1413 AccessPoint,
1415}
1416
1417impl InterfaceType {
1418 fn mac_address(&self) -> [u8; 6] {
1419 use esp_hal::efuse::InterfaceMacAddress;
1420 let mac = match self {
1421 InterfaceType::Station => {
1422 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::Station)
1423 }
1424 InterfaceType::AccessPoint => {
1425 esp_hal::efuse::interface_mac_address(InterfaceMacAddress::AccessPoint)
1426 }
1427 };
1428
1429 let mut out = [0u8; 6];
1430 out.copy_from_slice(mac.as_bytes());
1431 out
1432 }
1433
1434 fn data_queue_rx(&self) -> &'static NonReentrantMutex<PacketQueue> {
1435 match self {
1436 InterfaceType::Station => &DATA_QUEUE_RX_STA,
1437 InterfaceType::AccessPoint => &DATA_QUEUE_RX_AP,
1438 }
1439 }
1440
1441 fn can_send(&self) -> bool {
1442 WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE.load(Ordering::Relaxed)
1443 }
1444
1445 fn increase_in_flight_counter(&self) {
1446 WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst);
1447 }
1448
1449 fn tx_token(&self) -> Option<WifiTxToken> {
1450 if !self.can_send() {
1451 crate::preempt::yield_task();
1453 }
1454
1455 if self.can_send() {
1456 if self.link_state() == LinkState::Up {
1458 return Some(WifiTxToken { mode: *self });
1459 }
1460 }
1461
1462 None
1463 }
1464
1465 fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> {
1466 let is_empty = self.data_queue_rx().with(|q| q.is_empty());
1467 if is_empty || !self.can_send() {
1468 crate::preempt::yield_task();
1470 }
1471
1472 let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty());
1473
1474 if !is_empty {
1475 self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx))
1476 } else {
1477 None
1478 }
1479 }
1480
1481 fn interface(&self) -> wifi_interface_t {
1482 match self {
1483 InterfaceType::Station => wifi_interface_t_WIFI_IF_STA,
1484 InterfaceType::AccessPoint => wifi_interface_t_WIFI_IF_AP,
1485 }
1486 }
1487
1488 fn register_transmit_waker(&self, waker: &core::task::Waker) {
1489 TRANSMIT_WAKER.register(waker)
1490 }
1491
1492 fn register_receive_waker(&self, waker: &core::task::Waker) {
1493 self.data_queue_rx().with(|q| q.register_waker(waker));
1494 }
1495
1496 fn register_link_state_waker(&self, waker: &core::task::Waker) {
1497 match self {
1498 InterfaceType::Station => STA_LINK_STATE_WAKER.register(waker),
1499 InterfaceType::AccessPoint => AP_LINK_STATE_WAKER.register(waker),
1500 }
1501 }
1502
1503 fn link_state(&self) -> LinkState {
1504 let is_up = match self {
1505 InterfaceType::Station => {
1506 matches!(station_state(), WifiStationState::Connected)
1507 }
1508 InterfaceType::AccessPoint => {
1509 matches!(access_point_state(), WifiAccessPointState::Started)
1510 }
1511 };
1512
1513 if is_up {
1514 LinkState::Up
1515 } else {
1516 LinkState::Down
1517 }
1518 }
1519}
1520
1521static SINGLETONS: AtomicU8 = AtomicU8::new(0);
1522
1523const STA_BIT: u8 = 1 << 0;
1524const AP_BIT: u8 = 1 << 1;
1525#[cfg(feature = "sniffer")]
1526pub(super) const SNIFFER_BIT: u8 = 1 << 2;
1527
1528pub(super) fn try_acquire(bit: u8) -> bool {
1529 SINGLETONS.fetch_or(bit, Ordering::AcqRel) & bit == 0
1530}
1531
1532pub(super) fn release(bit: u8) {
1533 SINGLETONS.fetch_and(!bit, Ordering::Release);
1534}
1535
1536#[derive(Debug, PartialEq, Eq, Hash)]
1549#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1550pub struct Interface {
1551 mode: InterfaceType,
1552}
1553
1554impl Interface {
1555 pub fn station() -> Self {
1562 Self::try_station().expect("station interface already taken")
1563 }
1564
1565 pub fn try_station() -> Option<Self> {
1569 if try_acquire(STA_BIT) {
1570 Some(Self {
1571 mode: InterfaceType::Station,
1572 })
1573 } else {
1574 None
1575 }
1576 }
1577
1578 pub fn access_point() -> Self {
1585 Self::try_access_point().expect("access point interface already taken")
1586 }
1587
1588 pub fn try_access_point() -> Option<Self> {
1592 if try_acquire(AP_BIT) {
1593 Some(Self {
1594 mode: InterfaceType::AccessPoint,
1595 })
1596 } else {
1597 None
1598 }
1599 }
1600
1601 #[procmacros::doc_replace]
1602 pub fn mac_address(&self) -> [u8; 6] {
1617 self.mode.mac_address()
1618 }
1619
1620 #[doc(hidden)]
1621 pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> {
1623 self.mode.rx_token()
1624 }
1625
1626 #[doc(hidden)]
1627 pub fn transmit(&mut self) -> Option<WifiTxToken> {
1629 self.mode.tx_token()
1630 }
1631}
1632
1633impl Drop for Interface {
1634 fn drop(&mut self) {
1635 let bit = match self.mode {
1636 InterfaceType::Station => STA_BIT,
1637 InterfaceType::AccessPoint => AP_BIT,
1638 };
1639 release(bit);
1640 }
1641}
1642
1643#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, BuilderLite)]
1645#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1646#[non_exhaustive]
1647pub struct Bandwidths {
1648 _2_4: Bandwidth,
1650 #[cfg(wifi_has_5g)]
1652 _5: Bandwidth,
1653}
1654
1655impl Bandwidths {
1656 fn to_raw(self) -> wifi_bandwidths_t {
1657 wifi_bandwidths_t {
1658 ghz_2g: self._2_4.to_raw(),
1659 #[cfg(wifi_has_5g)]
1660 ghz_5g: self._5.to_raw(),
1661 #[cfg(not(wifi_has_5g))]
1662 ghz_5g: 0,
1663 }
1664 }
1665}
1666
1667#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1669#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1670#[allow(
1671 clippy::enum_variant_names,
1672 reason = "MHz suffix indicates physical unit."
1673)]
1674#[non_exhaustive]
1675pub enum Bandwidth {
1676 _20MHz,
1678 _40MHz,
1680 _80MHz,
1682 _160MHz,
1684 _80_80MHz,
1686}
1687
1688impl Bandwidth {
1689 fn to_raw(self) -> wifi_bandwidth_t {
1690 match self {
1691 Bandwidth::_20MHz => wifi_bandwidth_t_WIFI_BW20,
1692 Bandwidth::_40MHz => wifi_bandwidth_t_WIFI_BW40,
1693 Bandwidth::_80MHz => wifi_bandwidth_t_WIFI_BW80,
1694 Bandwidth::_160MHz => wifi_bandwidth_t_WIFI_BW160,
1695 Bandwidth::_80_80MHz => wifi_bandwidth_t_WIFI_BW80_BW80,
1696 }
1697 }
1698
1699 fn from_raw(raw: wifi_bandwidth_t) -> Self {
1700 match raw {
1701 raw if raw == wifi_bandwidth_t_WIFI_BW20 => Bandwidth::_20MHz,
1702 raw if raw == wifi_bandwidth_t_WIFI_BW40 => Bandwidth::_40MHz,
1703 raw if raw == wifi_bandwidth_t_WIFI_BW80 => Bandwidth::_80MHz,
1704 raw if raw == wifi_bandwidth_t_WIFI_BW160 => Bandwidth::_160MHz,
1705 raw if raw == wifi_bandwidth_t_WIFI_BW80_BW80 => Bandwidth::_80_80MHz,
1706 _ => Bandwidth::_20MHz,
1707 }
1708 }
1709}
1710
1711#[cfg(wifi_mac_version = "1")]
1714#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1715#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1716#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1717#[instability::unstable]
1718pub struct RxControlInfo {
1719 pub rssi: i32,
1721 pub rate: u32,
1724 pub sig_mode: u32,
1727 pub mcs: u32,
1730 pub cwb: u32,
1732 pub smoothing: u32,
1735 pub not_sounding: u32,
1738 pub aggregation: u32,
1740 pub stbc: u32,
1743 pub fec_coding: u32,
1746 pub sgi: u32,
1749 pub ampdu_cnt: u32,
1751 pub channel: u32,
1753 pub secondary_channel: SecondaryChannel,
1755 pub timestamp: Instant,
1758 pub noise_floor: i32,
1760 pub ant: u32,
1763 pub sig_len: u32,
1765 pub rx_state: u32,
1767}
1768
1769#[cfg(wifi_mac_version = "2")]
1772#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1773#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1774#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1775#[instability::unstable]
1776pub struct RxControlInfo {
1777 pub rssi: i32,
1779 pub rate: u32,
1782 pub sig_len: u32,
1784 pub rx_state: u32,
1787 pub dump_len: u32,
1789 pub he_sigb_len: u32,
1791 pub cur_single_mpdu: u32,
1793 pub cur_bb_format: u32,
1795 pub rx_channel_estimate_info_vld: u32,
1797 pub rx_channel_estimate_len: u32,
1799 pub secondary_channel: SecondaryChannel,
1801 pub channel: u32,
1803 pub noise_floor: i32,
1805 pub is_group: u32,
1807 pub rxend_state: u32,
1809 pub rxmatch3: u32,
1811 pub rxmatch2: u32,
1813 pub rxmatch1: u32,
1815 pub rxmatch0: u32,
1817 pub timestamp: Instant,
1820}
1821
1822#[cfg(wifi_mac_version = "3")]
1825#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1826#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1827#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1828#[instability::unstable]
1829pub struct RxControlInfo {
1830 pub rssi: i32,
1832 pub rate: u32,
1835 pub sig_len: u32,
1837 pub rx_state: u32,
1840 pub dump_len: u32,
1842 pub he_sigb_len: u32,
1844 pub cur_bb_format: u32,
1846 pub rx_channel_estimate_info_vld: u32,
1848 pub rx_channel_estimate_len: u32,
1850 pub secondary_channel: SecondaryChannel,
1852 pub channel: u32,
1854 pub noise_floor: i32,
1856 pub is_group: u32,
1858 pub rxend_state: u32,
1860 pub rxmatch3: u32,
1862 pub rxmatch2: u32,
1864 pub rxmatch1: u32,
1866 pub rxmatch0: u32,
1868 pub timestamp: Instant,
1871}
1872
1873#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
1874impl RxControlInfo {
1875 const fn sign_extend_i8_bitfield(value: i32) -> i32 {
1879 (value as u8 as i8) as i32
1880 }
1881
1882 pub(super) unsafe fn from_raw(rx_cntl: *const wifi_pkt_rx_ctrl_t) -> Self {
1888 #[cfg(wifi_mac_version = "1")]
1889 let rx_control_info = unsafe {
1890 RxControlInfo {
1891 rssi: Self::sign_extend_i8_bitfield((*rx_cntl).rssi()),
1892 rate: (*rx_cntl).rate(),
1893 sig_mode: (*rx_cntl).sig_mode(),
1894 mcs: (*rx_cntl).mcs(),
1895 cwb: (*rx_cntl).cwb(),
1896 smoothing: (*rx_cntl).smoothing(),
1897 not_sounding: (*rx_cntl).not_sounding(),
1898 aggregation: (*rx_cntl).aggregation(),
1899 stbc: (*rx_cntl).stbc(),
1900 fec_coding: (*rx_cntl).fec_coding(),
1901 sgi: (*rx_cntl).sgi(),
1902 ampdu_cnt: (*rx_cntl).ampdu_cnt(),
1903 channel: (*rx_cntl).channel(),
1904 secondary_channel: SecondaryChannel::from_raw_or_default(
1905 (*rx_cntl).secondary_channel(),
1906 ),
1907 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1908 noise_floor: Self::sign_extend_i8_bitfield((*rx_cntl).noise_floor()),
1909 ant: (*rx_cntl).ant(),
1910 sig_len: (*rx_cntl).sig_len(),
1911 rx_state: (*rx_cntl).rx_state(),
1912 }
1913 };
1914 #[cfg(wifi_mac_version = "2")]
1915 let rx_control_info = unsafe {
1916 RxControlInfo {
1917 rssi: Self::sign_extend_i8_bitfield((*rx_cntl).rssi()),
1918 rate: (*rx_cntl).rate(),
1919 sig_len: (*rx_cntl).sig_len(),
1920 rx_state: (*rx_cntl).rx_state(),
1921 dump_len: (*rx_cntl).dump_len(),
1922 he_sigb_len: (*rx_cntl).he_sigb_len(),
1923 cur_single_mpdu: (*rx_cntl).cur_single_mpdu(),
1924 cur_bb_format: (*rx_cntl).cur_bb_format(),
1925 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1926 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1927 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1928 channel: (*rx_cntl).channel(),
1929 noise_floor: Self::sign_extend_i8_bitfield((*rx_cntl).noise_floor()),
1930 is_group: (*rx_cntl).is_group(),
1931 rxend_state: (*rx_cntl).rxend_state(),
1932 rxmatch3: (*rx_cntl).rxmatch3(),
1933 rxmatch2: (*rx_cntl).rxmatch2(),
1934 rxmatch1: (*rx_cntl).rxmatch1(),
1935 rxmatch0: (*rx_cntl).rxmatch0(),
1936 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1937 }
1938 };
1939 #[cfg(wifi_mac_version = "3")]
1940 let rx_control_info = unsafe {
1941 RxControlInfo {
1942 rssi: Self::sign_extend_i8_bitfield((*rx_cntl).rssi()),
1943 rate: (*rx_cntl).rate(),
1944 sig_len: (*rx_cntl).sig_len(),
1945 rx_state: (*rx_cntl).rx_state(),
1946 dump_len: (*rx_cntl).dump_len(),
1947 he_sigb_len: (*rx_cntl).sigb_len(),
1948 cur_bb_format: (*rx_cntl).cur_bb_format(),
1949 rx_channel_estimate_info_vld: (*rx_cntl).rx_channel_estimate_info_vld(),
1950 rx_channel_estimate_len: (*rx_cntl).rx_channel_estimate_len(),
1951 secondary_channel: SecondaryChannel::from_raw_or_default((*rx_cntl).second()),
1952 channel: (*rx_cntl).channel(),
1953 noise_floor: Self::sign_extend_i8_bitfield((*rx_cntl).noise_floor()),
1954 is_group: (*rx_cntl).is_group(),
1955 rxend_state: (*rx_cntl).rxend_state(),
1956 rxmatch3: (*rx_cntl).rxmatch3(),
1957 rxmatch2: (*rx_cntl).rxmatch2(),
1958 rxmatch1: (*rx_cntl).rxmatch1(),
1959 rxmatch0: (*rx_cntl).rxmatch0(),
1960 timestamp: Instant::EPOCH + Duration::from_micros((*rx_cntl).timestamp() as u64),
1961 }
1962 };
1963 rx_control_info
1964 }
1965}
1966
1967#[doc(hidden)]
1968pub struct WifiRxToken {
1972 mode: InterfaceType,
1973}
1974
1975impl WifiRxToken {
1976 pub fn consume_token<R, F>(self, f: F) -> R
1979 where
1980 F: FnOnce(&mut [u8]) -> R,
1981 {
1982 let mut data = self.mode.data_queue_rx().with(|queue| {
1983 unwrap!(
1984 queue.pop_front(),
1985 "unreachable: transmit()/receive() ensures there is a packet to process"
1986 )
1987 });
1988
1989 let buffer = data.as_slice_mut();
1996 dump_packet_info(buffer);
1997
1998 f(buffer)
1999 }
2000}
2001
2002#[doc(hidden)]
2003pub struct WifiTxToken {
2007 mode: InterfaceType,
2008}
2009
2010impl WifiTxToken {
2011 pub fn consume_token<R, F>(self, len: usize, f: F) -> R
2014 where
2015 F: FnOnce(&mut [u8]) -> R,
2016 {
2017 self.mode.increase_in_flight_counter();
2018
2019 let mut buffer: [u8; MTU] = [0u8; MTU];
2020 let buffer = &mut buffer[..len];
2021
2022 let res = f(buffer);
2023
2024 esp_wifi_send_data(self.mode.interface(), buffer);
2025
2026 res
2027 }
2028}
2029
2030pub(crate) fn esp_wifi_send_data(interface: wifi_interface_t, data: &mut [u8]) {
2035 state::locked(|| {
2038 if (interface == wifi_interface_t_WIFI_IF_STA
2040 && !matches!(station_state(), WifiStationState::Connected))
2041 || (interface == wifi_interface_t_WIFI_IF_AP
2042 && !matches!(access_point_state(), WifiAccessPointState::Started))
2043 {
2044 return;
2045 }
2046
2047 trace!("sending... {} bytes", data.len());
2048 dump_packet_info(data);
2049
2050 let len = data.len() as u16;
2051 let ptr = data.as_mut_ptr().cast();
2052
2053 let res = unsafe { esp_wifi_internal_tx(interface, ptr, len) };
2054
2055 if res != include::ESP_OK as i32 {
2056 warn!("esp_wifi_internal_tx returned error: {}", res);
2057 decrement_inflight_counter();
2058 }
2059 })
2060}
2061
2062fn dump_packet_info(_buffer: &mut [u8]) {
2063 #[cfg(dump_packets)]
2064 {
2065 info!("@WIFIFRAME {:?}", _buffer);
2066 }
2067}
2068
2069macro_rules! esp_wifi_result {
2070 ($value:expr) => {{
2071 let result = $value;
2072 if result != $crate::sys::include::ESP_OK as i32 {
2073 Err(WifiError::from_error_code(result))
2076 } else {
2077 Ok::<(), WifiError>(())
2078 }
2079 }};
2080}
2081pub(crate) use esp_wifi_result;
2082
2083static TRANSMIT_WAKER: AtomicWaker = AtomicWaker::new();
2086
2087static AP_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2088static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new();
2089
2090pub(crate) mod embassy_02 {
2093 use embassy_net_driver_02::{
2094 Capabilities,
2095 Driver,
2096 HardwareAddress,
2097 LinkState,
2098 RxToken,
2099 TxToken,
2100 };
2101
2102 use super::*;
2103
2104 impl RxToken for WifiRxToken {
2105 fn consume<R, F>(self, f: F) -> R
2106 where
2107 F: FnOnce(&mut [u8]) -> R,
2108 {
2109 self.consume_token(f)
2110 }
2111 }
2112
2113 impl TxToken for WifiTxToken {
2114 fn consume<R, F>(self, len: usize, f: F) -> R
2115 where
2116 F: FnOnce(&mut [u8]) -> R,
2117 {
2118 self.consume_token(len, f)
2119 }
2120 }
2121
2122 impl Driver for Interface {
2123 type RxToken<'a>
2124 = WifiRxToken
2125 where
2126 Self: 'a;
2127 type TxToken<'a>
2128 = WifiTxToken
2129 where
2130 Self: 'a;
2131
2132 fn receive(
2133 &mut self,
2134 cx: &mut core::task::Context<'_>,
2135 ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
2136 self.mode.register_receive_waker(cx.waker());
2137 self.mode.register_transmit_waker(cx.waker());
2138 self.mode.rx_token()
2139 }
2140
2141 fn transmit(&mut self, cx: &mut core::task::Context<'_>) -> Option<Self::TxToken<'_>> {
2142 self.mode.register_transmit_waker(cx.waker());
2143 self.mode.tx_token()
2144 }
2145
2146 fn link_state(&mut self, cx: &mut core::task::Context<'_>) -> LinkState {
2147 self.mode.register_link_state_waker(cx.waker());
2148 match self.mode.link_state() {
2149 super::LinkState::Down => LinkState::Down,
2150 super::LinkState::Up => LinkState::Up,
2151 }
2152 }
2153
2154 fn capabilities(&self) -> Capabilities {
2155 let mut caps = Capabilities::default();
2156 caps.max_transmission_unit = MTU;
2157 caps.max_burst_size =
2158 if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 {
2159 None
2160 } else {
2161 Some(esp_config_int!(
2162 usize,
2163 "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE"
2164 ))
2165 };
2166 caps
2167 }
2168
2169 fn hardware_address(&self) -> HardwareAddress {
2170 HardwareAddress::Ethernet(self.mac_address())
2171 }
2172 }
2173}
2174
2175pub(crate) mod xarxa {
2176 use xarxa_driver::{
2177 Capabilities,
2178 Driver,
2179 HardwareAddress,
2180 LinkState,
2181 NotSupported,
2182 PacketBuf,
2183 config::PACKET_BUF_SIZE,
2184 };
2185
2186 use super::*;
2187
2188 const XARXA_MTU: usize = if MTU < PACKET_BUF_SIZE {
2192 MTU
2193 } else {
2194 PACKET_BUF_SIZE
2195 };
2196
2197 impl Driver for Interface {
2198 fn capabilities(&self) -> Capabilities {
2199 let mut caps = Capabilities::default();
2200 caps.max_transmission_unit = XARXA_MTU;
2201 caps
2202 }
2203
2204 fn hardware_address(&self) -> HardwareAddress {
2205 HardwareAddress::Ethernet(self.mac_address())
2206 }
2207
2208 fn link_state(&mut self) -> LinkState {
2209 match self.mode.link_state() {
2210 super::LinkState::Down => LinkState::Down,
2211 super::LinkState::Up => LinkState::Up,
2212 }
2213 }
2214
2215 fn register_waker(&mut self, waker: &core::task::Waker) -> Result<(), NotSupported> {
2216 self.mode.register_receive_waker(waker);
2218 self.mode.register_transmit_waker(waker);
2219 self.mode.register_link_state_waker(waker);
2220 Ok(())
2221 }
2222
2223 fn receive(&mut self) -> Option<PacketBuf> {
2224 let mut packet = self.mode.data_queue_rx().with(|queue| queue.pop_front())?;
2230
2231 let data = packet.as_slice_mut();
2232 dump_packet_info(data);
2233
2234 if data.len() > PACKET_BUF_SIZE {
2235 warn!(
2236 "Dropping a {} byte frame, the packet buffer holds {} bytes",
2237 data.len(),
2238 PACKET_BUF_SIZE
2239 );
2240 return None;
2241 }
2242
2243 let mut buffer = PacketBuf::try_new()?;
2246 buffer.set_len(data.len());
2247 buffer.copy_from_slice(data);
2248
2249 Some(buffer)
2250 }
2251
2252 fn can_transmit(&mut self) -> bool {
2253 self.mode.can_send() && self.mode.link_state() == super::LinkState::Up
2254 }
2255
2256 fn transmit(&mut self, mut buffer: PacketBuf) -> Result<(), PacketBuf> {
2257 if !self.can_transmit() {
2258 return Err(buffer);
2259 }
2260
2261 self.mode.increase_in_flight_counter();
2262 esp_wifi_send_data(self.mode.interface(), &mut buffer);
2263
2264 Ok(())
2265 }
2266 }
2267}
2268
2269#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)]
2271#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2272#[instability::unstable]
2273#[non_exhaustive]
2274pub enum PowerSaveMode {
2275 #[default]
2277 None,
2278 Minimum,
2281 Maximum,
2284}
2285
2286pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
2287 esp_wifi_result!(unsafe {
2288 crate::sys::include::esp_wifi_set_ps(match ps {
2289 PowerSaveMode::None => crate::sys::include::wifi_ps_type_t_WIFI_PS_NONE,
2290 PowerSaveMode::Minimum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM,
2291 PowerSaveMode::Maximum => crate::sys::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM,
2292 })
2293 })?;
2294 Ok(())
2295}
2296
2297#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
2301#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2302#[instability::unstable]
2303pub enum OperatingClass {
2304 AllEnvironments,
2307
2308 Outdoors,
2311
2312 Indoors,
2315
2316 NonCountryEntity,
2319
2320 Repr(u8),
2323}
2324
2325impl Default for OperatingClass {
2326 fn default() -> Self {
2327 OperatingClass::Repr(0) }
2329}
2330
2331impl OperatingClass {
2332 fn into_code(self) -> u8 {
2333 match self {
2334 OperatingClass::AllEnvironments => b' ',
2335 OperatingClass::Outdoors => b'O',
2336 OperatingClass::Indoors => b'I',
2337 OperatingClass::NonCountryEntity => b'X',
2338 OperatingClass::Repr(code) => code,
2339 }
2340 }
2341
2342 fn from_code(code: u8) -> Option<Self> {
2343 match code {
2344 b' ' => Some(OperatingClass::AllEnvironments),
2345 b'O' => Some(OperatingClass::Outdoors),
2346 b'I' => Some(OperatingClass::Indoors),
2347 b'X' => Some(OperatingClass::NonCountryEntity),
2348 code => Some(OperatingClass::Repr(code)),
2349 }
2350 }
2351}
2352
2353#[procmacros::doc_replace]
2354#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BuilderLite)]
2373#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2374#[instability::unstable]
2375pub struct CountryInfo {
2376 #[builder_lite(skip)]
2378 country: [u8; 2],
2379
2380 #[builder_lite(unstable)]
2382 operating_class: OperatingClass,
2383}
2384
2385impl From<[u8; 2]> for CountryInfo {
2386 fn from(country: [u8; 2]) -> Self {
2387 Self {
2388 country,
2389 operating_class: OperatingClass::default(),
2390 }
2391 }
2392}
2393
2394impl CountryInfo {
2395 fn into_blob(self) -> wifi_country_t {
2396 wifi_country_t {
2397 cc: [
2398 self.country[0],
2399 self.country[1],
2400 self.operating_class.into_code(),
2401 ],
2402 schan: 1,
2404 nchan: 13,
2405 max_tx_power: 0,
2409 policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL,
2410
2411 #[cfg(wifi_has_5g)]
2412 wifi_5g_channel_mask: 0,
2413 }
2414 }
2415
2416 #[cfg_attr(not(feature = "unstable"), expect(dead_code))]
2417 fn try_from_c(info: &wifi_country_t) -> Option<Self> {
2418 let cc = &info.cc;
2419 let operating_class = OperatingClass::from_code(cc[2])?;
2420
2421 Some(Self {
2422 country: [cc[0], cc[1]],
2423 operating_class,
2424 })
2425 }
2426}
2427
2428#[derive(Clone, BuilderLite, Debug, Hash, PartialEq, Eq)]
2430#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2431#[non_exhaustive]
2432pub struct ControllerConfig {
2433 #[builder_lite(into)]
2435 #[builder_lite(unstable)]
2436 country_info: CountryInfo,
2437 #[builder_lite(unstable)]
2439 rx_queue_size: usize,
2440 #[builder_lite(unstable)]
2442 tx_queue_size: usize,
2443
2444 #[builder_lite(unstable)]
2454 static_rx_buf_num: u8,
2455
2456 #[builder_lite(unstable)]
2473 dynamic_rx_buf_num: u16,
2474
2475 #[builder_lite(unstable)]
2487 static_tx_buf_num: u8,
2488
2489 #[builder_lite(unstable)]
2500 dynamic_tx_buf_num: u16,
2501
2502 #[builder_lite(unstable)]
2504 ampdu_rx_enable: bool,
2505
2506 #[builder_lite(unstable)]
2508 ampdu_tx_enable: bool,
2509
2510 #[builder_lite(unstable)]
2512 amsdu_tx_enable: bool,
2513
2514 #[builder_lite(unstable)]
2525 rx_ba_win: u8,
2526
2527 #[builder_lite(unstable)]
2529 sta_disconnected_pm: bool,
2530
2531 #[builder_lite(unstable)]
2537 espnow_max_encrypt_num: u8,
2538
2539 #[builder_lite(reference)]
2541 initial_config: Config,
2542}
2543
2544impl Default for ControllerConfig {
2545 fn default() -> Self {
2546 Self {
2547 rx_queue_size: 5,
2548 tx_queue_size: 3,
2549
2550 static_rx_buf_num: 10,
2551 dynamic_rx_buf_num: 32,
2552
2553 static_tx_buf_num: 0,
2554 dynamic_tx_buf_num: 32,
2555
2556 ampdu_rx_enable: true,
2557 ampdu_tx_enable: true,
2558 amsdu_tx_enable: false,
2559
2560 rx_ba_win: 6,
2561
2562 sta_disconnected_pm: crate::sys::include::CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE
2563 == 1,
2564
2565 espnow_max_encrypt_num: crate::sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM
2566 as _,
2567
2568 country_info: CountryInfo::from(*b"CN"),
2569
2570 initial_config: Config::Station(StationConfig::default()),
2571 }
2572 }
2573}
2574
2575impl ControllerConfig {
2576 fn validate(&self) {
2577 if self.rx_ba_win as u16 >= self.dynamic_rx_buf_num {
2578 warn!("RX BA window size should be less than the number of dynamic RX buffers.");
2579 }
2580 if self.rx_ba_win as u16 >= 2 * (self.static_rx_buf_num as u16) {
2581 warn!("RX BA window size should be less than twice the number of static RX buffers.");
2582 }
2583 if self.espnow_max_encrypt_num > TOTAL_HW_ENCRYPT_KEYS {
2584 warn!("ESP-NOW max encrypted peers should be at most the number of hardware keys.");
2585 }
2586 }
2587}
2588
2589static WIFI_REFCOUNT: Refcount = Refcount::new();
2590
2591#[derive(Debug)]
2593#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2594pub(crate) struct WifiRefGuard {
2595 _radio_guard: RadioRefGuard,
2596}
2597
2598impl Clone for WifiRefGuard {
2599 fn clone(&self) -> Self {
2600 let _radio_guard = RadioRefGuard::new();
2601 WIFI_REFCOUNT.increment(|| {});
2602 Self { _radio_guard }
2603 }
2604}
2605
2606impl Drop for WifiRefGuard {
2607 fn drop(&mut self) {
2608 WIFI_REFCOUNT.decrement(|| {
2609 state::locked(|| {
2610 set_access_point_state(WifiAccessPointState::Uninitialized);
2611 set_station_state(WifiStationState::Uninitialized);
2612
2613 if let Err(e) = crate::wifi::wifi_deinit() {
2614 warn!("Failed to cleanly deinit wifi: {:?}", e);
2615 }
2616
2617 #[cfg(rng_trng_supported)]
2618 esp_hal::if_unstable_hal! {
2619 esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe {
2620 esp_hal::Internal::conjure()
2621 });
2622 }
2623 })
2624 });
2625 }
2626}
2627
2628#[derive(Debug)]
2634#[cfg_attr(feature = "defmt", derive(defmt::Format))]
2635pub struct WifiController<'d> {
2636 _guard: WifiRefGuard,
2637 _phantom: PhantomData<&'d ()>,
2638}
2639
2640impl<'d> WifiController<'d> {
2641 #[procmacros::doc_replace(
2642 "esp_now" => {
2643 cfg(all(feature = "esp-now", feature = "unstable")) => "An ESP-NOW instance is available through the controller's [`WifiController::esp_now()`] method.",
2644 _ => "",
2645 },
2646 "sniffer" => {
2647 cfg(all(feature = "sniffer", feature = "unstable")) => "A sniffer instance is available through the controller's [`WifiController::sniffer()`] method.",
2648 _ => "",
2649 },
2650 )]
2651 pub fn new(
2674 device: crate::hal::peripherals::WIFI<'d>,
2675 config: ControllerConfig,
2676 ) -> Result<Self, WifiError> {
2677 config.validate();
2678
2679 event::enable_wifi_events(
2680 WifiEvent::StationStart
2681 | WifiEvent::StationStop
2682 | WifiEvent::StationConnected
2683 | WifiEvent::StationDisconnected
2684 | WifiEvent::AccessPointStart
2685 | WifiEvent::AccessPointStop
2686 | WifiEvent::AccessPointStationConnected
2687 | WifiEvent::AccessPointStationDisconnected
2688 | WifiEvent::ScanDone,
2689 );
2690
2691 let radio_guard = RadioRefGuard::new();
2692
2693 let first = WIFI_REFCOUNT.try_increment(|| -> Result<(), WifiError> {
2694 unsafe {
2695 internal::G_CONFIG = wifi_init_config_t {
2696 osi_funcs: (&raw const internal::__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(),
2697
2698 wpa_crypto_funcs: g_wifi_default_wpa_crypto_funcs,
2699 static_rx_buf_num: config.static_rx_buf_num as _,
2700 dynamic_rx_buf_num: config.dynamic_rx_buf_num as _,
2701 tx_buf_type: crate::sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32,
2702 static_tx_buf_num: config.static_tx_buf_num as _,
2703 dynamic_tx_buf_num: config.dynamic_tx_buf_num as _,
2704 rx_mgmt_buf_type: crate::sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF
2705 as i32,
2706 rx_mgmt_buf_num: crate::sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF
2707 as i32,
2708 cache_tx_buf_num: crate::sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32,
2709 csi_enable: cfg!(feature = "csi") as i32,
2710 ampdu_rx_enable: config.ampdu_rx_enable as _,
2711 ampdu_tx_enable: config.ampdu_tx_enable as _,
2712 amsdu_tx_enable: config.amsdu_tx_enable as _,
2713 nvs_enable: 0,
2714 nano_enable: 0,
2715 rx_ba_win: config.rx_ba_win as _,
2716 wifi_task_core_id: Cpu::current() as _,
2717 beacon_max_len: crate::sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32,
2718 mgmt_sbuf_num: crate::sys::include::WIFI_MGMT_SBUF_NUM as i32,
2719 feature_caps: internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS,
2720 sta_disconnected_pm: config.sta_disconnected_pm as _,
2721 espnow_max_encrypt_num: config.espnow_max_encrypt_num as _,
2722
2723 tx_hetb_queue_num: 3,
2724 dump_hesigb_enable: false,
2725
2726 privacy_enhancements: false,
2728 rmac_auto_reset_int: 0,
2729
2730 magic: WIFI_INIT_CONFIG_MAGIC as i32,
2731 };
2732 }
2733
2734 DATA_QUEUE_RX_AP.with(|queue| queue.change_capacity(config.rx_queue_size))?;
2735 DATA_QUEUE_RX_STA.with(|queue| queue.change_capacity(config.rx_queue_size))?;
2736
2737 TX_QUEUE_SIZE.store(config.tx_queue_size, Ordering::Relaxed);
2738
2739 crate::wifi::wifi_init(device)?;
2740
2741 #[cfg(rng_trng_supported)]
2742 esp_hal::if_unstable_hal! {
2743 unsafe {
2744 esp_hal::rng::TrngSource::increase_entropy_source_counter()
2745 };
2746 }
2747
2748 Ok(())
2749 })?;
2750
2751 if !first {
2752 warn!(
2753 "Wi-Fi is already initialized; init-only ControllerConfig settings were ignored: \
2754 static_rx_buf_num, dynamic_rx_buf_num, static_tx_buf_num, dynamic_tx_buf_num, \
2755 rx_queue_size, tx_queue_size, rx_ba_win, espnow_max_encrypt_num, \
2756 ampdu_rx_enable, ampdu_tx_enable, amsdu_tx_enable"
2757 );
2758 }
2759
2760 let mut controller = WifiController {
2761 _guard: WifiRefGuard {
2762 _radio_guard: radio_guard,
2763 },
2764 _phantom: Default::default(),
2765 };
2766
2767 controller.set_country_info(&config.country_info)?;
2768 controller.set_power_saving(PowerSaveMode::default())?;
2770
2771 controller.set_config(&config.initial_config)?;
2772
2773 esp_wifi_result!(unsafe { esp_wifi_set_max_tx_power(20) })?;
2775
2776 Ok(controller)
2777 }
2778}
2779
2780impl WifiController<'_> {
2781 #[cfg(feature = "esp-now")]
2790 #[instability::unstable]
2791 pub fn esp_now(&self) -> crate::esp_now::EspNow {
2792 crate::esp_now::EspNow::new_internal(self._guard.clone())
2793 }
2794
2795 #[cfg(feature = "sniffer")]
2804 #[instability::unstable]
2805 pub fn sniffer(&self) -> Sniffer {
2806 Sniffer::new(self._guard.clone())
2807 }
2808
2809 #[cfg(feature = "csi")]
2811 #[instability::unstable]
2812 pub fn set_csi(
2813 &mut self,
2814 mut csi: csi::CsiConfig,
2815 cb: impl FnMut(crate::wifi::csi::WifiCsiInfo<'_>) + Send,
2816 ) -> Result<(), WifiError> {
2817 csi.apply_config()?;
2818 csi.set_receive_cb(cb)?;
2819 csi.set_csi(true)?;
2820
2821 Ok(())
2822 }
2823
2824 #[procmacros::doc_replace]
2825 #[instability::unstable]
2854 pub fn set_protocols(&mut self, protocols: Protocols) -> Result<(), WifiError> {
2855 let mode = self.mode()?;
2856 if mode.is_station() {
2857 esp_wifi_result!(unsafe {
2858 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_STA, &mut protocols.to_raw())
2859 })?;
2860 }
2861 if mode.is_access_point() {
2862 esp_wifi_result!(unsafe {
2863 esp_wifi_set_protocols(wifi_interface_t_WIFI_IF_AP, &mut protocols.to_raw())
2864 })?;
2865 }
2866
2867 Ok(())
2868 }
2869
2870 fn apply_protocols(iface: wifi_interface_t, protocols: &Protocols) -> Result<(), WifiError> {
2871 esp_wifi_result!(unsafe { esp_wifi_set_protocols(iface, &mut protocols.to_raw()) })?;
2872 Ok(())
2873 }
2874
2875 #[procmacros::doc_replace]
2876 #[instability::unstable]
2889 pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> {
2890 apply_power_saving(ps)
2891 }
2892
2893 fn set_country_info(&mut self, country: &CountryInfo) -> Result<(), WifiError> {
2894 unsafe {
2895 let country = country.into_blob();
2896 esp_wifi_result!(esp_wifi_set_country(&country))?;
2897 }
2898 Ok(())
2899 }
2900
2901 #[procmacros::doc_replace]
2902 pub fn rssi(&self) -> Result<i32, WifiError> {
2932 if self.mode()?.is_station() {
2933 let mut rssi: i32 = 0;
2934 esp_wifi_result!(unsafe { esp_wifi_sta_get_rssi(&mut rssi) })?;
2936 Ok(rssi)
2937 } else {
2938 Err(WifiError::Unsupported)
2939 }
2940 }
2941
2942 #[procmacros::doc_replace]
2943 pub fn ap_info(&self) -> Result<AccessPointInfo, WifiError> {
2973 if self.mode()?.is_station() {
2974 let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
2975 esp_wifi_result!(unsafe { esp_wifi_sta_get_ap_info(record.as_mut_ptr()) })?;
2976
2977 let record = unsafe { MaybeUninit::assume_init(record) };
2978 let ap_info = convert_ap_info(&record);
2979 Ok(ap_info)
2980 } else {
2981 Err(WifiError::Unsupported)
2982 }
2983 }
2984
2985 #[procmacros::doc_replace]
2986 pub fn set_config(&mut self, conf: &Config) -> Result<(), WifiError> {
3017 struct ResetModeOnDrop;
3021 impl ResetModeOnDrop {
3022 fn defuse(self) {
3024 core::mem::forget(self);
3025 }
3026 }
3027 impl Drop for ResetModeOnDrop {
3028 fn drop(&mut self) {
3029 unsafe { esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_NULL) };
3030 unwrap!(WifiController::stop_impl());
3031 }
3032 }
3033
3034 let reset_mode_on_error = ResetModeOnDrop;
3035
3036 conf.validate()?;
3037
3038 let mut previous_mode = 0u32;
3039 esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut previous_mode) })?;
3040
3041 let mode = match conf {
3042 Config::Station(_) => wifi_mode_t_WIFI_MODE_STA,
3043 Config::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP,
3044 Config::AccessPointStation(_, _) => wifi_mode_t_WIFI_MODE_APSTA,
3045 #[cfg(feature = "wifi-eap")]
3046 Config::EapStation(_) => wifi_mode_t_WIFI_MODE_STA,
3047 };
3048
3049 if previous_mode != mode {
3050 Self::stop_impl()?;
3051 }
3052
3053 esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?;
3054
3055 match conf {
3056 Config::Station(config) => {
3057 self.apply_sta_config(config)?;
3058 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
3059 }
3060 Config::AccessPoint(config) => {
3061 self.apply_ap_config(config)?;
3062 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &config.protocols)?;
3063 }
3064 Config::AccessPointStation(sta_config, ap_config) => {
3065 self.apply_ap_config(ap_config)?;
3066 Self::apply_protocols(wifi_interface_t_WIFI_IF_AP, &ap_config.protocols)?;
3067 self.apply_sta_config(sta_config)?;
3068 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &sta_config.protocols)?;
3069 }
3070 #[cfg(feature = "wifi-eap")]
3071 Config::EapStation(config) => {
3072 self.apply_sta_eap_config(config)?;
3073 Self::apply_protocols(wifi_interface_t_WIFI_IF_STA, &config.protocols)?;
3074 }
3075 }
3076
3077 if previous_mode != mode {
3078 set_access_point_state(WifiAccessPointState::Starting);
3079 set_station_state(WifiStationState::Starting);
3080
3081 esp_wifi_result!(unsafe { esp_wifi_start() })?;
3083 }
3084
3085 reset_mode_on_error.defuse();
3086
3087 Ok(())
3088 }
3089
3090 #[cfg_attr(
3095 wifi_has_5g,
3096 doc = r"
3097When the WiFi band mode is set to [`BandMode::_5G`], it operates exclusively on the 5GHz channels.
3098
3099When the WiFi band mode is set to [`BandMode::Auto`], it can operate on both the 2.4GHz and
31005GHz channels.
3101
3102When a WiFi band mode change triggers a band change, if no channel is set for the current
3103band, a default channel will be assigned: channel 1 for 2.4G band and channel 36 for 5G
3104band.
3105"
3106 )]
3107 #[instability::unstable]
3109 pub fn set_band_mode(&mut self, band_mode: BandMode) -> Result<(), WifiError> {
3110 esp_wifi_result!(unsafe { esp_wifi_set_band_mode(band_mode.to_raw()) })
3112 }
3113
3114 #[instability::unstable]
3120 pub fn set_bandwidths(&mut self, bandwidths: Bandwidths) -> Result<(), WifiError> {
3121 let mode = self.mode()?;
3122 if mode.is_station() {
3123 esp_wifi_result!(unsafe {
3124 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bandwidths.to_raw())
3125 })?;
3126 }
3127 if mode.is_access_point() {
3128 esp_wifi_result!(unsafe {
3129 esp_wifi_set_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bandwidths.to_raw())
3130 })?;
3131 }
3132
3133 Ok(())
3134 }
3135
3136 #[instability::unstable]
3143 pub fn bandwidths(&self) -> Result<Bandwidths, WifiError> {
3144 let mut bw = wifi_bandwidths_t {
3145 ghz_2g: 0,
3146 ghz_5g: 0,
3147 };
3148
3149 let mode = self.mode()?;
3150 if mode.is_station() {
3151 esp_wifi_result!(unsafe {
3152 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_STA, &mut bw)
3153 })?;
3154 }
3155 if mode.is_access_point() {
3156 esp_wifi_result!(unsafe {
3157 esp_wifi_get_bandwidths(wifi_interface_t_WIFI_IF_AP, &mut bw)
3158 })?;
3159 }
3160
3161 Ok(Bandwidths {
3162 _2_4: Bandwidth::from_raw(bw.ghz_2g),
3163 #[cfg(wifi_has_5g)]
3164 _5: Bandwidth::from_raw(bw.ghz_5g),
3165 })
3166 }
3167
3168 #[instability::unstable]
3170 pub fn channel(&self) -> Result<(u8, SecondaryChannel), WifiError> {
3171 let mut primary = 0;
3172 let mut secondary = 0;
3173
3174 esp_wifi_result!(unsafe { esp_wifi_get_channel(&mut primary, &mut secondary) })?;
3175
3176 Ok((primary, SecondaryChannel::from_raw(secondary)))
3177 }
3178
3179 #[cfg_attr(
3181 wifi_has_5g,
3182 doc = r"
3183
3184When operating in 5 GHz band, the second channel is automatically determined by the primary
3185channel according to the 802.11 standard. Any manually configured second channel will be
3186ignored."
3187 )]
3188 #[instability::unstable]
3189 pub fn set_channel(
3190 &mut self,
3191 primary: u8,
3192 secondary: SecondaryChannel,
3193 ) -> Result<(), WifiError> {
3194 esp_wifi_result!(unsafe { esp_wifi_set_channel(primary, secondary as u32) })?;
3195
3196 Ok(())
3197 }
3198
3199 #[instability::unstable]
3206 pub fn set_max_tx_power(&mut self, power: i8) -> Result<(), WifiError> {
3207 esp_wifi_result!(unsafe { esp_wifi_set_max_tx_power(power) })
3208 }
3209
3210 fn stop_impl() -> Result<(), WifiError> {
3211 set_access_point_state(WifiAccessPointState::Stopping);
3212 set_station_state(WifiStationState::Stopping);
3213
3214 esp_wifi_result!(unsafe { esp_wifi_stop() })
3215 }
3216
3217 fn connect_impl(&mut self) -> Result<(), WifiError> {
3218 set_station_state(WifiStationState::Connecting);
3219
3220 esp_wifi_result!(unsafe { esp_wifi_connect_internal() })
3222 }
3223
3224 fn disconnect_impl(&mut self) -> Result<(), WifiError> {
3225 set_station_state(WifiStationState::Disconnecting);
3226
3227 esp_wifi_result!(unsafe { esp_wifi_disconnect_internal() })
3229 }
3230
3231 #[procmacros::doc_replace]
3232 #[instability::unstable]
3247 pub fn is_connected(&self) -> bool {
3248 matches!(
3249 crate::wifi::station_state(),
3250 crate::wifi::WifiStationState::Connected
3251 )
3252 }
3253
3254 fn mode(&self) -> Result<WifiMode, WifiError> {
3255 WifiMode::current()
3256 }
3257
3258 #[procmacros::doc_replace]
3259 pub async fn scan_async(
3278 &mut self,
3279 config: &ScanConfig,
3280 ) -> Result<Vec<AccessPointInfo>, WifiError> {
3281 let mut subscriber = EVENT_CHANNEL
3282 .subscriber()
3283 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3284
3285 esp_wifi_result!(wifi_start_scan(false, *config))?;
3286
3287 let guard = FreeApListOnDrop;
3289
3290 loop {
3291 let event = subscriber.next_message_pure().await;
3292 if let EventInfo::ScanDone {
3293 status: _status,
3294 number: _number,
3295 scan_id: _scan_id,
3296 } = event
3297 {
3298 break;
3299 }
3300 }
3301
3302 guard.defuse();
3303
3304 let limit = config.max.unwrap_or(usize::MAX);
3305 Ok(ScanResults::new(self)?.take(limit).collect::<Vec<_>>())
3306 }
3307
3308 #[procmacros::doc_replace]
3309 pub async fn connect_async(&mut self) -> Result<sta::ConnectedInfo, ConnectionError> {
3338 let mut subscriber = EVENT_CHANNEL
3339 .subscriber()
3340 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3341
3342 self.connect_impl()?;
3343
3344 let result = loop {
3345 let event = subscriber.next_message().await;
3346 if let embassy_sync::pubsub::WaitResult::Message(event) = event {
3347 match event {
3348 EventInfo::StationConnected { .. } => {
3349 break event;
3350 }
3351 EventInfo::StationDisconnected { .. } => {
3352 break event;
3353 }
3354 _ => (),
3355 }
3356 }
3357 };
3358
3359 match result {
3360 event::EventInfo::StationConnected {
3361 ssid,
3362 bssid,
3363 channel,
3364 authmode,
3365 aid,
3366 } => Ok(sta::ConnectedInfo {
3367 ssid,
3368 bssid,
3369 channel,
3370 authmode: AuthenticationMethod::from_raw(authmode),
3371 aid,
3372 }),
3373 event::EventInfo::StationDisconnected {
3374 ssid,
3375 bssid,
3376 reason,
3377 rssi,
3378 } => Err(ConnectionError::Failed(sta::DisconnectedInfo {
3379 ssid,
3380 bssid,
3381 reason: DisconnectReason::from_raw(reason),
3382 rssi,
3383 })),
3384 _ => unreachable!(),
3385 }
3386 }
3387
3388 #[procmacros::doc_replace]
3389 pub async fn disconnect_async(&mut self) -> Result<sta::DisconnectedInfo, WifiError> {
3413 if !matches!(
3418 station_state(),
3419 WifiStationState::Connected | WifiStationState::Connecting
3420 ) {
3421 return Err(WifiError::NotConnected);
3422 }
3423
3424 let mut subscriber = EVENT_CHANNEL
3425 .subscriber()
3426 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3427
3428 self.disconnect_impl()?;
3429
3430 loop {
3431 let event = subscriber.next_message_pure().await;
3432
3433 if let event::EventInfo::StationDisconnected {
3434 ssid,
3435 bssid,
3436 reason,
3437 rssi,
3438 } = event
3439 {
3440 break Ok(sta::DisconnectedInfo {
3441 ssid,
3442 bssid,
3443 reason: DisconnectReason::from_raw(reason),
3444 rssi,
3445 });
3446 }
3447 }
3448 }
3449
3450 pub async fn wait_for_disconnect_async(&self) -> Result<sta::DisconnectedInfo, WifiError> {
3452 if !self.is_connected() {
3455 return Err(WifiError::NotConnected);
3456 }
3457
3458 let mut subscriber = EVENT_CHANNEL
3459 .subscriber()
3460 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3461
3462 loop {
3463 let event = subscriber.next_message_pure().await;
3464
3465 if let event::EventInfo::StationDisconnected {
3466 ssid,
3467 bssid,
3468 reason,
3469 rssi,
3470 } = event
3471 {
3472 break Ok(sta::DisconnectedInfo {
3473 ssid,
3474 bssid,
3475 reason: DisconnectReason::from_raw(reason),
3476 rssi,
3477 });
3478 }
3479 }
3480 }
3481
3482 pub async fn wait_for_access_point_connected_event_async(
3484 &self,
3485 ) -> Result<ap::EventInfo, WifiError> {
3486 let mut subscriber = EVENT_CHANNEL
3487 .subscriber()
3488 .expect("Unable to subscribe to events - consider increasing the internal event channel subscriber count");
3489
3490 loop {
3491 let event = subscriber.next_message_pure().await;
3492
3493 match event {
3494 event::EventInfo::AccessPointStationConnected {
3495 mac,
3496 aid,
3497 is_mesh_child,
3498 } => {
3499 break Ok(ap::EventInfo::Connected(ap::ConnectedInfo {
3500 mac,
3501 aid,
3502 is_mesh_child,
3503 }));
3504 }
3505 event::EventInfo::AccessPointStationDisconnected {
3506 mac,
3507 aid,
3508 is_mesh_child,
3509 reason,
3510 } => {
3511 break Ok(ap::EventInfo::Disconnected(ap::DisconnectedInfo {
3512 mac,
3513 aid: aid as u16,
3514 is_mesh_child,
3515 reason: DisconnectReason::from_raw(reason),
3516 }));
3517 }
3518 _ => (),
3519 }
3520 }
3521 }
3522
3523 #[instability::unstable]
3529 pub fn subscribe<'a>(&'a self) -> Result<event::EventSubscriber<'a>, WifiError> {
3530 if let Ok(subscriber) = EVENT_CHANNEL.subscriber() {
3531 return Ok(event::EventSubscriber::new(subscriber));
3532 }
3533
3534 Err(WifiError::Other)
3535 }
3536
3537 fn apply_ap_config(&mut self, config: &AccessPointConfig) -> Result<(), WifiError> {
3538 config.validate()?;
3539
3540 let ap_max_connections = TOTAL_HW_ENCRYPT_KEYS
3543 .saturating_sub(unsafe { internal::G_CONFIG.espnow_max_encrypt_num } as _);
3544
3545 let mut cfg = wifi_config_t {
3546 ap: wifi_ap_config_t {
3547 ssid: [0; 32],
3548 password: [0; 64],
3549 ssid_len: 0,
3550 channel: config.channel,
3551 authmode: config.authentication.auth_method().to_raw(),
3552 ssid_hidden: if config.ssid_hidden { 1 } else { 0 },
3553 max_connection: (config.max_connections as u8).min(ap_max_connections),
3556 beacon_interval: 100,
3557 pairwise_cipher: wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP,
3558 ftm_responder: false,
3559 pmf_cfg: wifi_pmf_config_t {
3560 capable: true,
3561 required: false,
3562 },
3563 sae_pwe_h2e: 0,
3564 csa_count: 3,
3565 dtim_period: config.dtim_period,
3566 _bitfield_align_1: [0; 0],
3567 _bitfield_1: wifi_ap_config_t::new_bitfield_1(0, 0, 0, 0),
3570 bss_max_idle_cfg: include::wifi_bss_max_idle_config_t {
3571 period: 0,
3572 protected_keep_alive: false,
3573 },
3574 gtk_rekey_interval: 0,
3575 },
3576 };
3577
3578 unsafe {
3579 cfg.ap.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3580 cfg.ap.ssid_len = config.ssid.len() as u8;
3581 if let Some(password) = config.authentication.password() {
3582 cfg.ap.password[0..(password.len())].copy_from_slice(password);
3583 }
3584
3585 let mut current: wifi_config_t = core::mem::zeroed();
3588 if esp_wifi_get_config(wifi_interface_t_WIFI_IF_AP, &mut current)
3589 == include::ESP_OK as i32
3590 && current.ap == cfg.ap
3591 {
3592 return Ok(());
3593 }
3594
3595 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_AP, &mut cfg))
3596 }
3597 }
3598
3599 fn apply_sta_config(&mut self, config: &StationConfig) -> Result<(), WifiError> {
3600 config.validate()?;
3601
3602 let mut cfg = wifi_config_t {
3603 sta: wifi_sta_config_t {
3604 ssid: [0; 32],
3605 password: [0; 64],
3606 scan_method: config.scan_method as c_types::c_uint,
3607 bssid_set: config.bssid.is_some(),
3608 bssid: config.bssid.unwrap_or_default(),
3609 channel: config.channel.unwrap_or(0),
3610 listen_interval: config.listen_interval,
3611 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3612 threshold: wifi_scan_threshold_t {
3613 rssi: -99,
3614 authmode: config.authentication.auth_method().to_raw(),
3615 rssi_5g_adjustment: 0,
3616 },
3617 pmf_cfg: wifi_pmf_config_t {
3618 capable: true,
3619 required: false,
3620 },
3621 sae_pwe_h2e: 3,
3622 _bitfield_align_1: [0; 0],
3623 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3624 failure_retry_cnt: config.failure_retry_cnt,
3625 _bitfield_align_2: [0; 0],
3626 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3627 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3629 },
3630 };
3631
3632 unsafe {
3633 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3634 if let Some(password) = config.authentication.password() {
3635 cfg.sta.password[0..(password.len())].copy_from_slice(password);
3636 }
3637
3638 let mut current: wifi_config_t = core::mem::zeroed();
3641 if esp_wifi_get_config(wifi_interface_t_WIFI_IF_STA, &mut current)
3642 == include::ESP_OK as i32
3643 && current.sta == cfg.sta
3644 {
3645 return Ok(());
3646 }
3647
3648 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))
3649 }
3650 }
3651
3652 #[cfg(feature = "wifi-eap")]
3653 fn apply_sta_eap_config(&mut self, config: &EapStationConfig) -> Result<(), WifiError> {
3654 let mut cfg = wifi_config_t {
3655 sta: wifi_sta_config_t {
3656 ssid: [0; 32],
3657 password: [0; 64],
3658 scan_method: config.scan_method as c_types::c_uint,
3659 bssid_set: config.bssid.is_some(),
3660 bssid: config.bssid.unwrap_or_default(),
3661 channel: config.channel.unwrap_or(0),
3662 listen_interval: config.listen_interval,
3663 sort_method: wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL,
3664 threshold: wifi_scan_threshold_t {
3665 rssi: -99,
3666 authmode: config.auth_method.to_raw(),
3667 rssi_5g_adjustment: 0,
3668 },
3669 pmf_cfg: wifi_pmf_config_t {
3670 capable: true,
3671 required: false,
3672 },
3673 sae_pwe_h2e: 3,
3674 _bitfield_align_1: [0; 0],
3675 _bitfield_1: __BindgenBitfieldUnit::new([0; 4]),
3676 failure_retry_cnt: config.failure_retry_cnt,
3677 _bitfield_align_2: [0; 0],
3678 _bitfield_2: __BindgenBitfieldUnit::new([0; 4]),
3679 sae_pk_mode: 0, sae_h2e_identifier: [0; 32],
3681 },
3682 };
3683
3684 unsafe {
3685 cfg.sta.ssid[0..(config.ssid.len())].copy_from_slice(config.ssid.as_bytes());
3686 esp_wifi_result!(esp_wifi_set_config(wifi_interface_t_WIFI_IF_STA, &mut cfg))?;
3687
3688 if let Some(identity) = &config.identity {
3689 esp_wifi_result!(esp_eap_client_set_identity(
3690 identity.as_str().as_ptr(),
3691 identity.len() as i32
3692 ))?;
3693 } else {
3694 esp_eap_client_clear_identity();
3695 }
3696
3697 if let Some(username) = &config.username {
3698 esp_wifi_result!(esp_eap_client_set_username(
3699 username.as_str().as_ptr(),
3700 username.len() as i32
3701 ))?;
3702 } else {
3703 esp_eap_client_clear_username();
3704 }
3705
3706 if let Some(password) = &config.password {
3707 esp_wifi_result!(esp_eap_client_set_password(
3708 password.as_str().as_ptr(),
3709 password.len() as i32
3710 ))?;
3711 } else {
3712 esp_eap_client_clear_password();
3713 }
3714
3715 if let Some(new_password) = &config.new_password {
3716 esp_wifi_result!(esp_eap_client_set_new_password(
3717 new_password.as_str().as_ptr(),
3718 new_password.len() as i32
3719 ))?;
3720 } else {
3721 esp_eap_client_clear_new_password();
3722 }
3723
3724 if let Some(pac_file) = &config.pac_file {
3725 esp_wifi_result!(esp_eap_client_set_pac_file(
3726 pac_file.as_ptr(),
3727 pac_file.len() as i32
3728 ))?;
3729 }
3730
3731 if let Some(phase2_method) = &config.ttls_phase2_method {
3732 esp_wifi_result!(esp_eap_client_set_ttls_phase2_method(
3733 phase2_method.to_raw()
3734 ))?;
3735 }
3736
3737 if let Some(ca_cert) = config.ca_cert {
3738 esp_wifi_result!(esp_eap_client_set_ca_cert(
3739 ca_cert.as_ptr(),
3740 ca_cert.len() as i32
3741 ))?;
3742 } else {
3743 esp_eap_client_clear_ca_cert();
3744 }
3745
3746 if let Some((cert, key, password)) = config.certificate_and_key {
3747 let (pwd, pwd_len) = if let Some(pwd) = password {
3748 (pwd.as_ptr(), pwd.len() as i32)
3749 } else {
3750 (core::ptr::null(), 0)
3751 };
3752
3753 esp_wifi_result!(esp_eap_client_set_certificate_and_key(
3754 cert.as_ptr(),
3755 cert.len() as i32,
3756 key.as_ptr(),
3757 key.len() as i32,
3758 pwd,
3759 pwd_len,
3760 ))?;
3761 } else {
3762 esp_eap_client_clear_certificate_and_key();
3763 }
3764
3765 if let Some(cfg) = &config.eap_fast_config {
3766 let params = esp_eap_fast_config {
3767 fast_provisioning: cfg.fast_provisioning as i32,
3768 fast_max_pac_list_len: cfg.fast_max_pac_list_len as i32,
3769 fast_pac_format_binary: cfg.fast_pac_format_binary,
3770 };
3771 esp_wifi_result!(esp_eap_client_set_fast_params(params))?;
3772 }
3773
3774 esp_wifi_result!(esp_eap_client_set_disable_time_check(!&config.time_check))?;
3775
3776 esp_wifi_result!(esp_wifi_sta_enterprise_enable())?;
3783
3784 Ok(())
3785 }
3786 }
3787}