Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: add RX flag to report radiotap VHT information

mac80211 already reports some basic information in the radiotap header
with the known fields declared by the driver. However, drivers may want
to report more accurate information and in that case the full VHT
radiotap structure needs to be provided.

Add a new RX_FLAG_RADIOTAP_VHT which is set when the VHT information
should be pulled from the skb. Update the code to fill in the VHT fields
to only do so when requested by the driver or if the information has not
yet been set. This way the driver can fully control the information if
it chooses so.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20251027142118.0bad1c307a21.I2cf285c20a822698039603f2af00ed9c548f2ee0@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Benjamin Berg and committed by
Johannes Berg
db82ddea 0a119c68

+89 -37
+19 -1
include/net/ieee80211_radiotap.h
··· 1 1 /* 2 2 * Copyright (c) 2017 Intel Deutschland GmbH 3 - * Copyright (c) 2018-2019, 2021-2022 Intel Corporation 3 + * Copyright (c) 2018-2019, 2021-2022, 2025 Intel Corporation 4 4 * 5 5 * Permission to use, copy, modify, and/or distribute this software for any 6 6 * purpose with or without fee is hereby granted, provided that the above ··· 201 201 IEEE80211_RADIOTAP_CODING_LDPC_USER2 = 0x04, 202 202 IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08, 203 203 }; 204 + 205 + enum ieee80211_radiotap_vht_bandwidth { 206 + /* Note: more values are defined but can't really be used */ 207 + IEEE80211_RADIOTAP_VHT_BW_20 = 0, 208 + IEEE80211_RADIOTAP_VHT_BW_40 = 1, 209 + IEEE80211_RADIOTAP_VHT_BW_80 = 4, 210 + IEEE80211_RADIOTAP_VHT_BW_160 = 11, 211 + }; 212 + 213 + struct ieee80211_radiotap_vht { 214 + __le16 known; 215 + u8 flags; 216 + u8 bandwidth; 217 + u8 mcs_nss[4]; 218 + u8 coding; 219 + u8 group_id; 220 + __le16 partial_aid; 221 + } __packed; 204 222 205 223 /* for IEEE80211_RADIOTAP_TIMESTAMP */ 206 224 enum ieee80211_radiotap_timestamp_unit_spos {
+2
include/net/mac80211.h
··· 1529 1529 * known the frame shouldn't be reported. 1530 1530 * @RX_FLAG_8023: the frame has an 802.3 header (decap offload performed by 1531 1531 * hardware or driver) 1532 + * @RX_FLAG_RADIOTAP_VHT: VHT radiotap data is present 1532 1533 */ 1533 1534 enum mac80211_rx_flags { 1534 1535 RX_FLAG_MMIC_ERROR = BIT(0), ··· 1565 1564 RX_FLAG_RADIOTAP_LSIG = BIT(28), 1566 1565 RX_FLAG_NO_PSDU = BIT(29), 1567 1566 RX_FLAG_8023 = BIT(30), 1567 + RX_FLAG_RADIOTAP_VHT = BIT(31), 1568 1568 }; 1569 1569 1570 1570 /**
+68 -36
net/mac80211/rx.c
··· 59 59 status->flag &= ~(RX_FLAG_RADIOTAP_TLV_AT_END | 60 60 RX_FLAG_RADIOTAP_LSIG | 61 61 RX_FLAG_RADIOTAP_HE_MU | 62 - RX_FLAG_RADIOTAP_HE); 62 + RX_FLAG_RADIOTAP_HE | 63 + RX_FLAG_RADIOTAP_VHT); 63 64 64 65 hdr = (void *)skb->data; 65 66 fc = hdr->frame_control; ··· 152 151 } 153 152 154 153 if (status->encoding == RX_ENC_VHT) { 154 + /* Included even if RX_FLAG_RADIOTAP_VHT is not set */ 155 155 len = ALIGN(len, 2); 156 156 len += 12; 157 + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_vht) != 12); 157 158 } 158 159 159 160 if (local->hw.radiotap_timestamp.units_pos >= 0) { ··· 198 195 * The position to look at depends on the existence (or non- 199 196 * existence) of other elements, so take that into account... 200 197 */ 198 + if (status->flag & RX_FLAG_RADIOTAP_VHT) 199 + tlv_offset += 200 + sizeof(struct ieee80211_radiotap_vht); 201 201 if (status->flag & RX_FLAG_RADIOTAP_HE) 202 202 tlv_offset += 203 203 sizeof(struct ieee80211_radiotap_he); ··· 325 319 u32 tlvs_len = 0; 326 320 int mpdulen, chain; 327 321 unsigned long chains = status->chains; 322 + struct ieee80211_radiotap_vht vht = {}; 328 323 struct ieee80211_radiotap_he he = {}; 329 324 struct ieee80211_radiotap_he_mu he_mu = {}; 330 325 struct ieee80211_radiotap_lsig lsig = {}; 326 + 327 + if (status->flag & RX_FLAG_RADIOTAP_VHT) { 328 + vht = *(struct ieee80211_radiotap_vht *)skb->data; 329 + skb_pull(skb, sizeof(vht)); 330 + WARN_ON_ONCE(status->encoding != RX_ENC_VHT); 331 + } 331 332 332 333 if (status->flag & RX_FLAG_RADIOTAP_HE) { 333 334 he = *(struct ieee80211_radiotap_he *)skb->data; ··· 543 530 } 544 531 545 532 if (status->encoding == RX_ENC_VHT) { 546 - u16 known = local->hw.radiotap_vht_details; 533 + u16 fill = local->hw.radiotap_vht_details; 547 534 548 - rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_VHT)); 549 - put_unaligned_le16(known, pos); 550 - pos += 2; 551 - /* flags */ 552 - if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 553 - *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; 535 + /* Leave driver filled fields alone */ 536 + fill &= ~le16_to_cpu(vht.known); 537 + vht.known |= cpu_to_le16(fill); 538 + 539 + if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_GI && 540 + status->enc_flags & RX_ENC_FLAG_SHORT_GI) 541 + vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; 554 542 /* in VHT, STBC is binary */ 555 - if (status->enc_flags & RX_ENC_FLAG_STBC_MASK) 556 - *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; 557 - if (status->enc_flags & RX_ENC_FLAG_BF) 543 + if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_STBC && 544 + status->enc_flags & RX_ENC_FLAG_STBC_MASK) 545 + vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; 546 + if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED && 547 + status->enc_flags & RX_ENC_FLAG_BF) 558 548 *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; 559 - pos++; 560 - /* bandwidth */ 561 - switch (status->bw) { 562 - case RATE_INFO_BW_80: 563 - *pos++ = 4; 564 - break; 565 - case RATE_INFO_BW_160: 566 - *pos++ = 11; 567 - break; 568 - case RATE_INFO_BW_40: 569 - *pos++ = 1; 570 - break; 571 - default: 572 - *pos++ = 0; 549 + 550 + if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) { 551 + switch (status->bw) { 552 + case RATE_INFO_BW_40: 553 + vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_40; 554 + break; 555 + case RATE_INFO_BW_80: 556 + vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_80; 557 + break; 558 + case RATE_INFO_BW_160: 559 + vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_160; 560 + break; 561 + default: 562 + vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_20; 563 + break; 564 + } 573 565 } 574 - /* MCS/NSS */ 575 - *pos = (status->rate_idx << 4) | status->nss; 576 - pos += 4; 577 - /* coding field */ 578 - if (status->enc_flags & RX_ENC_FLAG_LDPC) 579 - *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; 580 - pos++; 581 - /* group ID */ 582 - pos++; 583 - /* partial_aid */ 584 - pos += 2; 566 + 567 + /* 568 + * If the driver filled in mcs_nss[0], then do not touch it. 569 + * 570 + * Otherwise, put some information about MCS/NSS into the 571 + * user 0 field. Note that this is not technically correct for 572 + * an MU frame as we might have decoded a different user. 573 + */ 574 + if (!vht.mcs_nss[0]) { 575 + vht.mcs_nss[0] = (status->rate_idx << 4) | status->nss; 576 + 577 + /* coding field */ 578 + if (status->enc_flags & RX_ENC_FLAG_LDPC) 579 + vht.coding |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; 580 + } 581 + 582 + /* ensure 2 byte alignment */ 583 + while ((pos - (u8 *)rthdr) & 1) 584 + pos++; 585 + rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_VHT)); 586 + memcpy(pos, &vht, sizeof(vht)); 587 + pos += sizeof(vht); 585 588 } 586 589 587 590 if (local->hw.radiotap_timestamp.units_pos >= 0) { ··· 862 833 dev_kfree_skb(origskb); 863 834 return NULL; 864 835 } 836 + 837 + if (status->flag & RX_FLAG_RADIOTAP_VHT) 838 + rtap_space += sizeof(struct ieee80211_radiotap_vht); 865 839 866 840 if (status->flag & RX_FLAG_RADIOTAP_HE) 867 841 rtap_space += sizeof(struct ieee80211_radiotap_he);