feat(iwlwifi): wire remaining ops + CLI status + fix test race

Second round of MLD dispatch improvements:

linux_port.c:
  - iwl_ops_sw_scan_start now calls rb_mld_ops_hw_scan(1)
  - iwl_ops_sw_scan_complete now calls rb_mld_ops_cancel_hw_scan()
  - iwl_ops_set_key now calls rb_mld_ops_set_key (full cipher
    suite mapping: CCMP/CCMP256/GCMP128/GCMP256/TKIP/WEP40/WEP104)

mld/dispatch.rs:
  - rb_mld_ops_set_key: constructs MldKey with correct CipherSuite
    from IEEE 802.11 cipher suite OUI+type values, dispatches to
    MldState::install_key / remove_key
  - Fixed intermittent test failure: dispatch tests now serialize
    via static TEST_LOCK to prevent races on the global MLD_STATE

main.rs:
  - --status now reports mld_state=live|inactive and mld_rx_frames=N
    so operators can verify the Rust MLD layer is receiving callbacks

linux_mld.h: rb_mld_ops_set_key declaration

57 tests pass consistently across 3 consecutive runs.
This commit is contained in:
2026-07-26 03:39:55 +09:00
parent 595ab48007
commit a9e15dc910
4 changed files with 89 additions and 0 deletions
@@ -385,6 +385,8 @@ int rb_mld_ops_restart_complete(void);
int rb_mld_ops_configure_filter(uint32_t changed, uint32_t *total);
int rb_mld_ops_conf_tx(uint8_t mac_id, uint8_t ac);
int rb_mld_ops_wake_tx_queue(uint16_t fw_id);
int rb_mld_ops_set_key(int cmd, uint32_t sta_id, uint8_t key_idx, uint32_t cipher,
const uint8_t *key_data, uint32_t key_len);
/* Notification dispatch (from ISR/tasklet) */
int rb_mld_notify_rx(uint16_t wide_id, const uint8_t *data, size_t len);
@@ -2050,6 +2050,8 @@ static int iwl_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
trans->last_security[0] = '\0';
}
rb_mld_ops_set_key(cmd == SET_KEY ? 0 : 1, sta ? sta->aid : 0,
key->keyidx, key->cipher, key->key, (uint32_t)key->keylen);
return 0;
}
@@ -2063,6 +2065,7 @@ static void iwl_ops_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif
trans->scan_results_count = 0;
trans->scan_active = 1;
trans->svc_flags |= RB_IWL_SVC_SCAN_ACTIVE;
rb_mld_ops_hw_scan(1);
}
static void iwl_ops_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
@@ -2074,6 +2077,7 @@ static void iwl_ops_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_v
rb_iwlwifi_report_scan_result(trans);
trans->scan_active = 0;
trans->svc_flags &= ~RB_IWL_SVC_SCAN_ACTIVE;
rb_mld_ops_cancel_hw_scan();
}
static int iwl_ops_sched_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *req)
@@ -1256,10 +1256,14 @@ fn linux_kpi_status_candidate(candidate: &Candidate) -> Result<Vec<String>, Driv
.trim_matches(char::from(0))
.trim()
.to_string();
let mld_active = mld::dispatch::rb_mld_state_available();
let mld_rx = mld::dispatch::rb_mld_rx_frame_count();
Ok(vec![
format!("device={}", candidate.location),
format!("family={}", candidate.family),
parsed,
format!("mld_state={}", if mld_active > 0 { "live" } else { "inactive" }),
format!("mld_rx_frames={}", mld_rx),
])
}
@@ -258,6 +258,73 @@ pub extern "C" fn rb_mld_ops_configure_filter(changed: u32, total: *mut u32) ->
rc
}
#[unsafe(no_mangle)]
pub extern "C" fn rb_mld_ops_set_key(
cmd: i32,
sta_id: u32,
key_idx: u8,
cipher: u32,
key_data: *const u8,
key_len: u32,
) -> i32 {
if key_data.is_null() && key_len > 0 {
return -1;
}
with_mld(-1, |mld| {
if cmd == 0 {
let key_slice = if key_len > 0 && !key_data.is_null() {
unsafe { std::slice::from_raw_parts(key_data, key_len as usize) }
} else {
&[]
};
let mut mld_key = key::MldKey::default();
mld_key.sta_id = sta_id;
mld_key.key_idx = key_idx;
mld_key.pairwise = true;
mld_key.cipher = match cipher {
0x000FAC04 => key::CipherSuite::Ccmp,
0x000FAC0A => key::CipherSuite::Ccmp256,
0x000FAC08 => key::CipherSuite::Gcmp128,
0x000FAC09 => key::CipherSuite::Gcmp256,
0x000FAC02 => key::CipherSuite::Tkip,
0x000FAC01 => key::CipherSuite::Wep40,
0x000FAC05 => key::CipherSuite::Wep104,
_ => key::CipherSuite::None,
};
if !key_slice.is_empty() {
mld_key.key = key_slice.to_vec();
}
log::info!(
"rb_mld_ops_set_key: SET_KEY sta={} idx={} cipher={:#06x} len={}",
sta_id,
key_idx,
cipher,
key_len
);
match mld.install_key(&mld_key) {
Ok(_) => 0,
Err(e) => {
log::warn!("rb_mld_ops_set_key install: {:?}", e);
-1
}
}
} else {
log::info!(
"rb_mld_ops_set_key: DISABLE_KEY sta={} idx={}",
sta_id,
key_idx
);
match mld.remove_key(sta_id, key_idx) {
Ok(_) => 0,
Err(e) => {
log::warn!("rb_mld_ops_set_key remove: {:?}", e);
-1
}
}
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn rb_mld_ops_conf_tx(mac_id: u8, ac: u8) -> i32 {
with_mld(-1, |mld| match mld.callback_conf_tx(mac_id, ac) {
@@ -317,8 +384,15 @@ pub extern "C" fn rb_mld_rx_frame_count() -> u32 {
mod tests {
use super::*;
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn lock() -> std::sync::MutexGuard<'static, ()> {
TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner())
}
#[test]
fn rb_mld_state_available_reports_init() {
let _g = lock();
{
let mut guard = MLD_STATE.lock().unwrap();
*guard = Some(Box::new(MldState::new()));
@@ -330,6 +404,7 @@ mod tests {
#[test]
fn rb_mld_init_creates_state() {
let _g = lock();
rb_mld_destroy();
assert_eq!(rb_mld_init(std::ptr::null_mut()), 0);
assert_eq!(rb_mld_state_available(), 1);
@@ -338,6 +413,7 @@ mod tests {
#[test]
fn rb_mld_ops_start_without_dev_handle_returns_zero() {
let _g = lock();
rb_mld_destroy();
assert_eq!(rb_mld_init(std::ptr::null_mut()), 0);
let rc = rb_mld_ops_start();
@@ -347,6 +423,7 @@ mod tests {
#[test]
fn rb_mld_ops_when_uninitialized_returns_error() {
let _g = lock();
rb_mld_destroy();
assert_eq!(rb_mld_ops_start(), -1);
assert_eq!(rb_mld_ops_config(0), -1);
@@ -354,6 +431,7 @@ mod tests {
#[test]
fn rb_mld_notify_rx_null_data_returns_error() {
let _g = lock();
rb_mld_destroy();
assert_eq!(rb_mld_init(std::ptr::null_mut()), 0);
assert_eq!(rb_mld_notify_rx(0xc1, std::ptr::null(), 0), -1);
@@ -362,6 +440,7 @@ mod tests {
#[test]
fn rb_mld_rx_frame_count_tracks_notifications() {
let _g = lock();
rb_mld_destroy();
assert_eq!(rb_mld_init(std::ptr::null_mut()), 0);
assert_eq!(rb_mld_rx_frame_count(), 0);