Extended the Scan API
This commit is contained in:
parent
915423fc63
commit
2dc421a589
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
defmt = ["dep:defmt"]
|
defmt = ["dep:defmt", "heapless/defmt-03"]
|
||||||
log = ["dep:log"]
|
log = ["dep:log"]
|
||||||
|
|
||||||
# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.
|
# 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" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" }
|
||||||
num_enum = { version = "0.5.7", default-features = false }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
|
heapless = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
|
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/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
use core::iter::zip;
|
use core::iter::zip;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
@ -29,6 +30,45 @@ pub struct Control<'a> {
|
|||||||
ioctl_state: &'a IoctlState,
|
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> {
|
impl<'a> Control<'a> {
|
||||||
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
|
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -458,22 +498,56 @@ impl<'a> Control<'a> {
|
|||||||
/// # Note
|
/// # Note
|
||||||
/// Device events are currently implemented using a bounded queue.
|
/// Device events are currently implemented using a bounded queue.
|
||||||
/// To not miss any events, you should make sure to always await the stream.
|
/// 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;
|
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 {
|
let scan_params = ScanParams {
|
||||||
version: 1,
|
version: 1,
|
||||||
action: 1,
|
action: 1,
|
||||||
sync_id: 1,
|
sync_id: 1,
|
||||||
ssid_len: 0,
|
ssid_len: scan_opts.ssid.as_ref().map(|e| e.as_bytes().len() as u32).unwrap_or(0),
|
||||||
ssid: [0; 32],
|
ssid: scan_opts
|
||||||
bssid: [0xff; 6],
|
.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,
|
bss_type: 2,
|
||||||
scan_type: SCANTYPE_PASSIVE,
|
scan_type,
|
||||||
nprobes: !0,
|
nprobes: scan_opts.nprobes.unwrap_or(!0).into(),
|
||||||
active_time: !0,
|
active_time,
|
||||||
passive_time: !0,
|
passive_time,
|
||||||
home_time: !0,
|
home_time: scan_opts.home_time.map(|e| e.as_millis() as u32).unwrap_or(!0),
|
||||||
channel_num: 0,
|
channel_num: 0,
|
||||||
channel_list: [0; 1],
|
channel_list: [0; 1],
|
||||||
};
|
};
|
||||||
|
@ -311,13 +311,13 @@ pub struct Status {
|
|||||||
pub status: u32,
|
pub status: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone)]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
None,
|
None,
|
||||||
BssInfo(BssInfo),
|
BssInfo(BssInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone)]
|
||||||
|
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub header: Status,
|
pub header: Status,
|
||||||
|
@ -427,7 +427,7 @@ where
|
|||||||
let Some(bss_info) = BssInfo::parse(bss_info) else {
|
let Some(bss_info) = BssInfo::parse(bss_info) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
events::Payload::BssInfo(*bss_info)
|
events::Payload::BssInfo(bss_info)
|
||||||
}
|
}
|
||||||
Event::ESCAN_RESULT => events::Payload::None,
|
Event::ESCAN_RESULT => events::Payload::None,
|
||||||
_ => 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, bssinfo) = packet.split_at_mut(ScanResults::SIZE);
|
||||||
let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap());
|
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");
|
warn!("Scan result, incomplete BssInfo");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -466,10 +466,49 @@ impl ScanResults {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wifi Scan Result
|
|
||||||
#[derive(Clone, Copy)]
|
#[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))]
|
#[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]
|
#[non_exhaustive]
|
||||||
pub struct BssInfo {
|
pub struct BssInfo {
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
@ -477,20 +516,85 @@ pub struct BssInfo {
|
|||||||
pub bssid: [u8; 6],
|
pub bssid: [u8; 6],
|
||||||
pub beacon_period: u16,
|
pub beacon_period: u16,
|
||||||
pub capability: u16,
|
pub capability: u16,
|
||||||
pub ssid_len: u8,
|
pub ssid: heapless::String<32>,
|
||||||
pub ssid: [u8; 32],
|
pub rssi: i16,
|
||||||
// there will be more stuff here
|
pub security: BssSecurity,
|
||||||
}
|
}
|
||||||
impl_bytes!(BssInfo);
|
|
||||||
|
|
||||||
impl BssInfo {
|
impl BssInfo {
|
||||||
pub fn parse(packet: &mut [u8]) -> Option<&mut Self> {
|
pub fn parse(packet: &mut [u8]) -> Option<Self> {
|
||||||
if packet.len() < BssInfo::SIZE {
|
if packet.len() < BssInfoInternal::SIZE {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(BssInfo::from_bytes_mut(
|
const DOT11_CAP_PRIVACY: u16 = 0x0010;
|
||||||
packet[..BssInfo::SIZE].as_mut().try_into().unwrap(),
|
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