diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-04-30 15:05:16 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-04-30 15:05:16 +0000 |
| commit | 5659269c8fb2f7d03d4a903e4ad48c8268668f0a (patch) | |
| tree | e35e59f37d50791029c2f88b3014b409ea802d39 | |
| parent | c19de2984751ba6fa2972ee66cfa2a6310d5f0c1 (diff) | |
| parent | 76b967a966677e570cc0a2942ed3ccd29b2d1017 (diff) | |
Merge pull request #70 from kbleeke/wifi-scanning-ioctl
Wifi scanning ioctl
| -rw-r--r-- | examples/rpi-pico-w/src/main.rs | 1 | ||||
| -rw-r--r-- | src/control.rs | 75 | ||||
| -rw-r--r-- | src/events.rs | 11 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/runner.rs | 14 | ||||
| -rw-r--r-- | src/structs.rs | 81 |
6 files changed, 173 insertions, 10 deletions
diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index d075aec2a..944beaac0 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs | |||
| @@ -143,4 +143,3 @@ async fn main(spawner: Spawner) { | |||
| 143 | } | 143 | } |
| 144 | } | 144 | } |
| 145 | } | 145 | } |
| 146 | |||
diff --git a/src/control.rs b/src/control.rs index 0c06009b9..934bade23 100644 --- a/src/control.rs +++ b/src/control.rs | |||
| @@ -6,11 +6,11 @@ use embassy_time::{Duration, Timer}; | |||
| 6 | 6 | ||
| 7 | pub use crate::bus::SpiBusCyw43; | 7 | pub use crate::bus::SpiBusCyw43; |
| 8 | use crate::consts::*; | 8 | use crate::consts::*; |
| 9 | use crate::events::{Event, Events}; | 9 | use crate::events::{Event, EventSubscriber, Events}; |
| 10 | use crate::fmt::Bytes; | 10 | use crate::fmt::Bytes; |
| 11 | use crate::ioctl::{IoctlState, IoctlType}; | 11 | use crate::ioctl::{IoctlState, IoctlType}; |
| 12 | use crate::structs::*; | 12 | use crate::structs::*; |
| 13 | use crate::{countries, PowerManagementMode}; | 13 | use crate::{countries, events, PowerManagementMode}; |
| 14 | 14 | ||
| 15 | pub struct Control<'a> { | 15 | pub struct Control<'a> { |
| 16 | state_ch: ch::StateRunner<'a>, | 16 | state_ch: ch::StateRunner<'a>, |
| @@ -245,9 +245,13 @@ impl<'a> Control<'a> { | |||
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { | 247 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { |
| 248 | self.set_iovar_v::<64>(name, val).await | ||
| 249 | } | ||
| 250 | |||
| 251 | async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) { | ||
| 248 | info!("set {} = {:02x}", name, Bytes(val)); | 252 | info!("set {} = {:02x}", name, Bytes(val)); |
| 249 | 253 | ||
| 250 | let mut buf = [0; 64]; | 254 | let mut buf = [0; BUFSIZE]; |
| 251 | buf[..name.len()].copy_from_slice(name.as_bytes()); | 255 | buf[..name.len()].copy_from_slice(name.as_bytes()); |
| 252 | buf[name.len()] = 0; | 256 | buf[name.len()] = 0; |
| 253 | buf[name.len() + 1..][..val.len()].copy_from_slice(val); | 257 | buf[name.len() + 1..][..val.len()].copy_from_slice(val); |
| @@ -304,4 +308,69 @@ impl<'a> Control<'a> { | |||
| 304 | 308 | ||
| 305 | resp_len | 309 | resp_len |
| 306 | } | 310 | } |
| 311 | |||
| 312 | /// Start a wifi scan | ||
| 313 | /// | ||
| 314 | /// Returns a `Stream` of networks found by the device | ||
| 315 | /// | ||
| 316 | /// # Note | ||
| 317 | /// Device events are currently implemented using a bounded queue. | ||
| 318 | /// To not miss any events, you should make sure to always await the stream. | ||
| 319 | pub async fn scan(&mut self) -> Scanner<'_> { | ||
| 320 | const SCANTYPE_PASSIVE: u8 = 1; | ||
| 321 | |||
| 322 | let scan_params = ScanParams { | ||
| 323 | version: 1, | ||
| 324 | action: 1, | ||
| 325 | sync_id: 1, | ||
| 326 | ssid_len: 0, | ||
| 327 | ssid: [0; 32], | ||
| 328 | bssid: [0xff; 6], | ||
| 329 | bss_type: 2, | ||
| 330 | scan_type: SCANTYPE_PASSIVE, | ||
| 331 | nprobes: !0, | ||
| 332 | active_time: !0, | ||
| 333 | passive_time: !0, | ||
| 334 | home_time: !0, | ||
| 335 | channel_num: 0, | ||
| 336 | channel_list: [0; 1], | ||
| 337 | }; | ||
| 338 | |||
| 339 | self.events.mask.enable(&[Event::ESCAN_RESULT]); | ||
| 340 | let subscriber = self.events.queue.subscriber().unwrap(); | ||
| 341 | self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; | ||
| 342 | |||
| 343 | Scanner { | ||
| 344 | subscriber, | ||
| 345 | events: &self.events, | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | pub struct Scanner<'a> { | ||
| 351 | subscriber: EventSubscriber<'a>, | ||
| 352 | events: &'a Events, | ||
| 353 | } | ||
| 354 | |||
| 355 | impl Scanner<'_> { | ||
| 356 | /// wait for the next found network | ||
| 357 | pub async fn next(&mut self) -> Option<BssInfo> { | ||
| 358 | let event = self.subscriber.next_message_pure().await; | ||
| 359 | if event.header.status != EStatus::PARTIAL { | ||
| 360 | self.events.mask.disable_all(); | ||
| 361 | return None; | ||
| 362 | } | ||
| 363 | |||
| 364 | if let events::Payload::BssInfo(bss) = event.payload { | ||
| 365 | Some(bss) | ||
| 366 | } else { | ||
| 367 | None | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | impl Drop for Scanner<'_> { | ||
| 373 | fn drop(&mut self) { | ||
| 374 | self.events.mask.disable_all(); | ||
| 375 | } | ||
| 307 | } | 376 | } |
diff --git a/src/events.rs b/src/events.rs index d6f114ed9..a94c49a0c 100644 --- a/src/events.rs +++ b/src/events.rs | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | #![allow(unused)] | 1 | #![allow(dead_code)] |
| 2 | #![allow(non_camel_case_types)] | 2 | #![allow(non_camel_case_types)] |
| 3 | 3 | ||
| 4 | use core::cell::RefCell; | 4 | use core::cell::RefCell; |
| 5 | 5 | ||
| 6 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | 6 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; |
| 7 | use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; | 7 | use embassy_sync::pubsub::{PubSubChannel, Subscriber}; |
| 8 | |||
| 9 | use crate::structs::BssInfo; | ||
| 8 | 10 | ||
| 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] |
| 10 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -286,7 +288,6 @@ pub enum Event { | |||
| 286 | 288 | ||
| 287 | // TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. | 289 | // TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. |
| 288 | pub type EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>; | 290 | pub type EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>; |
| 289 | pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, Message, 2, 1, 1>; | ||
| 290 | pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; | 291 | pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; |
| 291 | 292 | ||
| 292 | pub struct Events { | 293 | pub struct Events { |
| @@ -313,6 +314,7 @@ pub struct Status { | |||
| 313 | #[derive(Clone, Copy)] | 314 | #[derive(Clone, Copy)] |
| 314 | pub enum Payload { | 315 | pub enum Payload { |
| 315 | None, | 316 | None, |
| 317 | BssInfo(BssInfo), | ||
| 316 | } | 318 | } |
| 317 | 319 | ||
| 318 | #[derive(Clone, Copy)] | 320 | #[derive(Clone, Copy)] |
| @@ -344,7 +346,7 @@ impl EventMask { | |||
| 344 | let word = n / u32::BITS; | 346 | let word = n / u32::BITS; |
| 345 | let bit = n % u32::BITS; | 347 | let bit = n % u32::BITS; |
| 346 | 348 | ||
| 347 | self.mask[word as usize] |= (1 << bit); | 349 | self.mask[word as usize] |= 1 << bit; |
| 348 | } | 350 | } |
| 349 | 351 | ||
| 350 | fn disable(&mut self, event: Event) { | 352 | fn disable(&mut self, event: Event) { |
| @@ -378,6 +380,7 @@ impl SharedEventMask { | |||
| 378 | } | 380 | } |
| 379 | } | 381 | } |
| 380 | 382 | ||
| 383 | #[allow(dead_code)] | ||
| 381 | pub fn disable(&self, events: &[Event]) { | 384 | pub fn disable(&self, events: &[Event]) { |
| 382 | let mut mask = self.mask.borrow_mut(); | 385 | let mut mask = self.mask.borrow_mut(); |
| 383 | for event in events { | 386 | for event in events { |
diff --git a/src/lib.rs b/src/lib.rs index f9244bddb..d437a882e 100644 --- a/src/lib.rs +++ b/src/lib.rs | |||
| @@ -29,6 +29,7 @@ use crate::bus::Bus; | |||
| 29 | pub use crate::bus::SpiBusCyw43; | 29 | pub use crate::bus::SpiBusCyw43; |
| 30 | pub use crate::control::Control; | 30 | pub use crate::control::Control; |
| 31 | pub use crate::runner::Runner; | 31 | pub use crate::runner::Runner; |
| 32 | pub use crate::structs::BssInfo; | ||
| 32 | 33 | ||
| 33 | const MTU: usize = 1514; | 34 | const MTU: usize = 1514; |
| 34 | 35 | ||
diff --git a/src/runner.rs b/src/runner.rs index 806ddfc49..9b99e174f 100644 --- a/src/runner.rs +++ b/src/runner.rs | |||
| @@ -7,7 +7,7 @@ use embedded_hal_1::digital::OutputPin; | |||
| 7 | use crate::bus::Bus; | 7 | use crate::bus::Bus; |
| 8 | pub use crate::bus::SpiBusCyw43; | 8 | pub use crate::bus::SpiBusCyw43; |
| 9 | use crate::consts::*; | 9 | use crate::consts::*; |
| 10 | use crate::events::{Events, Status}; | 10 | use crate::events::{Event, Events, Status}; |
| 11 | use crate::fmt::Bytes; | 11 | use crate::fmt::Bytes; |
| 12 | use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; | 12 | use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; |
| 13 | use crate::nvram::NVRAM; | 13 | use crate::nvram::NVRAM; |
| @@ -351,6 +351,8 @@ where | |||
| 351 | panic!("IOCTL error {}", cdc_header.status as i32); | 351 | panic!("IOCTL error {}", cdc_header.status as i32); |
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | info!("IOCTL Response: {:02x}", Bytes(response)); | ||
| 355 | |||
| 354 | self.ioctl_state.ioctl_done(response); | 356 | self.ioctl_state.ioctl_done(response); |
| 355 | } | 357 | } |
| 356 | } | 358 | } |
| @@ -404,7 +406,15 @@ where | |||
| 404 | 406 | ||
| 405 | if self.events.mask.is_enabled(evt_type) { | 407 | if self.events.mask.is_enabled(evt_type) { |
| 406 | let status = event_packet.msg.status; | 408 | let status = event_packet.msg.status; |
| 407 | let event_payload = events::Payload::None; | 409 | let event_payload = match evt_type { |
| 410 | Event::ESCAN_RESULT if status == EStatus::PARTIAL => { | ||
| 411 | let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; | ||
| 412 | let Some(bss_info) = BssInfo::parse(bss_info) else { return }; | ||
| 413 | events::Payload::BssInfo(*bss_info) | ||
| 414 | } | ||
| 415 | Event::ESCAN_RESULT => events::Payload::None, | ||
| 416 | _ => events::Payload::None, | ||
| 417 | }; | ||
| 408 | 418 | ||
| 409 | // this intentionally uses the non-blocking publish immediate | 419 | // this intentionally uses the non-blocking publish immediate |
| 410 | // publish() is a deadlock risk in the current design as awaiting here prevents ioctls | 420 | // publish() is a deadlock risk in the current design as awaiting here prevents ioctls |
diff --git a/src/structs.rs b/src/structs.rs index f54ec7fcf..d01d5a65c 100644 --- a/src/structs.rs +++ b/src/structs.rs | |||
| @@ -404,3 +404,84 @@ impl EventMask { | |||
| 404 | self.events[evt / 8] &= !(1 << (evt % 8)); | 404 | self.events[evt / 8] &= !(1 << (evt % 8)); |
| 405 | } | 405 | } |
| 406 | } | 406 | } |
| 407 | |||
| 408 | /// Parameters for a wifi scan | ||
| 409 | #[derive(Clone, Copy)] | ||
| 410 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 411 | #[repr(C)] | ||
| 412 | pub struct ScanParams { | ||
| 413 | pub version: u32, | ||
| 414 | pub action: u16, | ||
| 415 | pub sync_id: u16, | ||
| 416 | pub ssid_len: u32, | ||
| 417 | pub ssid: [u8; 32], | ||
| 418 | pub bssid: [u8; 6], | ||
| 419 | pub bss_type: u8, | ||
| 420 | pub scan_type: u8, | ||
| 421 | pub nprobes: u32, | ||
| 422 | pub active_time: u32, | ||
| 423 | pub passive_time: u32, | ||
| 424 | pub home_time: u32, | ||
| 425 | pub channel_num: u32, | ||
| 426 | pub channel_list: [u16; 1], | ||
| 427 | } | ||
| 428 | impl_bytes!(ScanParams); | ||
| 429 | |||
| 430 | /// Wifi Scan Results Header, followed by `bss_count` `BssInfo` | ||
| 431 | #[derive(Clone, Copy)] | ||
| 432 | // #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 433 | #[repr(C, packed(2))] | ||
| 434 | pub struct ScanResults { | ||
| 435 | pub buflen: u32, | ||
| 436 | pub version: u32, | ||
| 437 | pub sync_id: u16, | ||
| 438 | pub bss_count: u16, | ||
| 439 | } | ||
| 440 | impl_bytes!(ScanResults); | ||
| 441 | |||
| 442 | impl ScanResults { | ||
| 443 | pub fn parse(packet: &mut [u8]) -> Option<(&mut ScanResults, &mut [u8])> { | ||
| 444 | if packet.len() < ScanResults::SIZE { | ||
| 445 | return None; | ||
| 446 | } | ||
| 447 | |||
| 448 | let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE); | ||
| 449 | let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap()); | ||
| 450 | |||
| 451 | if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE { | ||
| 452 | warn!("Scan result, incomplete BssInfo"); | ||
| 453 | return None; | ||
| 454 | } | ||
| 455 | |||
| 456 | Some((scan_results, bssinfo)) | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | /// Wifi Scan Result | ||
| 461 | #[derive(Clone, Copy)] | ||
| 462 | // #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 463 | #[repr(C, packed(2))] | ||
| 464 | #[non_exhaustive] | ||
| 465 | pub struct BssInfo { | ||
| 466 | pub version: u32, | ||
| 467 | pub length: u32, | ||
| 468 | pub bssid: [u8; 6], | ||
| 469 | pub beacon_period: u16, | ||
| 470 | pub capability: u16, | ||
| 471 | pub ssid_len: u8, | ||
| 472 | pub ssid: [u8; 32], | ||
| 473 | // there will be more stuff here | ||
| 474 | } | ||
| 475 | impl_bytes!(BssInfo); | ||
| 476 | |||
| 477 | impl BssInfo { | ||
| 478 | pub fn parse(packet: &mut [u8]) -> Option<&mut Self> { | ||
| 479 | if packet.len() < BssInfo::SIZE { | ||
| 480 | return None; | ||
| 481 | } | ||
| 482 | |||
| 483 | Some(BssInfo::from_bytes_mut( | ||
| 484 | packet[..BssInfo::SIZE].as_mut().try_into().unwrap(), | ||
| 485 | )) | ||
| 486 | } | ||
| 487 | } | ||
