]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: iwlwifi: mld: decode VHT information for sniffer
authorBenjamin Berg <benjamin.berg@intel.com>
Mon, 10 Nov 2025 13:02:12 +0000 (15:02 +0200)
committerMiri Korenblit <miriam.rachel.korenblit@intel.com>
Wed, 21 Jan 2026 12:23:01 +0000 (14:23 +0200)
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
drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
drivers/net/wireless/intel/iwlwifi/mld/rx.c

index 3ed7e0807b90bfbb96cd7ec323e75c0ce4c8252d..6d0523497663880418a47bccba82bbe1282db398 100644 (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 {
index 6a76e3fcb581c008b59ae75fb8f3a76b3a5e3d98..4386e6b5c37cb6ab2d3ad71a2976c31b36ffb62b 100644 (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);