diff --git a/examples/wps.rs b/examples/wps.rs index e6de665ef8c..60d2a364520 100644 --- a/examples/wps.rs +++ b/examples/wps.rs @@ -5,7 +5,7 @@ use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration}; use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::log::EspLogger; use esp_idf_svc::wifi::{BlockingWifi, EspWifi}; -use esp_idf_svc::wifi::{WpsConfig, WpsEvent, WpsFactoryInfo, WpsType}; +use esp_idf_svc::wifi::{WpsConfig, WpsFactoryInfo, WpsStatus, WpsType}; use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition}; use log::info; @@ -37,11 +37,10 @@ fn main() -> anyhow::Result<()> { info!("Wifi started"); match wifi.start_wps(&WPS_CONFIG)? { - WpsEvent::Active => anyhow::bail!("invalid WPS response"), - WpsEvent::Success(None) => (), // credentials only received for one network, configuration will be set automatically - WpsEvent::Success(Some(credentials)) => { + WpsStatus::SuccessConnected => (), + WpsStatus::SuccessMultipleAccessPoints(credentials) => { log::info!("received multiple credentials, connecting to first one:"); - for i in credentials.as_ref() { + for i in &credentials { log::info!(" - ssid: {}", i.ssid); } let wifi_configuration: Configuration = Configuration::Client(ClientConfiguration { @@ -53,10 +52,10 @@ fn main() -> anyhow::Result<()> { }); wifi.set_configuration(&wifi_configuration)?; } - WpsEvent::Failure => anyhow::bail!("WPS failure"), - WpsEvent::Timeout => anyhow::bail!("WPS timeout"), - WpsEvent::Pin(_) => anyhow::bail!("WPS pin"), - WpsEvent::PbcOverlap => anyhow::bail!("WPS PBC overlap"), + WpsStatus::Failure => anyhow::bail!("WPS failure"), + WpsStatus::Timeout => anyhow::bail!("WPS timeout"), + WpsStatus::Pin(_) => anyhow::bail!("WPS pin"), + WpsStatus::PbcOverlap => anyhow::bail!("WPS PBC overlap"), } match wifi.get_configuration()? { diff --git a/examples/wps_async.rs b/examples/wps_async.rs index 28d07f9b078..c678ba0c65a 100644 --- a/examples/wps_async.rs +++ b/examples/wps_async.rs @@ -6,7 +6,7 @@ use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::log::EspLogger; use esp_idf_svc::timer::EspTaskTimerService; use esp_idf_svc::wifi::{AsyncWifi, EspWifi}; -use esp_idf_svc::wifi::{WpsConfig, WpsEvent, WpsFactoryInfo, WpsType}; +use esp_idf_svc::wifi::{WpsConfig, WpsFactoryInfo, WpsStatus, WpsType}; use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition}; use futures::executor::block_on; @@ -55,11 +55,10 @@ async fn connect_wps(wifi: &mut AsyncWifi>) -> anyhow::Result<( info!("Wifi started"); match wifi.start_wps(&WPS_CONFIG).await? { - WpsEvent::Active => anyhow::bail!("invalid WPS response"), - WpsEvent::Success(None) => (), // credentials only received for one network, configuration will be set automatically - WpsEvent::Success(Some(credentials)) => { + WpsStatus::SuccessConnected => (), + WpsStatus::SuccessMultipleAccessPoints(credentials) => { log::info!("received multiple credentials, connecting to first one:"); - for i in credentials.as_ref() { + for i in &credentials { log::info!(" - ssid: {}", i.ssid); } let wifi_configuration: Configuration = Configuration::Client(ClientConfiguration { @@ -71,10 +70,10 @@ async fn connect_wps(wifi: &mut AsyncWifi>) -> anyhow::Result<( }); wifi.set_configuration(&wifi_configuration)?; } - WpsEvent::Failure => anyhow::bail!("WPS failure"), - WpsEvent::Timeout => anyhow::bail!("WPS timeout"), - WpsEvent::Pin(_) => anyhow::bail!("WPS pin"), - WpsEvent::PbcOverlap => anyhow::bail!("WPS PBC overlap"), + WpsStatus::Failure => anyhow::bail!("WPS failure"), + WpsStatus::Timeout => anyhow::bail!("WPS timeout"), + WpsStatus::Pin(_) => anyhow::bail!("WPS pin"), + WpsStatus::PbcOverlap => anyhow::bail!("WPS PBC overlap"), } match wifi.get_configuration()? { diff --git a/src/private/cstr.rs b/src/private/cstr.rs index b12dffa2782..31806eafdfa 100644 --- a/src/private/cstr.rs +++ b/src/private/cstr.rs @@ -9,24 +9,21 @@ use core::str::Utf8Error; use crate::sys::{EspError, ESP_ERR_INVALID_SIZE}; -#[cfg(feature = "alloc")] pub fn set_str(buf: &mut [u8], s: &str) -> Result<(), EspError> { - assert!(s.len() < buf.len()); - let cs = to_cstring_arg(s)?; - let ss: &[u8] = cs.as_bytes_with_nul(); - buf[..ss.len()].copy_from_slice(ss); + if s.len() >= buf.len() { + return Err(EspError::from_infallible::()); + } + + buf[..s.len()].copy_from_slice(s.as_bytes()); + buf[s.len()] = 0; Ok(()) } pub fn set_cchar_slice(buf: &mut [c_char], s: &str) -> Result<(), EspError> { - if s.len() > buf.len() { - return Err(EspError::from_infallible::()); - } - let s_cchar = unsafe { s.as_bytes().as_ptr() as *const c_char }; - let s_slice = unsafe { core::slice::from_raw_parts(s_cchar, s.len()) }; - buf[..s.len()].copy_from_slice(s_slice); - Ok(()) + let buf_ptr = unsafe { buf.as_mut_ptr() as *mut u8 }; + let buf_slice = unsafe { core::slice::from_raw_parts_mut(buf_ptr, buf.len()) }; + set_str(buf_slice, s) } pub unsafe fn from_cstr_ptr<'a>(ptr: *const c_char) -> &'a str { diff --git a/src/wifi.rs b/src/wifi.rs index 0fa7d81c6ab..78c0d1b4849 100644 --- a/src/wifi.rs +++ b/src/wifi.rs @@ -356,9 +356,9 @@ pub trait NonBlocking { fn start_wps(&mut self, config: &WpsConfig) -> Result<(), EspError>; - fn is_wps_active(&self) -> Result; + fn stop_wps(&mut self) -> Result; - fn take_wps_event(&mut self) -> Result; + fn is_wps_finished(&self) -> Result; } impl NonBlocking for &mut T @@ -396,12 +396,12 @@ where (**self).start_wps(config) } - fn is_wps_active(&self) -> Result { - (**self).is_wps_active() + fn stop_wps(&mut self) -> Result { + (**self).stop_wps() } - fn take_wps_event(&mut self) -> Result { - (**self).take_wps_event() + fn is_wps_finished(&self) -> Result { + (**self).is_wps_finished() } } @@ -423,7 +423,7 @@ pub struct WifiDriver<'d> { struct WifiDriverStatus { pub sta: WifiEvent, pub ap: WifiEvent, - pub wps: Option, + pub wps: Option, } impl<'d> WifiDriver<'d> { @@ -1101,27 +1101,26 @@ impl<'d> WifiDriver<'d> { esp!(unsafe { esp_wifi_wps_enable(&config.0 as *const _) })?; esp!(unsafe { esp_wifi_wps_start(0) })?; - self.status.lock().wps = Some(WpsEvent::Active); + self.status.lock().wps = None; Ok(()) } - pub fn is_wps_active(&self) -> Result { - let status = self.status.lock(); - Ok(matches!(status.wps, Some(WpsEvent::Active))) - } - /// Gets the WPS status as a [`WPS Event`] and disables WPS. - fn take_wps_event(&mut self) -> Result { + fn stop_wps(&mut self) -> Result { let mut status = self.status.lock(); - if status.wps.is_some() && !matches!(status.wps.as_ref().unwrap(), WpsEvent::Active) { + if let Some(status) = status.wps.take() { esp!(unsafe { esp_wifi_wps_disable() })?; - Ok(status.wps.take().unwrap()) + Ok(status) } else { Err(EspError::from_infallible::()) } } + fn is_wps_finished(&self) -> Result { + Ok(self.status.lock().wps.is_some()) + } + fn get_sta_conf(&self) -> Result { let mut wifi_config: wifi_config_t = Default::default(); esp!(unsafe { esp_wifi_get_config(wifi_interface_t_WIFI_IF_STA, &mut wifi_config) })?; @@ -1307,12 +1306,12 @@ impl<'d> NonBlocking for WifiDriver<'d> { WifiDriver::start_wps(self, config) } - fn is_wps_active(&self) -> Result { - WifiDriver::is_wps_active(self) + fn stop_wps(&mut self) -> Result { + WifiDriver::stop_wps(self) } - fn take_wps_event(&mut self) -> Result { - WifiDriver::take_wps_event(self) + fn is_wps_finished(&self) -> Result { + WifiDriver::is_wps_finished(self) } } @@ -1613,12 +1612,12 @@ impl<'d> EspWifi<'d> { self.driver_mut().start_wps(config) } - pub fn is_wps_active(&self) -> Result { - self.driver().is_wps_active() + pub fn stop_wps(&mut self) -> Result { + self.driver_mut().stop_wps() } - pub fn take_wps_event(&mut self) -> Result { - self.driver_mut().take_wps_event() + pub fn is_wps_finished(&self) -> Result { + self.driver().is_wps_finished() } /// As per [`WifiDriver::get_mac()`]. @@ -1707,12 +1706,12 @@ impl<'d> NonBlocking for EspWifi<'d> { EspWifi::start_wps(self, config) } - fn is_wps_active(&self) -> Result { - EspWifi::is_wps_active(self) + fn stop_wps(&mut self) -> Result { + EspWifi::stop_wps(self) } - fn take_wps_event(&mut self) -> Result { - EspWifi::take_wps_event(self) + fn is_wps_finished(&self) -> Result { + EspWifi::is_wps_finished(self) } } @@ -1780,8 +1779,6 @@ pub struct WpsCredentials { pub passphrase: heapless::String<64>, } -const MAX_WPS_AP_CRED_USIZE: usize = MAX_WPS_AP_CRED as usize; - #[derive(Clone, Debug, Eq, PartialEq)] pub enum WifiEvent { Ready, @@ -1795,10 +1792,10 @@ pub enum WifiEvent { StaAuthmodeChanged, StaBssRssiLow, StaBeaconTimeout, - StaWpsSuccess(Option>>), + StaWpsSuccess(alloc::vec::Vec), StaWpsFailed, StaWpsTimeout, - StaWpsPin(heapless::String<8>), + StaWpsPin(Option), StaWpsPbcOverlap, ApStarted, @@ -1843,27 +1840,32 @@ impl EspTypedEventDeserializer for WifiEvent { } else if event_id == wifi_event_t_WIFI_EVENT_STA_WPS_ER_SUCCESS { let payload = unsafe { (data.payload as *const wifi_event_sta_wps_er_success_t).as_ref() }; - let credentials = payload.map(|payload| { - let mut vec = heapless::Vec::new(); + let credentials = if let Some(payload) = payload { + let mut credentials = alloc::vec::Vec::with_capacity(payload.ap_cred_cnt.into()); for i in 0..payload.ap_cred_cnt { let creds = &payload.ap_cred[i as usize]; - let Ok(ssid) = core::str::from_utf8(&creds.ssid) else { + + let Ok(ssid) = from_cstr_fallible(&creds.ssid) else { log::warn!("Received a non-UTF-8 SSID via WPS"); continue; }; - let Ok(passphrase) = core::str::from_utf8(&creds.passphrase) else { + + let Ok(passphrase) = from_cstr_fallible(&creds.passphrase) else { log::warn!("Received a non-UTF-8 passphrase via WPS"); continue; }; + let creds = WpsCredentials { ssid: ssid.into(), passphrase: passphrase.into(), }; - vec.push(creds).unwrap(); + credentials.push(creds); } - vec - }); - WifiEvent::StaWpsSuccess(credentials.map(Arc::new)) + credentials + } else { + Vec::new() + }; + WifiEvent::StaWpsSuccess(credentials) } else if event_id == wifi_event_t_WIFI_EVENT_STA_WPS_ER_FAILED { WifiEvent::StaWpsFailed } else if event_id == wifi_event_t_WIFI_EVENT_STA_WPS_ER_TIMEOUT { @@ -1872,8 +1874,8 @@ impl EspTypedEventDeserializer for WifiEvent { let payload = unsafe { (data.payload as *const wifi_event_sta_wps_er_pin_t).as_ref() }; let pin = payload .and_then(|x| core::str::from_utf8(&x.pin_code).ok()) - .unwrap_or("bad pin"); - WifiEvent::StaWpsPin(heapless::String::from(pin)) + .and_then(|x| x.parse().ok()); + WifiEvent::StaWpsPin(pin) } else if event_id == wifi_event_t_WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP { WifiEvent::StaWpsPbcOverlap } else if event_id == wifi_event_t_WIFI_EVENT_AP_START { @@ -2044,17 +2046,20 @@ where } /// Start WPS and perform a blocking wait until it connects, fails or times - /// out. A [`WpsEvent`] is returned that contains the success status of the + /// out. A [`WpsStatus`] is returned that contains the success status of the /// WPS connection. When the credentials of only one access point are /// received, the WiFi driver configuration will automatically be set to /// that access point. If multiple credentials were received, a - /// `WpsEvent::Success` will be returned with a vector containing those + /// `WpsStatus::Success` will be returned with a vector containing those /// credentials. The caller must then handle those credentials and set the /// configuration manually. - pub fn start_wps(&mut self, config: &WpsConfig) -> Result { + pub fn start_wps(&mut self, config: &WpsConfig) -> Result { self.wifi.start_wps(config)?; - self.wifi_wait_while(|| self.wifi.is_wps_active(), Some(WPS_TIMEOUT))?; - Ok(self.wifi.take_wps_event().unwrap_or(WpsEvent::Timeout)) + self.wifi_wait_while( + || self.wifi.is_wps_finished().map(|x| !x), + Some(WPS_TIMEOUT), + )?; + Ok(self.wifi.stop_wps().unwrap_or(WpsStatus::Timeout)) } } @@ -2298,18 +2303,21 @@ where } /// Start WPS and perform a wait asynchronously until it connects, fails or - /// times out. A [`WpsEvent`] is returned that contains the success status + /// times out. A [`WpsStatus`] is returned that contains the success status /// of the WPS connection. When the credentials of only one access point are /// received, the WiFi driver configuration will automatically be set to /// that access point. If multiple credentials were received, a - /// `WpsEvent::Success` will be returned with a vector containing those + /// `WpsStatus::Success` will be returned with a vector containing those /// credentials. The caller must then handle those credentials and set the /// configuration manually. - pub async fn start_wps(&mut self, config: &WpsConfig<'_>) -> Result { + pub async fn start_wps(&mut self, config: &WpsConfig<'_>) -> Result { self.wifi.start_wps(config)?; - self.wifi_wait(|| self.wifi.is_wps_active(), Some(WPS_TIMEOUT)) - .await?; - Ok(self.wifi.take_wps_event().unwrap_or(WpsEvent::Timeout)) + self.wifi_wait( + || self.wifi.is_wps_finished().map(|x| !x), + Some(WPS_TIMEOUT), + ) + .await?; + Ok(self.wifi.stop_wps().unwrap_or(WpsStatus::Timeout)) } } @@ -2471,7 +2479,7 @@ impl TryFrom<&WpsFactoryInfo<'_>> for Newtype { pub enum WpsType { Pbc, - Pin(heapless::String<8>), + Pin(u32), } impl WpsType { @@ -2488,10 +2496,13 @@ impl WpsType { WpsType::Pbc => [0; 9], WpsType::Pin(pin) => { let mut result = [0; 9]; - let bytes = pin.as_bytes(); - let bytes_cchar = bytes.as_ptr() as *const ffi::c_char; - let slice = unsafe { core::slice::from_raw_parts(bytes_cchar, pin.len()) }; - result[..pin.len()].copy_from_slice(slice); + let mut pin = *pin; + let mut rem: u32; + for i in 0..8 { + rem = pin % 10; + pin /= 10; + result[7 - i] = (rem as ffi::c_char) + 48; + } result } } @@ -2499,25 +2510,31 @@ impl WpsType { } #[derive(Debug)] -pub enum WpsEvent { - Active, - Success(Option>>), +pub enum WpsStatus { + SuccessConnected, + SuccessMultipleAccessPoints(alloc::vec::Vec), Failure, Timeout, - Pin(heapless::String<8>), + Pin(Option), PbcOverlap, } -impl TryFrom<&WifiEvent> for WpsEvent { +impl TryFrom<&WifiEvent> for WpsStatus { type Error = EspError; fn try_from(event: &WifiEvent) -> Result { match event { - WifiEvent::StaWpsSuccess(credentials) => Ok(WpsEvent::Success(credentials.clone())), - WifiEvent::StaWpsFailed => Ok(WpsEvent::Failure), - WifiEvent::StaWpsTimeout => Ok(WpsEvent::Timeout), - WifiEvent::StaWpsPin(pin) => Ok(WpsEvent::Pin(pin.clone())), - WifiEvent::StaWpsPbcOverlap => Ok(WpsEvent::PbcOverlap), + WifiEvent::StaWpsSuccess(credentials) => { + if credentials.is_empty() { + Ok(WpsStatus::SuccessConnected) + } else { + Ok(WpsStatus::SuccessMultipleAccessPoints(credentials.clone())) + } + } + WifiEvent::StaWpsFailed => Ok(WpsStatus::Failure), + WifiEvent::StaWpsTimeout => Ok(WpsStatus::Timeout), + WifiEvent::StaWpsPin(pin) => Ok(WpsStatus::Pin(*pin)), + WifiEvent::StaWpsPbcOverlap => Ok(WpsStatus::PbcOverlap), _ => Err(EspError::from_infallible::()), } }