Extended the Scan API

This commit is contained in:
umgefahren 2023-12-13 18:08:23 +01:00
parent 915423fc63
commit 2dc421a589
5 changed files with 205 additions and 25 deletions

View File

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"
[features]
defmt = ["dep:defmt"]
defmt = ["dep:defmt", "heapless/defmt-03"]
log = ["dep:log"]
# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.
@ -26,6 +26,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" }
num_enum = { version = "0.5.7", default-features = false }
heapless = "0.8.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"

View File

@ -1,5 +1,6 @@
use core::cmp::{max, min};
use core::iter::zip;
use core::time::Duration;
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
@ -29,6 +30,45 @@ pub struct Control<'a> {
ioctl_state: &'a IoctlState,
}
#[derive(Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanType {
Active {
/// Period of time to wait on each channel when active scanning.
dwell_time: Option<Duration>,
},
Passive {
/// Period of time to wait on each channel when passive scanning.
dwell_time: Option<Duration>,
},
}
#[derive(Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScanOptions {
pub ssid: Option<heapless::String<32>>,
/// If set to `None`, all APs will be returned. If set to `Some`, only APs
/// with the specified BSSID will be returned.
pub bssid: Option<[u8; 6]>,
/// Number of probes to send on each channel.
pub nprobes: Option<u16>,
/// Time to spend waiting on the home channel.
pub home_time: Option<Duration>,
pub scan_type: ScanType,
}
impl Default for ScanOptions {
fn default() -> Self {
Self {
ssid: None,
bssid: None,
nprobes: None,
home_time: None,
scan_type: ScanType::Passive { dwell_time: None },
}
}
}
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
Self {
@ -458,22 +498,56 @@ impl<'a> Control<'a> {
/// # Note
/// Device events are currently implemented using a bounded queue.
/// To not miss any events, you should make sure to always await the stream.
pub async fn scan(&mut self) -> Scanner<'_> {
pub async fn scan(&mut self, scan_opts: ScanOptions) -> Scanner<'_> {
const SCANTYPE_ACTIVE: u8 = 0;
const SCANTYPE_PASSIVE: u8 = 1;
let mut active_time = !0;
let mut passive_time = !0;
let scan_type = match scan_opts.scan_type {
ScanType::Active { dwell_time: None } => SCANTYPE_ACTIVE,
ScanType::Active {
dwell_time: Some(dwell_time),
} => {
active_time = dwell_time.as_millis() as u32;
if active_time == !0 {
active_time = !0 - 1;
}
SCANTYPE_ACTIVE
}
ScanType::Passive { dwell_time: None } => SCANTYPE_PASSIVE,
ScanType::Passive {
dwell_time: Some(dwell_time),
} => {
passive_time = dwell_time.as_millis() as u32;
if passive_time == !0 {
passive_time = !0 - 1;
}
SCANTYPE_PASSIVE
}
};
let scan_params = ScanParams {
version: 1,
action: 1,
sync_id: 1,
ssid_len: 0,
ssid: [0; 32],
bssid: [0xff; 6],
ssid_len: scan_opts.ssid.as_ref().map(|e| e.as_bytes().len() as u32).unwrap_or(0),
ssid: scan_opts
.ssid
.map(|e| {
let mut ssid = [0; 32];
ssid[..e.as_bytes().len()].copy_from_slice(e.as_bytes());
ssid
})
.unwrap_or([0; 32]),
bssid: scan_opts.bssid.unwrap_or([0xff; 6]),
bss_type: 2,
scan_type: SCANTYPE_PASSIVE,
nprobes: !0,
active_time: !0,
passive_time: !0,
home_time: !0,
scan_type,
nprobes: scan_opts.nprobes.unwrap_or(!0).into(),
active_time,
passive_time,
home_time: scan_opts.home_time.map(|e| e.as_millis() as u32).unwrap_or(!0),
channel_num: 0,
channel_list: [0; 1],
};

View File

@ -311,13 +311,13 @@ pub struct Status {
pub status: u32,
}
#[derive(Clone, Copy)]
#[derive(Clone)]
pub enum Payload {
None,
BssInfo(BssInfo),
}
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Message {
pub header: Status,

View File

@ -427,7 +427,7 @@ where
let Some(bss_info) = BssInfo::parse(bss_info) else {
return;
};
events::Payload::BssInfo(*bss_info)
events::Payload::BssInfo(bss_info)
}
Event::ESCAN_RESULT => events::Payload::None,
_ => events::Payload::None,

View File

@ -457,7 +457,7 @@ impl ScanResults {
let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE);
let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap());
if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE {
if scan_results.bss_count > 0 && bssinfo.len() < BssInfoInternal::SIZE {
warn!("Scan result, incomplete BssInfo");
return None;
}
@ -466,10 +466,49 @@ impl ScanResults {
}
}
/// Wifi Scan Result
#[derive(Clone, Copy)]
// #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BssSecurity {
Open,
Wpa2,
Wpa,
WepPsk,
None,
}
#[derive(Clone, Copy)]
#[repr(C, packed(2))]
struct BssInfoInternal {
version: u32,
length: u32,
bssid: [u8; 6],
beacon_period: u16,
capability: u16,
ssid_len: u8,
ssid: [u8; 32],
rateset_count: u32,
rateset_rates: [u8; 16],
chanspec: u16,
atim_window: u16,
dtim_period: u8,
rssi: i16,
phy_noise: i8,
n_cap: u8,
nbss_cap: u32,
ctl_ch: u8,
reserved32: [u32; 1],
flags: u8,
reserved: [u8; 3],
basic_mcs: [u8; 16],
ie_offset: u16,
ie_length: u32,
snr: i16,
}
impl_bytes!(BssInfoInternal);
/// Wifi Scan Result
#[derive(Clone)]
// #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct BssInfo {
pub version: u32,
@ -477,20 +516,85 @@ pub struct BssInfo {
pub bssid: [u8; 6],
pub beacon_period: u16,
pub capability: u16,
pub ssid_len: u8,
pub ssid: [u8; 32],
// there will be more stuff here
pub ssid: heapless::String<32>,
pub rssi: i16,
pub security: BssSecurity,
}
impl_bytes!(BssInfo);
impl BssInfo {
pub fn parse(packet: &mut [u8]) -> Option<&mut Self> {
if packet.len() < BssInfo::SIZE {
pub fn parse(packet: &mut [u8]) -> Option<Self> {
if packet.len() < BssInfoInternal::SIZE {
return None;
}
Some(BssInfo::from_bytes_mut(
packet[..BssInfo::SIZE].as_mut().try_into().unwrap(),
))
const DOT11_CAP_PRIVACY: u16 = 0x0010;
const DOT11_IE_ID_RSN: u8 = 48;
const DOT11_IE_ID_VENDOR_SPECIFIC: u8 = 221;
const WPA_OUI_TYPE1: &[u8] = b"\x00\x50\xF2\x01";
let bss_info = *BssInfoInternal::from_bytes_mut(packet[..BssInfoInternal::SIZE].as_mut().try_into().unwrap());
let mut ie_ptr = bss_info.ie_offset as usize;
let ie_top = ie_ptr + bss_info.ie_length as usize;
let mut ie_rsn = None;
let mut ie_wpa = None;
while ie_ptr < ie_top {
let ie_type = packet[ie_ptr];
let ie_len = packet[ie_ptr + 1] as usize;
if ie_ptr + 2 + ie_len <= ie_top {
match ie_type {
DOT11_IE_ID_RSN => {
ie_rsn = Some(ie_ptr);
}
DOT11_IE_ID_VENDOR_SPECIFIC => {
if &packet[ie_ptr + 2..ie_ptr + 6] == WPA_OUI_TYPE1 {
ie_wpa = Some(ie_ptr);
}
}
_ => {}
}
}
ie_ptr += 2 + ie_len;
}
let mut security_field = 0;
if ie_rsn.is_some() {
security_field |= 4;
}
if ie_wpa.is_some() {
security_field |= 2;
}
if bss_info.capability & DOT11_CAP_PRIVACY > 0 {
security_field |= 1;
}
let security = match security_field {
4 => BssSecurity::Wpa2,
2 => BssSecurity::Wpa,
1 => BssSecurity::WepPsk,
0 => BssSecurity::Open,
_ => BssSecurity::None,
};
let ssid = heapless::String::from_utf8(
heapless::Vec::from_slice(&bss_info.ssid[..bss_info.ssid_len as usize]).unwrap(),
)
.ok()?;
let BssInfoInternal {
version,
length,
bssid,
beacon_period,
capability,
rssi,
..
} = bss_info;
Some(Self {
version,
length,
bssid,
beacon_period,
capability,
ssid,
rssi,
security,
})
}
}