wifi: iwlwifi: mld: decode VHT information for sniffer

The available VHT information may be useful, so decode it and include it
in the generated radiotap headers.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20251110150012.6751d1d0b31d.I927cb0767667f2c03ee41f2ba417f3b94bba6d91@changeid
This commit is contained in:
Benjamin Berg 2025-11-10 15:02:12 +02:00 committed by Miri Korenblit
parent b47b3d0d8b
commit 4bfdbd5d1e
2 changed files with 155 additions and 1 deletions

View File

@ -1062,7 +1062,29 @@ struct iwl_vht_sigs {
#define OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM 0x000007ff
#define OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM_VALID 0x80000000
__le32 a0;
__le32 a1, a2;
#define OFDM_RX_FRAME_VHT_BANDWIDTH 0x00000003
#define OFDM_RX_FRAME_VHT_STBC 0x00000008
#define OFDM_RX_FRAME_VHT_GRP_ID 0x000003f0
#define OFDM_RX_FRAME_VHT_STS_USER0 0x00001c00
#define OFDM_RX_FRAME_VHT_MU_STS_USER1 0x0000e000
#define OFDM_RX_FRAME_VHT_MU_STS_USER2 0x00070000
#define OFDM_RX_FRAME_VHT_MU_STS_USER3 0x00380000
#define OFDM_RX_FRAME_VHT_PARTIAL_AID_OR_MU_STS 0x003fe000
#define OFDM_RX_FRAME_VHT_MU_MIMO_USER_POSITION 0x03000000
#define OFDM_RX_FRAME_VHT_NO_STREAMS 0x04000000
#define OFDM_RX_FRAME_VHT_STS 0x38000000
__le32 a1;
#define OFDM_RX_FRAME_VHT_SHORT_GI 0x00000001
#define OFDM_RX_FRAME_VHT_SHORT_GI_AMBIG 0x00000002
#define OFDM_RX_FRAME_VHT_CODING 0x00000004
#define OFDM_RX_FRAME_VHT_CODING_EXTRA_SYM 0x00000008
#define OFDM_RX_FRAME_VHT_MCS_OR_MU_CODING 0x000000f0
#define OFDM_RX_FRAME_VHT_BF_OR_MU_RESERVED 0x00000100
#define OFDM_RX_FRAME_VHT_CRC 0x0003fc00
#define OFDM_RX_FRAME_VHT_CRC_OK_BIT 0x00040000
#define OFDM_RX_FRAME_VHT_CUR_USER_CODING 0x00080000
#define OFDM_RX_FRAME_VHT_CUR_USER_STS 0x00700000
__le32 a2;
};
struct iwl_he_sigs {

View File

@ -207,6 +207,134 @@ static void iwl_mld_fill_signal(struct iwl_mld *mld, int link_id,
rx_status->chain_signal[1] = energy_b;
}
static void
iwl_mld_decode_vht_phy_data(struct iwl_mld_rx_phy_data *phy_data,
struct ieee80211_radiotap_vht *vht,
struct ieee80211_rx_status *rx_status)
{
bool stbc;
vht->known = cpu_to_le16(IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH |
IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID |
IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
IEEE80211_RADIOTAP_VHT_KNOWN_GI |
IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS |
IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM |
IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED);
switch (le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_BANDWIDTH)) {
case 0:
vht->bandwidth = IEEE80211_RADIOTAP_VHT_BW_20;
break;
case 1:
vht->bandwidth = IEEE80211_RADIOTAP_VHT_BW_40;
break;
case 2:
vht->bandwidth = IEEE80211_RADIOTAP_VHT_BW_80;
break;
case 3:
vht->bandwidth = IEEE80211_RADIOTAP_VHT_BW_160;
break;
}
vht->group_id = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_GRP_ID);
stbc = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_STBC);
if (stbc)
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
if (le32_get_bits(phy_data->ntfy->sigs.vht.a2,
OFDM_RX_FRAME_VHT_SHORT_GI))
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
if (le32_get_bits(phy_data->ntfy->sigs.vht.a2,
OFDM_RX_FRAME_VHT_SHORT_GI_AMBIG))
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9;
if (le32_get_bits(phy_data->ntfy->sigs.vht.a2,
OFDM_RX_FRAME_VHT_CODING_EXTRA_SYM))
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM;
if (vht->group_id != 0 && vht->group_id != 63) {
/* MU frame */
int user = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_MU_MIMO_USER_POSITION);
int nsts;
/* Always beamformed */
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
/* No MCS information in the a1/a2 data for MU frames */
nsts = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_STS_USER0);
vht->mcs_nss[0] = (stbc ? nsts / 2 : nsts) | 0xf0;
nsts = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_MU_STS_USER1);
vht->mcs_nss[1] = (stbc ? nsts / 2 : nsts) | 0xf0;
nsts = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_MU_STS_USER2);
vht->mcs_nss[2] = (stbc ? nsts / 2 : nsts) | 0xf0;
nsts = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_MU_STS_USER3);
vht->mcs_nss[3] = (stbc ? nsts / 2 : nsts) | 0xf0;
/* Report current user MCS from rate_n_flags via rx_status */
vht->mcs_nss[user] &= 0x0f;
vht->mcs_nss[user] |= rx_status->rate_idx << 4;
/* Report LDPC for current user */
if (rx_status->enc_flags & RX_ENC_FLAG_LDPC)
vht->coding = 0x1 << user;
} else {
int nsts;
/* SU frame */
vht->known |= cpu_to_le16(IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID);
if (le32_get_bits(phy_data->ntfy->sigs.vht.a2,
OFDM_RX_FRAME_VHT_BF_OR_MU_RESERVED))
vht->flags |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
vht->partial_aid =
cpu_to_le16(le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_PARTIAL_AID_OR_MU_STS));
nsts = le32_get_bits(phy_data->ntfy->sigs.vht.a1,
OFDM_RX_FRAME_VHT_STS) + 1;
vht->mcs_nss[0] =
(stbc ? nsts / 2 : nsts) |
le32_get_bits(phy_data->ntfy->sigs.vht.a2,
OFDM_RX_FRAME_VHT_MCS_OR_MU_CODING) << 4;
vht->mcs_nss[1] = 0;
vht->mcs_nss[2] = 0;
vht->mcs_nss[3] = 0;
if (rx_status->enc_flags & RX_ENC_FLAG_LDPC)
vht->coding = 0x1;
}
}
static void iwl_mld_rx_vht(struct sk_buff *skb,
struct iwl_mld_rx_phy_data *phy_data)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vht *vht;
if (likely(!phy_data->ntfy))
return;
vht = skb_put_zero(skb, sizeof(*vht));
rx_status->flag |= RX_FLAG_RADIOTAP_VHT;
iwl_mld_decode_vht_phy_data(phy_data, vht, rx_status);
}
static void
iwl_mld_he_set_ru_alloc(struct ieee80211_rx_status *rx_status,
struct ieee80211_radiotap_he *he,
@ -1377,6 +1505,10 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
iwl_mld_set_rx_rate(mld, phy_data, rx_status);
/* must be before HE data (radiotap field order) */
if (format == RATE_MCS_MOD_TYPE_VHT)
iwl_mld_rx_vht(skb, phy_data);
/* must be before L-SIG data (radiotap field order) */
if (format == RATE_MCS_MOD_TYPE_HE)
iwl_mld_rx_he(skb, phy_data);