use esp_radio::esp_now::WifiPhyRate;
use esp_radio::wifi::sta::StationConfig;
use esp_radio::wifi::{Config, SecondaryChannel, WifiController};
use crate::log_ln;
pub(crate) fn takeover_esp_now_recv(is_sniffer: bool) {
install_static_espnow_recv();
if is_sniffer {
suspend_esp_now_recv();
}
}
pub fn install_static_espnow_recv() {
crate::esp_now_pool::install();
}
pub(crate) fn suspend_esp_now_recv() {
unsafe extern "C" {
fn esp_now_unregister_recv_cb() -> i32;
}
unsafe {
let _ = esp_now_unregister_recv_cb();
}
}
#[cfg(feature = "esp32c5")]
pub(crate) fn with_espnow_recv_suspended<F: FnOnce()>(f: F) {
suspend_esp_now_recv();
f();
install_static_espnow_recv();
}
#[cfg(not(feature = "esp32c5"))]
pub(crate) fn with_espnow_recv_suspended<F: FnOnce()>(f: F) {
f();
}
pub(crate) fn bring_up_espnow_sta(controller: &mut WifiController, resume_recv: bool) {
if controller
.set_config(&Config::Station(StationConfig::default()))
.is_err()
{
log_ln!("ESP-NOW: STA bring-up failed; PHY may stay legacy/20 MHz");
}
if resume_recv {
install_static_espnow_recv();
}
}
pub(crate) fn apply_espnow_ht40_mode(
controller: &mut WifiController,
primary: u8,
secondary: SecondaryChannel,
) {
#[cfg(feature = "esp32c5")]
bring_up_espnow_sta(controller, false);
apply_espnow_ht40(controller, primary, secondary);
}
pub(crate) fn apply_espnow_band_for_channel(controller: &mut WifiController, primary: u8) {
#[cfg(feature = "esp32c5")]
{
use esp_radio::wifi::BandMode;
let band = if primary >= 36 {
BandMode::_5G
} else {
BandMode::_2_4G
};
if controller.set_band_mode(band).is_err() {
log_ln!("ESP-NOW: set_band_mode failed for ch {}", primary);
}
}
#[cfg(not(feature = "esp32c5"))]
{
let _ = (controller, primary);
}
}
pub(crate) fn apply_espnow_ht40(
controller: &mut WifiController,
primary: u8,
secondary: SecondaryChannel,
) {
apply_espnow_band_for_channel(controller, primary);
#[cfg(feature = "esp32c5")]
{
use esp_radio::wifi::{Protocol, Protocols};
if primary >= 36 {
let protocols = Protocols::default().with_5(Protocol::A | Protocol::N);
if controller.set_protocols(protocols).is_err() {
log_ln!("HT40: set_protocols (A/N on 5G) failed");
}
}
}
if controller.set_channel(primary, secondary).is_err() {
log_ln!("HT40: set_channel failed");
}
#[cfg(feature = "esp32c5")]
{
use esp_radio::wifi::Bandwidth;
match controller.bandwidths() {
Ok(bw) => {
let bw = if primary >= 36 {
bw.with_5(Bandwidth::_40MHz)
} else {
bw.with_2_4(Bandwidth::_40MHz)
};
if let Err(e) = controller.set_bandwidths(bw) {
log_ln!("HT40: set_bandwidths failed: {:?}", e);
}
}
Err(_) => log_ln!("HT40: read bandwidths failed"),
}
}
}
#[repr(C)]
struct WifiTxRateConfig {
phymode: u32,
rate: u32,
ersu: bool,
dcm: bool,
}
const WIFI_PHY_MODE_11B: u32 = 1;
const WIFI_PHY_MODE_11G: u32 = 2;
const WIFI_PHY_MODE_HT20: u32 = 4;
const WIFI_PHY_MODE_HT40: u32 = 5;
unsafe extern "C" {
fn esp_now_set_peer_rate_config(peer_addr: *const u8, config: *mut WifiTxRateConfig) -> i32;
}
fn wifi_phy_rate_to_c(rate: WifiPhyRate) -> u32 {
match rate {
WifiPhyRate::RateLora250k => 41,
WifiPhyRate::RateLora500k => 42,
WifiPhyRate::RateMax => 43,
other => {
let idx = other as u32;
if idx < 4 { idx } else { idx + 1 }
}
}
}
fn espnow_phymode(rate: WifiPhyRate, secondary: Option<SecondaryChannel>) -> u32 {
let c = wifi_phy_rate_to_c(rate);
if (16..=31).contains(&c) {
if secondary.is_some() {
WIFI_PHY_MODE_HT40
} else {
WIFI_PHY_MODE_HT20
}
} else if c <= 7 {
WIFI_PHY_MODE_11B
} else {
WIFI_PHY_MODE_11G
}
}
pub fn set_peer_espnow_phy(
peer: &[u8; 6],
rate: WifiPhyRate,
secondary: Option<SecondaryChannel>,
) {
let mut cfg = WifiTxRateConfig {
phymode: espnow_phymode(rate, secondary),
rate: wifi_phy_rate_to_c(rate),
ersu: false,
dcm: false,
};
let rc = unsafe { esp_now_set_peer_rate_config(peer.as_ptr(), &mut cfg) };
if rc != 0 {
log_ln!(
"ESP-NOW: set_peer_rate_config rc={} phymode={} rate={}",
rc,
cfg.phymode,
cfg.rate
);
}
}
pub fn apply_peer_espnow_phy(
peer: &[u8; 6],
rate: WifiPhyRate,
secondary: Option<SecondaryChannel>,
) {
with_espnow_recv_suspended(|| {
set_peer_espnow_phy(peer, rate, secondary);
});
}