diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mld.h b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mld.h
index 4fa3f97db2..b1da4a18bd 100644
--- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mld.h
+++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_mld.h
@@ -353,4 +353,41 @@ int rb_iwl_mld_parse_rx_mpdu(const uint8_t *payload, size_t len,
const char *rb_iwl_mld_name(void);
uint32_t rb_iwl_mld_rx_frames(const struct rb_iwl_mld *mld);
+/* ── Rust MLD dispatch bridge ──────────────────────────────────────
+ * These #[no_mangle] extern "C" wrappers live in mld/dispatch.rs.
+ * linux_port.c iwl_ops_* callbacks call them alongside their existing
+ * C implementation, making the Rust MldState state machine live.
+ * All functions are safe to call when MldState is uninitialized (they
+ * return -1 or 0 and log a warning).
+ */
+
+/* Lifecycle */
+int rb_mld_state_available(void);
+int rb_mld_init(void *dev_handle);
+void rb_mld_destroy(void);
+
+/* mac80211 ops dispatch */
+int rb_mld_ops_start(void);
+void rb_mld_ops_stop(void);
+int rb_mld_ops_config(uint32_t changed);
+int rb_mld_ops_bss_info_changed(uint8_t mac_id, uint64_t changed);
+int rb_mld_ops_add_interface(uint32_t vif_type, const uint8_t *addr);
+int rb_mld_ops_remove_interface(uint8_t mac_id);
+int rb_mld_ops_sta_state(uint32_t sta_id, uint32_t old_state, uint32_t new_state);
+int rb_mld_ops_hw_scan(int active);
+void rb_mld_ops_cancel_hw_scan(void);
+int rb_mld_ops_flush(uint32_t queues, int drop);
+int rb_mld_ops_ampdu_action(uint32_t action, uint32_t sta_id, uint16_t tid, uint16_t ssn);
+int rb_mld_ops_assign_vif_chanctx(uint8_t mac_id);
+int rb_mld_ops_unassign_vif_chanctx(uint8_t mac_id);
+int rb_mld_ops_reconfig_complete(void);
+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);
+
+/* Notification dispatch (from ISR/tasklet) */
+int rb_mld_notify_rx(uint16_t wide_id, const uint8_t *data, size_t len);
+uint32_t rb_mld_rx_frame_count(void);
+
#endif /* RB_IWLWIFI_MLD_H */
diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c
index e1039c9a23..971d99451d 100644
--- a/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c
+++ b/local/recipes/drivers/redbear-iwlwifi/source/src/linux_port.c
@@ -898,6 +898,8 @@ static int rb_iwlwifi_register_mac80211_locked(struct iwl_trans_pcie *trans)
return -EIO;
}
+ rb_mld_init((void *)trans);
+
trans->netdev = alloc_netdev_mqs(0, "wlan%d", 0, NULL, 1, 1);
if (!trans->netdev) {
ieee80211_unregister_hw(trans->hw);
@@ -1558,10 +1560,11 @@ static void iwl_pcie_rx_handle(struct iwl_trans_pcie *trans)
pr_warn("rx_handle: failed to allocate skb, skipping frame\n");
dma_sync_single_for_device(&trans->pci_dev->device_obj, buf->dma_addr,
buf->size, DMA_FROM_DEVICE);
- break;
}
}
+ rb_mld_notify_rx(0x00c1, buf->addr, (size_t)buf->size);
+
if (trans->scan_active && trans->wiphy &&
frame_type == 0U && (frame_subtype == 8U || frame_subtype == 5U)) {
u16 beacon_interval = 0;
@@ -1873,6 +1876,7 @@ static int iwl_ops_start(struct ieee80211_hw *hw)
if (!trans)
return -ENODEV;
trans->fw_running = trans->nic_active ? 1 : 0;
+ rb_mld_ops_start();
return 0;
}
@@ -1885,6 +1889,7 @@ static void iwl_ops_stop(struct ieee80211_hw *hw)
trans->fw_running = 0;
if (trans->netdev)
netif_carrier_off(trans->netdev);
+ rb_mld_ops_stop();
}
static int iwl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
@@ -1895,6 +1900,7 @@ static int iwl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *
trans->vif = vif;
memcpy(vif->addr, trans->mac_addr, sizeof(trans->mac_addr));
vif->type = NL80211_IFTYPE_STATION;
+ rb_mld_ops_add_interface(NL80211_IFTYPE_STATION, vif->addr);
return 0;
}
@@ -1905,6 +1911,7 @@ static void iwl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_v
return;
if (trans->vif == vif)
trans->vif = NULL;
+ rb_mld_ops_remove_interface(0);
}
static int iwl_ops_config(struct ieee80211_hw *hw, u32 changed)
@@ -1939,6 +1946,7 @@ static int iwl_ops_config(struct ieee80211_hw *hw, u32 changed)
pr_debug("config: tx power set to %u dBm\n", trans->tx_power);
}
+ rb_mld_ops_config(changed);
return 0;
}
@@ -1977,6 +1985,8 @@ static void iwl_ops_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_v
trans->bss_conf.chandef.center_freq = info->chandef.center_freq;
trans->bss_conf.chandef.band = info->chandef.band;
trans->bss_conf.chandef.channel = info->chandef.channel;
+
+ rb_mld_ops_bss_info_changed(0, changed);
}
static int iwl_ops_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
@@ -2008,6 +2018,7 @@ static int iwl_ops_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
trans->connected = 0;
}
+ rb_mld_ops_sta_state(sta->aid, (u32)old_state, (u32)new_state);
return 0;
}
diff --git a/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs
new file mode 100644
index 0000000000..d07db869c6
--- /dev/null
+++ b/local/recipes/drivers/redbear-iwlwifi/source/src/mld/dispatch.rs
@@ -0,0 +1,378 @@
+//! FFI dispatch bridge — C mac80211 ops → Rust MldState.
+//!
+//! linux_port.c's `iwl_ops_*` callback functions call these
+//! `#[unsafe(no_mangle)] extern "C"` wrappers alongside their existing C
+//! implementation. This makes the Rust MLD state machine LIVE without
+//! removing the C code (kept as historic reference per operator request).
+//!
+//! Design:
+//! - Single global `Mutex