Skip to content

Commit e8c48bd

Browse files
authored
Clean up processing scan results (#4409)
1 parent 7e2e470 commit e8c48bd

File tree

2 files changed

+123
-78
lines changed

2 files changed

+123
-78
lines changed

esp-radio/src/wifi/mod.rs

Lines changed: 16 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,10 @@
55
pub mod event;
66
mod internal;
77
pub(crate) mod os_adapter;
8+
mod scan;
89
pub(crate) mod state;
9-
use alloc::{collections::vec_deque::VecDeque, string::String};
10-
use core::{
11-
fmt::Debug,
12-
marker::PhantomData,
13-
mem::MaybeUninit,
14-
ptr::addr_of,
15-
task::Poll,
16-
time::Duration,
17-
};
10+
use alloc::{collections::vec_deque::VecDeque, string::String, vec::Vec};
11+
use core::{fmt::Debug, marker::PhantomData, ptr::addr_of, task::Poll, time::Duration};
1812

1913
use enumset::{EnumSet, EnumSetType};
2014
use esp_config::esp_config_int;
@@ -84,7 +78,10 @@ use crate::{
8478
wifi_scan_channel_bitmap_t,
8579
},
8680
},
87-
wifi::private::PacketBuffer,
81+
wifi::{
82+
private::PacketBuffer,
83+
scan::{FreeApListOnDrop, ScanResults},
84+
},
8885
};
8986

9087
const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU");
@@ -1779,7 +1776,7 @@ pub(crate) fn wifi_start_scan(
17791776
};
17801777

17811778
let mut ssid_buf = ssid.map(|m| {
1782-
let mut buf = alloc::vec::Vec::from_iter(m.bytes());
1779+
let mut buf = Vec::from_iter(m.bytes());
17831780
buf.push(b'\0');
17841781
buf
17851782
});
@@ -1978,33 +1975,6 @@ impl WifiDevice<'_> {
19781975
}
19791976
}
19801977

1981-
fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
1982-
let str_len = record
1983-
.ssid
1984-
.iter()
1985-
.position(|&c| c == 0)
1986-
.unwrap_or(record.ssid.len());
1987-
let ssid_ref = unsafe { core::str::from_utf8_unchecked(&record.ssid[..str_len]) };
1988-
1989-
let mut ssid = String::new();
1990-
ssid.push_str(ssid_ref);
1991-
1992-
AccessPointInfo {
1993-
ssid,
1994-
bssid: record.bssid,
1995-
channel: record.primary,
1996-
secondary_channel: match record.second {
1997-
include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
1998-
include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
1999-
include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
2000-
_ => panic!(),
2001-
},
2002-
signal_strength: record.rssi,
2003-
auth_method: Some(AuthMethod::from_raw(record.authmode)),
2004-
country: Country::try_from_c(&record.country),
2005-
}
2006-
}
2007-
20081978
/// The radio metadata header of the received packet, which is the common header
20091979
/// at the beginning of all RX callback buffers in promiscuous mode.
20101980
#[cfg(not(esp32c6))]
@@ -2564,21 +2534,6 @@ pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> {
25642534
Ok(())
25652535
}
25662536

2567-
struct FreeApListOnDrop;
2568-
impl FreeApListOnDrop {
2569-
pub fn defuse(self) {
2570-
core::mem::forget(self);
2571-
}
2572-
}
2573-
2574-
impl Drop for FreeApListOnDrop {
2575-
fn drop(&mut self) {
2576-
unsafe {
2577-
include::esp_wifi_clear_ap_list();
2578-
}
2579-
}
2580-
}
2581-
25822537
/// Represents the Wi-Fi controller and its associated interfaces.
25832538
#[non_exhaustive]
25842539
pub struct Interfaces<'d> {
@@ -3049,33 +3004,17 @@ impl WifiController<'_> {
30493004
pub fn scan_with_config(
30503005
&mut self,
30513006
config: ScanConfig<'_>,
3052-
) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3007+
) -> Result<Vec<AccessPointInfo>, WifiError> {
30533008
esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?;
30543009
self.scan_results(config.max.unwrap_or(usize::MAX))
30553010
}
30563011

3057-
fn scan_results(&mut self, max: usize) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3058-
let mut scanned = alloc::vec::Vec::<AccessPointInfo>::new();
3059-
let mut bss_total: u16 = max as u16;
3060-
3061-
// Prevents memory leak on error
3062-
let guard = FreeApListOnDrop;
3063-
3064-
unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
3065-
3066-
guard.defuse();
3067-
3068-
let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
3069-
for _ in 0..usize::min(bss_total as usize, max) {
3070-
unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record.as_mut_ptr()))? };
3071-
let record = unsafe { MaybeUninit::assume_init_ref(&record) };
3072-
let ap_info = convert_ap_info(record);
3073-
scanned.push(ap_info);
3074-
}
3075-
3076-
unsafe { esp_wifi_result!(include::esp_wifi_clear_ap_list())? };
3012+
fn scan_results_iter(&mut self) -> Result<ScanResults<'_>, WifiError> {
3013+
ScanResults::new(self)
3014+
}
30773015

3078-
Ok(scanned)
3016+
fn scan_results(&mut self, max: usize) -> Result<Vec<AccessPointInfo>, WifiError> {
3017+
Ok(self.scan_results_iter()?.take(max).collect::<Vec<_>>())
30793018
}
30803019

30813020
/// Starts the Wi-Fi controller.
@@ -3282,14 +3221,13 @@ impl WifiController<'_> {
32823221
pub async fn scan_with_config_async(
32833222
&mut self,
32843223
config: ScanConfig<'_>,
3285-
) -> Result<alloc::vec::Vec<AccessPointInfo>, WifiError> {
3224+
) -> Result<Vec<AccessPointInfo>, WifiError> {
32863225
Self::clear_events(WifiEvent::ScanDone);
32873226
esp_wifi_result!(wifi_start_scan(false, config))?;
32883227

3289-
// Prevents memory leak if `scan_n`'s future is dropped.
3228+
// Prevents memory leak if `scan_with_config_async`'s future is dropped.
32903229
let guard = FreeApListOnDrop;
32913230
WifiEventFuture::new(WifiEvent::ScanDone).await;
3292-
32933231
guard.defuse();
32943232

32953233
let result = self.scan_results(config.max.unwrap_or(usize::MAX))?;

esp-radio/src/wifi/scan.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use core::{marker::PhantomData, mem::MaybeUninit};
2+
3+
use crate::{
4+
esp_wifi_result,
5+
sys::include,
6+
wifi::{
7+
AccessPointInfo,
8+
AuthMethod,
9+
AuthMethodExt as _,
10+
Country,
11+
SecondaryChannel,
12+
WifiController,
13+
WifiError,
14+
},
15+
};
16+
17+
pub struct ScanResults<'d> {
18+
/// Number of APs to return
19+
remaining: usize,
20+
21+
/// Ensures the result list is free'd when this struct is dropped.
22+
_drop_guard: FreeApListOnDrop,
23+
24+
/// Hold a lifetime to ensure the scan list is freed before a new scan is started.
25+
_marker: PhantomData<&'d mut ()>,
26+
}
27+
28+
impl<'d> ScanResults<'d> {
29+
pub fn new(_controller: &'d mut WifiController<'_>) -> Result<Self, WifiError> {
30+
// Construct Self first. This ensures we'll free the result list even if `get_ap_num`
31+
// returns an error.
32+
let mut this = Self {
33+
remaining: 0,
34+
_drop_guard: FreeApListOnDrop,
35+
_marker: PhantomData,
36+
};
37+
38+
let mut bss_total = 0;
39+
unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? };
40+
41+
this.remaining = bss_total as usize;
42+
43+
Ok(this)
44+
}
45+
}
46+
47+
impl Iterator for ScanResults<'_> {
48+
type Item = AccessPointInfo;
49+
50+
fn next(&mut self) -> Option<Self::Item> {
51+
if self.remaining == 0 {
52+
return None;
53+
}
54+
55+
self.remaining -= 1;
56+
57+
let mut record: MaybeUninit<include::wifi_ap_record_t> = MaybeUninit::uninit();
58+
59+
// We could detect ESP_FAIL to see if we've exhausted the list, but we know the number of
60+
// results. Reading the number of results also ensures we're in the correct state, so
61+
// unwrapping here should never fail.
62+
unwrap!(unsafe {
63+
esp_wifi_result!(include::esp_wifi_scan_get_ap_record(record.as_mut_ptr()))
64+
});
65+
66+
Some(convert_ap_info(unsafe { record.assume_init_ref() }))
67+
}
68+
}
69+
70+
fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
71+
let str_len = record
72+
.ssid
73+
.iter()
74+
.position(|&c| c == 0)
75+
.unwrap_or(record.ssid.len());
76+
let ssid = alloc::string::String::from_utf8_lossy(&record.ssid[..str_len]).into_owned();
77+
78+
AccessPointInfo {
79+
ssid,
80+
bssid: record.bssid,
81+
channel: record.primary,
82+
secondary_channel: match record.second {
83+
include::wifi_second_chan_t_WIFI_SECOND_CHAN_NONE => SecondaryChannel::None,
84+
include::wifi_second_chan_t_WIFI_SECOND_CHAN_ABOVE => SecondaryChannel::Above,
85+
include::wifi_second_chan_t_WIFI_SECOND_CHAN_BELOW => SecondaryChannel::Below,
86+
_ => panic!(),
87+
},
88+
signal_strength: record.rssi,
89+
auth_method: Some(AuthMethod::from_raw(record.authmode)),
90+
country: Country::try_from_c(&record.country),
91+
}
92+
}
93+
94+
pub struct FreeApListOnDrop;
95+
impl FreeApListOnDrop {
96+
pub fn defuse(self) {
97+
core::mem::forget(self);
98+
}
99+
}
100+
101+
impl Drop for FreeApListOnDrop {
102+
fn drop(&mut self) {
103+
unsafe {
104+
include::esp_wifi_clear_ap_list();
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)