summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/net/ieee80211_radiotap.h20
-rw-r--r--include/net/mac80211.h2
-rw-r--r--net/mac80211/rx.c104
3 files changed, 89 insertions, 37 deletions
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index 813e163ce27c..c60867e7e43c 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2017 Intel Deutschland GmbH
- * Copyright (c) 2018-2019, 2021-2022 Intel Corporation
+ * Copyright (c) 2018-2019, 2021-2022, 2025 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -202,6 +202,24 @@ enum ieee80211_radiotap_vht_coding {
IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08,
};
+enum ieee80211_radiotap_vht_bandwidth {
+ /* Note: more values are defined but can't really be used */
+ IEEE80211_RADIOTAP_VHT_BW_20 = 0,
+ IEEE80211_RADIOTAP_VHT_BW_40 = 1,
+ IEEE80211_RADIOTAP_VHT_BW_80 = 4,
+ IEEE80211_RADIOTAP_VHT_BW_160 = 11,
+};
+
+struct ieee80211_radiotap_vht {
+ __le16 known;
+ u8 flags;
+ u8 bandwidth;
+ u8 mcs_nss[4];
+ u8 coding;
+ u8 group_id;
+ __le16 partial_aid;
+} __packed;
+
/* for IEEE80211_RADIOTAP_TIMESTAMP */
enum ieee80211_radiotap_timestamp_unit_spos {
IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK = 0x000F,
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a55085cf4ec4..c326243e1f01 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1529,6 +1529,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* known the frame shouldn't be reported.
* @RX_FLAG_8023: the frame has an 802.3 header (decap offload performed by
* hardware or driver)
+ * @RX_FLAG_RADIOTAP_VHT: VHT radiotap data is present
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -1564,6 +1565,7 @@ enum mac80211_rx_flags {
RX_FLAG_RADIOTAP_LSIG = BIT(28),
RX_FLAG_NO_PSDU = BIT(29),
RX_FLAG_8023 = BIT(30),
+ RX_FLAG_RADIOTAP_VHT = BIT(31),
};
/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4641a2a80856..b59aeed340b3 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -59,7 +59,8 @@ static struct sk_buff *ieee80211_clean_skb(struct sk_buff *skb,
status->flag &= ~(RX_FLAG_RADIOTAP_TLV_AT_END |
RX_FLAG_RADIOTAP_LSIG |
RX_FLAG_RADIOTAP_HE_MU |
- RX_FLAG_RADIOTAP_HE);
+ RX_FLAG_RADIOTAP_HE |
+ RX_FLAG_RADIOTAP_VHT);
hdr = (void *)skb->data;
fc = hdr->frame_control;
@@ -151,8 +152,10 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
}
if (status->encoding == RX_ENC_VHT) {
+ /* Included even if RX_FLAG_RADIOTAP_VHT is not set */
len = ALIGN(len, 2);
len += 12;
+ BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_vht) != 12);
}
if (local->hw.radiotap_timestamp.units_pos >= 0) {
@@ -195,6 +198,9 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
* The position to look at depends on the existence (or non-
* existence) of other elements, so take that into account...
*/
+ if (status->flag & RX_FLAG_RADIOTAP_VHT)
+ tlv_offset +=
+ sizeof(struct ieee80211_radiotap_vht);
if (status->flag & RX_FLAG_RADIOTAP_HE)
tlv_offset +=
sizeof(struct ieee80211_radiotap_he);
@@ -319,10 +325,17 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
u32 tlvs_len = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
+ struct ieee80211_radiotap_vht vht = {};
struct ieee80211_radiotap_he he = {};
struct ieee80211_radiotap_he_mu he_mu = {};
struct ieee80211_radiotap_lsig lsig = {};
+ if (status->flag & RX_FLAG_RADIOTAP_VHT) {
+ vht = *(struct ieee80211_radiotap_vht *)skb->data;
+ skb_pull(skb, sizeof(vht));
+ WARN_ON_ONCE(status->encoding != RX_ENC_VHT);
+ }
+
if (status->flag & RX_FLAG_RADIOTAP_HE) {
he = *(struct ieee80211_radiotap_he *)skb->data;
skb_pull(skb, sizeof(he));
@@ -530,45 +543,61 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
}
if (status->encoding == RX_ENC_VHT) {
- u16 known = local->hw.radiotap_vht_details;
+ u16 fill = local->hw.radiotap_vht_details;
- rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_VHT));
- put_unaligned_le16(known, pos);
- pos += 2;
- /* flags */
- if (status->enc_flags & RX_ENC_FLAG_SHORT_GI)
- *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ /* Leave driver filled fields alone */
+ fill &= ~le16_to_cpu(vht.known);
+ vht.known |= cpu_to_le16(fill);
+
+ if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_GI &&
+ status->enc_flags & RX_ENC_FLAG_SHORT_GI)
+ vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
/* in VHT, STBC is binary */
- if (status->enc_flags & RX_ENC_FLAG_STBC_MASK)
- *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
- if (status->enc_flags & RX_ENC_FLAG_BF)
+ if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_STBC &&
+ status->enc_flags & RX_ENC_FLAG_STBC_MASK)
+ vht.flags |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+ if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED &&
+ status->enc_flags & RX_ENC_FLAG_BF)
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED;
- pos++;
- /* bandwidth */
- switch (status->bw) {
- case RATE_INFO_BW_80:
- *pos++ = 4;
- break;
- case RATE_INFO_BW_160:
- *pos++ = 11;
- break;
- case RATE_INFO_BW_40:
- *pos++ = 1;
- break;
- default:
- *pos++ = 0;
+
+ if (fill & IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) {
+ switch (status->bw) {
+ case RATE_INFO_BW_40:
+ vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_40;
+ break;
+ case RATE_INFO_BW_80:
+ vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_80;
+ break;
+ case RATE_INFO_BW_160:
+ vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_160;
+ break;
+ default:
+ vht.bandwidth = IEEE80211_RADIOTAP_VHT_BW_20;
+ break;
+ }
}
- /* MCS/NSS */
- *pos = (status->rate_idx << 4) | status->nss;
- pos += 4;
- /* coding field */
- if (status->enc_flags & RX_ENC_FLAG_LDPC)
- *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
- pos++;
- /* group ID */
- pos++;
- /* partial_aid */
- pos += 2;
+
+ /*
+ * If the driver filled in mcs_nss[0], then do not touch it.
+ *
+ * Otherwise, put some information about MCS/NSS into the
+ * user 0 field. Note that this is not technically correct for
+ * an MU frame as we might have decoded a different user.
+ */
+ if (!vht.mcs_nss[0]) {
+ vht.mcs_nss[0] = (status->rate_idx << 4) | status->nss;
+
+ /* coding field */
+ if (status->enc_flags & RX_ENC_FLAG_LDPC)
+ vht.coding |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
+ }
+
+ /* ensure 2 byte alignment */
+ while ((pos - (u8 *)rthdr) & 1)
+ pos++;
+ rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_VHT));
+ memcpy(pos, &vht, sizeof(vht));
+ pos += sizeof(vht);
}
if (local->hw.radiotap_timestamp.units_pos >= 0) {
@@ -834,6 +863,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return NULL;
}
+ if (status->flag & RX_FLAG_RADIOTAP_VHT)
+ rtap_space += sizeof(struct ieee80211_radiotap_vht);
+
if (status->flag & RX_FLAG_RADIOTAP_HE)
rtap_space += sizeof(struct ieee80211_radiotap_he);