diff --git a/Cargo.toml b/Cargo.toml index e250465..0bce86d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ esp-radio = { version = "0.18.0", features = [ "log-04", "unstable", "wifi", + "sniffer", ] } diff --git a/src/bin/main.rs b/src/bin/main.rs index e11a39a..4da699d 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,12 +10,66 @@ use esp_backtrace as _; use esp_hal::clock::CpuClock; use esp_hal::main; -use esp_hal::time::{Duration, Instant}; use esp_hal::timer::timg::TimerGroup; -use log::info; +use log::{debug, error, info, warn}; extern crate alloc; +const WIFI_CHANNEL: core::ffi::c_int = 180; + +unsafe extern "C" { + /// Changes the 802.11 PHY channel + /// + /// C function signature: `void phy_change_channel(int,int,int,int)` + /// + /// Options: + /// - `primary`: Channel frequency in MHz + /// - `ignored1`: Ignored (set to 1) + /// - `ignored2`: Ignored (set to 0) + /// - `ht_mode`: Probably HT mode + pub unsafe fn phy_change_channel( + primary: core::ffi::c_int, + ignored1: core::ffi::c_int, + ignored2: core::ffi::c_int, + ht_mode: core::ffi::c_int, + ) -> core::ffi::c_int; + + /// Enables 802.11p mode on the ESP32-C5 + /// + /// C function signature: `void phy_11p_set(int,int)` + /// + /// Options: + /// - `enable`: Boolean option to enable 802.11p mode + /// - `zero`: Unknown, but need to be zero + pub unsafe fn phy_11p_set(enable: u8, zero: u8); +} + +#[repr(u8)] +#[derive(Debug)] +#[allow(dead_code)] +enum EspWifiPromiscuousPktType { + /// Management frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + MGMT = 0, + /// Control frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + CTRL = 1, + /// Data frame, indicates 'buf' argument is wifi_promiscuous_pkt_t + DATA = 2, + /// Other type, such as MIMO etc. 'buf' argument is wifi_promiscuous_pkt_t but the payload is zero length. + MISC = 3, +} + +impl EspWifiPromiscuousPktType { + #[allow(dead_code)] + pub fn as_repr(self) -> u8 { + self as u8 + } + + /// Converts to underlying type of `wifi_promiscuous_pkt_type_t` (used by `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`) + pub fn as_frame_type(self) -> core::ffi::c_uint { + (self as u8) as core::ffi::c_uint + } +} + // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); @@ -50,15 +104,45 @@ fn main() -> ! { let sw_interrupt = esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (mut _wifi_controller, _interfaces) = - esp_radio::wifi::new(peripherals.WIFI, Default::default()) - .expect("Failed to initialize Wi-Fi controller"); + + let (_wifi_controller, interfaces) = esp_radio::wifi::new( + peripherals.WIFI, + esp_radio::wifi::ControllerConfig::default(), + ) + .expect("Failed to initialize Wi-Fi controller"); + + let mut sniffer = interfaces.sniffer; + sniffer + .set_promiscuous_mode(true) + .expect("Failed to enable promiscuous mode"); + + sniffer.set_receive_cb(|packet| { + // Ignore frames with errors and non-data frames + if packet.rx_cntl.rx_state != 0 { + warn!("Received frame has RX error: {}", packet.rx_cntl.rx_state); + return; + } + if packet.frame_type != EspWifiPromiscuousPktType::DATA.as_frame_type() { + debug!("Received frame is not a DATA frame: {}", packet.frame_type); + return; + } + + // TODO: handle 802.11 frame + info!("Received frame with {} bytes", packet.len); + }); + + unsafe { + phy_11p_set(1, 0); + match phy_change_channel(5000 + (WIFI_CHANNEL * 5), 1, 0, 0) { + 0 => {} + ret => { + error!("ERROR: Failed to change channel: {ret}"); + } + } + } + info!("WiFi promiscuous mode on channel {WIFI_CHANNEL} running"); loop { - info!("Hello world!"); - let delay_start = Instant::now(); - while delay_start.elapsed() < Duration::from_millis(500) {} + // nothing to do here, all data is handled in RX callback for now } - - // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples }