Extended the Scan API
This commit is contained in:
parent
915423fc63
commit
2dc421a589
@ -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/"
|
||||
|
@ -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],
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user