use core::{marker::PhantomData, mem::MaybeUninit};
use esp_hal::time::Duration;
use procmacros::BuilderLite;
use crate::{
sys::include,
wifi::{
Ssid,
WifiController,
WifiError,
ap::{AccessPointInfo, convert_ap_info},
esp_wifi_result,
},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ScanTypeConfig {
Active {
min: Duration,
max: Duration,
},
Passive(Duration),
}
impl Default for ScanTypeConfig {
fn default() -> Self {
Self::Active {
min: Duration::from_millis(10),
max: Duration::from_millis(20),
}
}
}
impl ScanTypeConfig {
pub(crate) fn validate(&self) {
if matches!(self, Self::Passive(dur) if *dur > Duration::from_millis(1500)) {
warn!(
"Passive scan duration longer than 1500ms may cause a station to disconnect from the access point"
);
}
}
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ScanConfig {
#[builder_lite(skip_setter)]
pub(crate) ssid: Option<Ssid>,
pub(crate) bssid: Option<[u8; 6]>,
pub(crate) channel: Option<u8>,
pub(crate) show_hidden: bool,
pub(crate) scan_type: ScanTypeConfig,
pub(crate) max: Option<usize>,
}
impl ScanConfig {
pub fn with_ssid(mut self, ssid: impl Into<Ssid>) -> Self {
self.ssid = Some(ssid.into());
self
}
pub fn with_ssid_none(mut self) -> Self {
self.ssid = None;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ScanResults<'d> {
remaining: usize,
_drop_guard: FreeApListOnDrop,
_marker: PhantomData<&'d mut ()>,
}
impl<'d> ScanResults<'d> {
pub fn new(_controller: &'d mut WifiController<'_>) -> Result<Self, WifiError> {
let mut this = Self {
remaining: 0,
_drop_guard: FreeApListOnDrop,
_marker: PhantomData,
};
let mut bss_total = 0;
unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
this.remaining = bss_total as usize;
Ok(this)
}
}
impl Iterator for ScanResults<'_> {
type Item = AccessPointInfo;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
unwrap!(unsafe {
esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record.as_mut_ptr()))
});
Some(convert_ap_info(unsafe { record.assume_init_ref() }))
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(super) struct FreeApListOnDrop;
impl FreeApListOnDrop {
pub fn defuse(self) {
core::mem::forget(self);
}
}
impl Drop for FreeApListOnDrop {
fn drop(&mut self) {
unsafe {
include::esp_wifi_clear_ap_list();
}
}
}