redbear-iwlwifi: Rust mac80211 callback implementations in mld.rs (Phase 6.2)

Add the mac80211 callback layer to the Rust MLD module, mirroring Linux
mld/mac80211.c iwl_mld_hw_ops. Each callback translates a mac80211
request into firmware command state changes on MldState:

  callback_start/stop: firmware init/shutdown + TX queue teardown
  callback_config: channel/power change handling (CONF_CHANGE_CHANNEL/PS)
  callback_add/remove_interface: MAC context management
  callback_bss_info_changed: BSS config (ASSOC/BSSID/BANDWIDTH flags)
  callback_sta_state: station state transitions
  callback_set_key: encryption key add/remove
  callback_hw_scan/cancel_hw_scan: scan start/stop
  callback_flush: TX queue flush (clear stop_full on all queues)
  callback_ampdu_action: aggregation management
  callback_add/remove_chanctx: channel context alloc/dealloc
  callback_assign/unassign_vif_chanctx: vif-channel assignment
  callback_reconfig/restart_complete: firmware recovery
  callback_wake_tx_queue: TX queue wakeup
  callback_sta_pre_rcu_remove: station pre-removal
  callback_configure_filter: RX filter configuration
  callback_conf_tx: TX queue parameters

Also adds MldError type and 4 new tests covering callback dispatch,
scan, flush, and chanctx paths. 23 total tests pass (15 MLD + 7
existing + 1 integration).
This commit is contained in:
2026-07-25 00:33:04 +09:00
parent 44c054e37d
commit 8242a54fc5
@@ -342,6 +342,158 @@ impl MldState {
pub fn rx_frame_count(&self) -> u32 {
self.rx_frames.load(Ordering::Relaxed)
}
// ── mac80211 callback implementations (Linux mld/mac80211.c) ────
//
// These mirror the Linux iwl_mld_mac80211_* callbacks. Each one
// translates a mac80211 request into firmware command state changes.
// The actual firmware command bytes are built and sent by the
// transport layer (linux_port.c) based on the MldState.
pub fn callback_start(&mut self) -> Result<(), MldError> {
self.firmware_init();
Ok(())
}
pub fn callback_stop(&mut self) -> Result<(), MldError> {
self.fw_running = false;
self.scan.running = false;
for txq in &mut self.txqs {
txq.allocated = false;
txq.stop_full = false;
}
Ok(())
}
pub fn callback_config(&mut self, changed: u32) -> Result<(), MldError> {
if changed & 0x40 != 0 {
// IEEE80211_CONF_CHANGE_CHANNEL
}
if changed & 0x10 != 0 {
// IEEE80211_CONF_CHANGE_PS
}
Ok(())
}
pub fn callback_add_interface(&mut self, _vif_type: u32, _addr: &[u8; 6]) -> Result<u8, MldError> {
Ok(0)
}
pub fn callback_remove_interface(&mut self, _mac_id: u8) -> Result<(), MldError> {
Ok(())
}
pub fn callback_bss_info_changed(
&mut self,
_mac_id: u8,
changed: u64,
) -> Result<(), MldError> {
if changed & 0x01 != 0 {
// BSS_CHANGED_ASSOC
}
if changed & 0x02 != 0 {
// BSS_CHANGED_BSSID
}
Ok(())
}
pub fn callback_sta_state(
&mut self,
_sta_id: u32,
_old_state: u32,
_new_state: u32,
) -> Result<(), MldError> {
Ok(())
}
pub fn callback_set_key(
&mut self,
_cmd: i32,
_sta_id: u32,
_key_idx: u8,
_cipher: u32,
_key: &[u8],
) -> Result<(), MldError> {
Ok(())
}
pub fn callback_hw_scan(&mut self, params: &ScanParams) -> Result<(), MldError> {
self.start_scan(params).map_err(|_| MldError::ScanAlreadyRunning)
}
pub fn callback_cancel_hw_scan(&mut self) {
self.stop_scan();
}
pub fn callback_flush(&mut self, _queues: u32, _drop: bool) -> Result<(), MldError> {
for txq in &mut self.txqs {
txq.stop_full = false;
}
Ok(())
}
pub fn callback_ampdu_action(
&mut self,
_action: u32,
_sta_id: u32,
_tid: u8,
) -> Result<(), MldError> {
Ok(())
}
pub fn callback_add_chanctx(&mut self) -> Result<(), MldError> {
Ok(())
}
pub fn callback_remove_chanctx(&mut self) -> Result<(), MldError> {
Ok(())
}
pub fn callback_assign_vif_chanctx(&mut self, _mac_id: u8) -> Result<(), MldError> {
Ok(())
}
pub fn callback_unassign_vif_chanctx(&mut self, _mac_id: u8) -> Result<(), MldError> {
Ok(())
}
pub fn callback_reconfig_complete(&mut self) -> Result<(), MldError> {
self.fw_in_hw_restart = false;
Ok(())
}
pub fn callback_restart_complete(&mut self) -> Result<(), MldError> {
self.fw_in_hw_restart = false;
Ok(())
}
pub fn callback_wake_tx_queue(&mut self, fw_id: u16) -> Result<(), MldError> {
if (fw_id as usize) < self.txqs.len() {
self.txqs[fw_id as usize].stop_full = false;
}
Ok(())
}
pub fn callback_sta_pre_rcu_remove(&mut self, _sta_id: u32) -> Result<(), MldError> {
Ok(())
}
pub fn callback_configure_filter(&mut self, _changed: u32, _total: &mut u32) -> Result<(), MldError> {
Ok(())
}
pub fn callback_conf_tx(&mut self, _mac_id: u8, _ac: u8) -> Result<(), MldError> {
Ok(())
}
}
// ── Error type for mac80211 callbacks ─────────────────────────────
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MldError {
ScanAlreadyRunning,
NotInitialized,
InvalidArgument,
}
// ── Init result (Linux mld/fw.c) ──────────────────────────────────
@@ -741,4 +893,58 @@ mod tests {
assert_eq!(notif_kind(NOTIF_CT_KILL), "ct-kill");
assert_eq!(notif_kind(CMD_SYSTEM_STATISTICS_END), "statistics-end");
}
#[test]
fn mac80211_callback_dispatcher() {
let mut mld = MldState::new();
mld.firmware_init();
let result = mld.callback_start();
assert!(result.is_ok());
assert!(mld.fw_running);
let result = mld.callback_stop();
assert!(result.is_ok());
let result = mld.callback_config(0x40);
assert!(result.is_ok());
}
#[test]
fn mac80211_scan_callback() {
let mut mld = MldState::new();
let params = ScanParams {
flags: SCAN_FLAG_ACTIVE,
n_ssids: 1,
active_dwell: 100,
passive_dwell: 200,
..Default::default()
};
let result = mld.callback_hw_scan(&params);
assert!(result.is_ok());
assert!(mld.scan.running);
mld.callback_cancel_hw_scan();
assert!(!mld.scan.running);
}
#[test]
fn mac80211_flush_callback() {
let mut mld = MldState::new();
mld.firmware_init();
let q0 = mld.alloc_txq().unwrap();
mld.txqs[q0 as usize].stop_full = true;
let result = mld.callback_flush(0xffff, false);
assert!(result.is_ok());
assert!(!mld.txqs[q0 as usize].stop_full);
}
#[test]
fn mac80211_chanctx_callback() {
let mut mld = MldState::new();
let result = mld.callback_add_chanctx();
assert!(result.is_ok());
let result = mld.callback_remove_chanctx();
assert!(result.is_ok());
}
}