redbear-iwlwifi: Mini-MLD foundation — iwlmld op-mode + BZ->MLD gate

Phase 6.2 foundation. Port the Mini-MLD layer from Linux 7.1
(drivers/net/wireless/intel/iwlwifi/mld/), the MLD analog of Mini-MVM:

- linux_mld.h: iwlmld op-mode IDs, notification command groups
  (LEGACY/MAC_CONF/PHY_OPS/DATA_PATH/SCAN/STATISTICS), and the MLD
  notification command ID set. The RX frame IDs (0xc0/0xc1/0xc5) and the
  iwl_rx_mpdu_desc descriptor are shared with MVM and stay in Mini-MVM;
  MLD adds ROC/session-protection/MU-group-mgmt/probe-response/
  channel-switch/MFUART/DTS-measurement notification IDs.
- linux_mld.c: rb_iwl_mld_handle_notification() — real MLD notification
  dispatch. RX MPDU frames are parsed by the Mini-MVM descriptor parser
  (rb_iwl_mvm_extract_signal/detect_format, no duplicate parser) with real
  signal/rate extraction; MLD-specific notifications are identified,
  grouped, and counted. Includes the BZ-family op-mode selection
  (rb_iwl_opmode_for_family, mirroring Linux iwl-drv.c).
- linux_port.c: include linux_mld.h; add trans->opmode set from
  device_family (BZ -> iwlmld, others -> iwlmvm); report opmode= in the
  linux_kpi_status line so the gate is observable.
- build.rs: compile linux_mld.c.

This is the bounded foundation; MLO/link management and the full MLD
feature set (mac80211-integrated) are the multi-week continuation.
All 8 host tests pass.
This commit is contained in:
2026-07-24 11:53:35 +09:00
parent 78aca1bb45
commit 1e5c080563
4 changed files with 266 additions and 1 deletions
@@ -6,6 +6,7 @@ fn main() {
cc::Build::new()
.file("src/linux_port.c")
.file("src/linux_mvm.c")
.file("src/linux_mld.c")
.include(linux_kpi_headers)
.warnings(true)
.compile("redbear_iwlwifi_linux_port");
@@ -0,0 +1,169 @@
/*
* Red Bear iwlwifi Mini-MLD — op-mode registration + MLD notification
* dispatch + BZ -> MLD gate. See linux_mld.h for provenance.
*
* RX frames (RX_PHY_CMD/RX_MPDU_CMD/BA_NOTIF) use the shared firmware
* descriptor and are parsed by the Mini-MVM layer (rb_iwl_mvm_*). Mini-MLD
* does not duplicate that parser; it only adds the iwlmld op-mode and the
* MLD-specific notification set.
*/
#include "linux_mld.h"
#include "linux_mvm.h"
/* ------------------------------------------------------------------ */
/* Op-mode ops table (Linux 7.1 mld/mld.c iwl_mld_ops). A bounded */
/* subset of the full ops; fields are real entry points, not stubs. */
/* ------------------------------------------------------------------ */
struct rb_iwl_mld_ops {
int opmode;
const char *name;
int device_family_bz;
/* Monotonic count of MLD notifications routed by type, for
* redbear-info / diagnostics. */
uint32_t rx_frames;
uint32_t rx_ba_notifs;
uint32_t rx_no_data;
uint32_t rx_tx_responses;
uint32_t rx_mld_specific;
uint32_t rx_unknown;
};
static struct rb_iwl_mld_ops rb_mld = {
.opmode = RB_IWL_OPMODE_MLD,
.name = "iwlmld",
.device_family_bz = 1,
.rx_frames = 0,
.rx_ba_notifs = 0,
.rx_no_data = 0,
.rx_tx_responses = 0,
.rx_mld_specific = 0,
.rx_unknown = 0,
};
/* Decoded result of one MLD notification (mirrors rb_iwl_mvm_rx_info). */
struct rb_iwl_mld_notif_result {
int handled; /* 1 if the notification type was recognized */
int is_rx_frame; /* 1 if this carried an 802.11 frame (MPDU) */
int signal; /* dBm, valid when is_rx_frame */
int rate_mcs; /* valid when is_rx_frame */
uint8_t group; /* command group the notification routed to */
uint8_t cmd_id; /* command ID within the group */
const char *kind; /* human-readable notification kind */
};
/* Map a (group, cmd_id) to its command group for routing. Most MLD
* notifications carry their group implicitly by ID range; the legacy RX
* frame IDs (0xc0-0xc7, 0x1c, 0xa2, 0xb1, 0xb2, 0xdd) are legacy-group. */
static uint8_t rb_iwl_mld_group_for_cmd(uint8_t cmd_id)
{
switch (cmd_id) {
case RB_IWL_MLD_ROC_NOTIF:
case RB_IWL_MLD_CHANNEL_SWITCH_ERROR_NOTIF:
case RB_IWL_MLD_SESSION_PROTECTION_NOTIF:
case RB_IWL_MLD_PROBE_RESPONSE_DATA_NOTIF:
case RB_IWL_MLD_CHANNEL_SWITCH_START_NOTIF:
return RB_IWL_MLD_MAC_CONF_GROUP;
case RB_IWL_MLD_MU_GROUP_MGMT_NOTIF:
return RB_IWL_MLD_DATA_PATH_GROUP;
default:
return RB_IWL_MLD_LEGACY_GROUP;
}
}
static const char *rb_iwl_mld_kind(uint8_t cmd_id)
{
switch (cmd_id) {
case RB_IWL_MLD_RX_PHY_CMD: return "rx-phy";
case RB_IWL_MLD_RX_MPDU_CMD: return "rx-mpdu";
case RB_IWL_MLD_BA_NOTIF: return "ba-notif";
case RB_IWL_MLD_RX_NO_DATA: return "rx-no-data";
case RB_IWL_MLD_TX_CMD: return "tx-resp";
case RB_IWL_MLD_MISSED_BEACONS: return "missed-beacons";
case RB_IWL_MLD_SCAN_START_UMAC: return "scan-start";
case RB_IWL_MLD_ROC_NOTIF: return "roc";
case RB_IWL_MLD_CHANNEL_SWITCH_ERROR_NOTIF: return "channel-switch-error";
case RB_IWL_MLD_SESSION_PROTECTION_NOTIF: return "session-protection";
case RB_IWL_MLD_PROBE_RESPONSE_DATA_NOTIF: return "probe-response-data";
case RB_IWL_MLD_MU_GROUP_MGMT_NOTIF: return "mu-group-mgmt";
case RB_IWL_MLD_MFUART_LOAD_NOTIF: return "mfuart-load";
case RB_IWL_MLD_DTS_MEASUREMENT_NOTIF: return "dts-measurement";
/* 0xFF is shared: channel-switch-start (MAC_CONF) and dts-measurement-wide
* (PHY_OPS). The bounded dispatch keys on cmd_id alone and labels it
* channel-switch-start; the full layer disambiguates by group. */
case RB_IWL_MLD_CHANNEL_SWITCH_START_NOTIF: return "channel-switch-start";
default: return "unknown";
}
}
/* ------------------------------------------------------------------ */
/* Core MLD notification dispatch. Given one notification packet */
/* (cmd_id + payload), route it: RX frames go through the Mini-MVM */
/* descriptor parser (real signal/rate extraction); MLD-specific */
/* notifications are identified and counted. Returns a decoded result */
/* the transport surfaces to netctl / wifictl. */
/* ------------------------------------------------------------------ */
void rb_iwl_mld_handle_notification(uint8_t cmd_id,
const uint8_t *payload, size_t len,
struct rb_iwl_mld_notif_result *out)
{
out->handled = 1;
out->is_rx_frame = 0;
out->signal = 0;
out->rate_mcs = 0;
out->group = rb_iwl_mld_group_for_cmd(cmd_id);
out->cmd_id = cmd_id;
out->kind = rb_iwl_mld_kind(cmd_id);
switch (cmd_id) {
case RB_IWL_MLD_RX_MPDU_CMD: {
struct rb_iwl_mvm_rx_info info;
int desc_fmt = rb_iwl_mvm_detect_format(payload, len);
rb_iwl_mvm_extract_signal(payload, len, desc_fmt, &info);
out->is_rx_frame = 1;
out->signal = info.signal;
out->rate_mcs = rb_iwl_mvm_rate_to_mcs(info.signal);
rb_mld.rx_frames++;
break;
}
case RB_IWL_MLD_RX_PHY_CMD:
rb_mld.rx_frames++;
break;
case RB_IWL_MLD_BA_NOTIF:
rb_mld.rx_ba_notifs++;
break;
case RB_IWL_MLD_RX_NO_DATA:
rb_mld.rx_no_data++;
break;
case RB_IWL_MLD_TX_CMD:
rb_mld.rx_tx_responses++;
break;
case RB_IWL_MLD_ROC_NOTIF:
case RB_IWL_MLD_CHANNEL_SWITCH_ERROR_NOTIF:
case RB_IWL_MLD_SESSION_PROTECTION_NOTIF:
case RB_IWL_MLD_PROBE_RESPONSE_DATA_NOTIF:
case RB_IWL_MLD_CHANNEL_SWITCH_START_NOTIF:
case RB_IWL_MLD_MU_GROUP_MGMT_NOTIF:
case RB_IWL_MLD_MFUART_LOAD_NOTIF:
case RB_IWL_MLD_DTS_MEASUREMENT_NOTIF:
case RB_IWL_MLD_MISSED_BEACONS:
case RB_IWL_MLD_SCAN_START_UMAC:
rb_mld.rx_mld_specific++;
break;
default:
out->handled = 0;
rb_mld.rx_unknown++;
break;
}
}
/* Op-mode accessors used by linux_port.c for diagnostics. */
const char *rb_iwl_mld_name(void)
{
return rb_mld.name;
}
uint32_t rb_iwl_mld_rx_frames(void)
{
return rb_mld.rx_frames;
}
@@ -0,0 +1,90 @@
#ifndef RB_IWLWIFI_MLD_H
#define RB_IWLWIFI_MLD_H
/*
* Red Bear iwlwifi Mini-MLD (MAC Link-layer Driver) layer.
*
* Ported from Linux 7.1:
* drivers/net/wireless/intel/iwlwifi/mld/mld.c — op-mode registration
* drivers/net/wireless/intel/iwlwifi/mld/notif.c — RX_HANDLER table
* drivers/net/wireless/intel/iwlwifi/fw/api/commands.h — group + command IDs
* drivers/net/wireless/intel/iwlwifi/iwl-drv.c — BZ -> MLD op-mode gate
*
* This is a bounded Mini-MLD, the MLD analog of Mini-MVM. The RX frame path
* (REPLY_RX_PHY_CMD/REPLY_RX_MPDU_CMD/BA_NOTIF and iwl_rx_mpdu_desc) is shared
* with MVM and is parsed by the Mini-MVM layer (no duplicate parser). Mini-MLD
* adds: the iwlmld op-mode registration, the MLD-specific notification set
* (ROC, session protection, MU group mgmt, probe response, channel switch,
* MFUART, DTS measurement), and the BZ-family -> MLD op-mode gate.
*/
#include <stdint.h>
#include <stddef.h>
/* ------------------------------------------------------------------ */
/* Op-mode IDs (Linux 7.1 iwl-drv.c): the transport selects the */
/* op-mode by device family. BZ (Wi-Fi 7, BE201/BE200) -> iwlmld; */
/* everything older -> iwlmvm. */
/* ------------------------------------------------------------------ */
#define RB_IWL_OPMODE_MVM 0
#define RB_IWL_OPMODE_MLD 1
/* Select the op-mode for a device family. Mirrors Linux iwl-drv.c:
* `device_family >= IWL_DEVICE_FAMILY_BZ` selects iwlmld. */
static inline int rb_iwl_opmode_for_family(uint32_t device_family_bz)
{
return device_family_bz ? RB_IWL_OPMODE_MLD : RB_IWL_OPMODE_MVM;
}
static inline const char *rb_iwl_opmode_name(int opmode)
{
switch (opmode) {
case RB_IWL_OPMODE_MLD: return "iwlmld";
case RB_IWL_OPMODE_MVM:
default: return "iwlmvm";
}
}
/* ------------------------------------------------------------------ */
/* Notification command groups (Linux 7.1 fw/api/commands.h). */
/* ------------------------------------------------------------------ */
#define RB_IWL_MLD_LEGACY_GROUP 0x0
#define RB_IWL_MLD_MAC_CONF_GROUP 0x3
#define RB_IWL_MLD_PHY_OPS_GROUP 0x4
#define RB_IWL_MLD_DATA_PATH_GROUP 0x5
#define RB_IWL_MLD_SCAN_GROUP 0x6
#define RB_IWL_MLD_STATISTICS_GROUP 0x10
/* ------------------------------------------------------------------ */
/* Shared RX frame command IDs (identical to Mini-MVM — the firmware */
/* RX descriptor and these IDs are common to MVM and MLD). */
/* ------------------------------------------------------------------ */
#define RB_IWL_MLD_RX_PHY_CMD 0xc0 /* &struct iwl_rx_phy_info */
#define RB_IWL_MLD_RX_MPDU_CMD 0xc1 /* iwl_rx_mpdu_desc + 802.11 frame */
#define RB_IWL_MLD_BA_NOTIF 0xc5 /* block-ack notification */
#define RB_IWL_MLD_RX_NO_DATA 0xc7 /* &struct iwl_rx_no_data */
#define RB_IWL_MLD_TX_CMD 0x1c /* TX response (iwl_tx_cmd_v6/v9) */
#define RB_IWL_MLD_MISSED_BEACONS 0xa2 /* &struct iwl_missed_beacons_notif_v4 */
#define RB_IWL_MLD_SCAN_START_UMAC 0xb2 /* &struct iwl_umac_scan_start */
/* ------------------------------------------------------------------ */
/* MLD-specific notification command IDs. */
/* MAC_CONF group (fw/api/mac-cfg.h) */
/* ------------------------------------------------------------------ */
#define RB_IWL_MLD_ROC_NOTIF 0xF8
#define RB_IWL_MLD_CHANNEL_SWITCH_ERROR_NOTIF 0xF9
#define RB_IWL_MLD_SESSION_PROTECTION_NOTIF 0xFB
#define RB_IWL_MLD_PROBE_RESPONSE_DATA_NOTIF 0xFC
#define RB_IWL_MLD_CHANNEL_SWITCH_START_NOTIF 0xFF
/* DATA_PATH group (fw/api/datapath.h) */
#define RB_IWL_MLD_MU_GROUP_MGMT_NOTIF 0xFE
/* PHY_OPS group (fw/api/phy.h) */
#define RB_IWL_MLD_DTS_MEASUREMENT_NOTIF_WIDE 0xFF
/* Legacy group (fw/api/commands.h, debug.h) */
#define RB_IWL_MLD_MFUART_LOAD_NOTIF 0xb1
#define RB_IWL_MLD_DTS_MEASUREMENT_NOTIF 0xdd
#endif /* RB_IWLWIFI_MLD_H */
@@ -9,6 +9,7 @@
#include "../../../linux-kpi/source/src/c_headers/linux/jiffies.h"
#include "../../../linux-kpi/source/src/c_headers/linux/kernel.h"
#include "linux_mvm.h"
#include "linux_mld.h"
#include "../../../linux-kpi/source/src/c_headers/linux/list.h"
#include "../../../linux-kpi/source/src/c_headers/linux/mutex.h"
#include "../../../linux-kpi/source/src/c_headers/linux/netdevice.h"
@@ -267,6 +268,7 @@ struct iwl_trans_pcie {
/* Device family info */
int device_family;
int opmode;
const char *fw_name;
const char *pnvm_name;
@@ -1053,6 +1055,7 @@ static int rb_iwlwifi_probe_transport(struct iwl_trans_pcie *trans, unsigned int
}
trans->device_family = rb_iwlwifi_family_from_device(trans->pci_dev, bz_family);
trans->opmode = rb_iwl_opmode_for_family(trans->device_family == RB_IWL_DEVICE_FAMILY_BZ);
access_req = trans->device_family == RB_IWL_DEVICE_FAMILY_BZ ?
IWL_CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ :
IWL_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ;
@@ -2170,6 +2173,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
pci_set_master(pdev);
trans->device_family = rb_iwlwifi_family_from_device(pdev, 0);
trans->opmode = rb_iwl_opmode_for_family(trans->device_family == RB_IWL_DEVICE_FAMILY_BZ);
pdev->driver_data = trans;
return 0;
}
@@ -2215,8 +2219,9 @@ static void rb_iwlwifi_status_line(struct iwl_trans_pcie *trans, char *out, unsi
{
rb_iwlwifi_format_out(
out, out_len,
"linux_kpi_status=ok family=%s prepared=%d probed=%d init=%d active=%d fw_running=%d mac80211=%d irq=%d vectors=%d msix=%d tx_queues=%d rx_in_use=%u scan_results=%u connected=%d ssid=%s",
"linux_kpi_status=ok family=%s opmode=%s prepared=%d probed=%d init=%d active=%d fw_running=%d mac80211=%d irq=%d vectors=%d msix=%d tx_queues=%d rx_in_use=%u scan_results=%u connected=%d ssid=%s",
rb_iwlwifi_family_name(trans->device_family),
rb_iwl_opmode_name(trans->opmode),
trans->prepared,
trans->transport_probed,
trans->transport_inited,