From 1a2494ceb09d0499025bd9f4b4372e7e1e7dc5a7 Mon Sep 17 00:00:00 2001 From: Jeff Chen Date: Tue, 28 Apr 2026 22:28:48 +0800 Subject: mmc: core: add NXP IW61x base ID and block size quirk The NXP IW61x series SDIO chipset identifies itself with a base card ID (0x0204) during the initial MMC bus scan, while the specific WLAN function reports a different ID (0x0205). To ensure that the MMC_QUIRK_BLKSZ_FOR_BYTE_MODE quirk is correctly inherited by all SDIO functions (including Wi-Fi), it must be attached to the base card ID at the core level. Add the SDIO_DEVICE_ID_NXP_IW61X_BASE definition and apply the required fixup in the SDIO quirk table. Signed-off-by: Jeff Chen --- include/linux/mmc/sdio_ids.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 0685dd717e85..7dac5428afe0 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -118,6 +118,7 @@ #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347 #define SDIO_VENDOR_ID_NXP 0x0471 +#define SDIO_DEVICE_ID_NXP_IW61X_BASE 0x0204 #define SDIO_DEVICE_ID_NXP_IW61X 0x0205 #define SDIO_VENDOR_ID_REALTEK 0x024c -- cgit v1.2.3 From 25bc29ba671d0496b599149a0823b5dbb5409f94 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 12 Apr 2026 15:26:09 +0300 Subject: wifi: radiotap: add definitions for the new UHR TLVs Add the necessary definitions to create radiotap UHR TLVs for UHR sniffers. Signed-off-by: Johannes Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260412152605.73e682d0c8c3.I5a0c858467c852b7a2a00f580bd073af29c37705@changeid Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 190 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) (limited to 'include') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index c60867e7e43c..8bbaf77da7cf 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -95,6 +95,8 @@ enum ieee80211_radiotap_presence { IEEE80211_RADIOTAP_EXT = 31, IEEE80211_RADIOTAP_EHT_USIG = 33, IEEE80211_RADIOTAP_EHT = 34, + IEEE80211_RADIOTAP_UHR_ELR = 37, + IEEE80211_RADIOTAP_UHR = 38, }; /* for IEEE80211_RADIOTAP_FLAGS */ @@ -602,6 +604,194 @@ enum ieee80211_radiotap_eht_usig_tb { IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL = 0xfc000000, }; +/* + * ieee80211_radiotap_uhr_elr - content of UHR-ELR TLV (type 37) + * see https://www.radiotap.org/fields/UHR-ELR for details + */ +struct ieee80211_radiotap_uhr_elr { + __le32 known; + __le32 sig1, sig2, mark; +} __packed; + +enum ieee80211_radiotap_uhr_elr_known { + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_VERSION_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_UL_DL = 0x00000002, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MCS = 0x00000004, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_CODING = 0x00000008, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LENGTH = 0x00000010, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x00000020, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC = 0x00000040, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_TAIL = 0x00000080, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_STA_ID = 0x00000100, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_DISREGARD = 0x00000200, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC = 0x00000400, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_TAIL = 0x00000800, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_1_CRC_CHECKED = 0x00001000, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_SIG_2_CRC_CHECKED = 0x00002000, + IEEE80211_RADIOTAP_UHR_ELR_KNOWN_MARK_BSS_COLOR = 0x00010000, +}; + +enum ieee80211_radiotap_uhr_elr_sig1 { + IEEE80211_RADIOTAP_UHR_ELR_SIG1_VERSION_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_UL_DL = 0x00000002, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_MCS = 0x00000004, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CODING = 0x00000008, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_LENGTH = 0x00001FF0, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_LDPC_EXTRA_OFDM_SYM = 0x00002000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC = 0x0003C000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_TAIL = 0x00FC0000, + IEEE80211_RADIOTAP_UHR_ELR_SIG1_CRC_VALID = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_elr_sig2 { + IEEE80211_RADIOTAP_UHR_ELR_SIG2_STA_ID = 0x000007FF, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_DISREGARD = 0x00003800, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC = 0x0003C000, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_TAIL = 0x00FC0000, + IEEE80211_RADIOTAP_UHR_ELR_SIG2_CRC_VALID = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_elr_mark { + IEEE80211_RADIOTAP_UHR_ELR_MARK_BSS_COLOR = 0x0000003F, +}; + +/* + * ieee80211_radiotap_uhr - content of UHR TLV (type 38) + * see https://www.radiotap.org/fields/UHR for details + */ +struct ieee80211_radiotap_uhr { + __le32 known; + __le32 data[9]; + struct { + __le32 known, info; + } user[]; +} __packed; + +enum ieee80211_radiotap_uhr_known { + IEEE80211_RADIOTAP_UHR_KNOWN_SPATIAL_REUSE = 0x00000001, + IEEE80211_RADIOTAP_UHR_KNOWN_GI_LTF_SIZE = 0x00000002, + IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_UHR_LTF_SYMBOLS = 0x00000004, + IEEE80211_RADIOTAP_UHR_KNOWN_LDPC_EXTRA_SYMBOL_SEGMENT = 0x00000008, + IEEE80211_RADIOTAP_UHR_KNOWN_PRE_FEC_PADDING_FACTOR = 0x00000010, + IEEE80211_RADIOTAP_UHR_KNOWN_PE_DISAMBIGUITY = 0x00000020, + IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_OFDMA = 0x00000040, + IEEE80211_RADIOTAP_UHR_KNOWN_CRC1 = 0x00000080, + IEEE80211_RADIOTAP_UHR_KNOWN_TAIL1 = 0x00000100, + IEEE80211_RADIOTAP_UHR_KNOWN_CRC2 = 0x00000200, + IEEE80211_RADIOTAP_UHR_KNOWN_TAIL2 = 0x00000400, + IEEE80211_RADIOTAP_UHR_KNOWN_INTERFERENCE_MITIGATION = 0x00000800, + IEEE80211_RADIOTAP_UHR_KNOWN_DISREGARD_NON_OFDMA = 0x00001000, + IEEE80211_RADIOTAP_UHR_KNOWN_NUMBER_OF_NON_OFDMA_USERS = 0x00002000, + IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_CRC = 0x00004000, + IEEE80211_RADIOTAP_UHR_KNOWN_COMMON_ENCODING_BLOCK_TAIL = 0x00008000, + IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_DRU_SIZE = 0x00010000, + IEEE80211_RADIOTAP_UHR_KNOWN_RU_MRU_INDEX = 0x00020000, + IEEE80211_RADIOTAP_UHR_KNOWN_DRU_RRU_ALLOC_TB_FMT = 0x00040000, + IEEE80211_RADIOTAP_UHR_KNOWN_PRI80_CHAN_POS = 0x00080000, +}; + +enum ieee80211_radiotap_uhr_data { + /* data[0] */ + IEEE80211_RADIOTAP_UHR_DATA0_SPATIAL_REUSE = 0x0000000F, + IEEE80211_RADIOTAP_UHR_DATA0_GI_LTF_SIZE = 0x00000030, + IEEE80211_RADIOTAP_UHR_DATA0_NUMBER_OF_LTF_SYMBOLS = 0x00000700, + IEEE80211_RADIOTAP_UHR_DATA0_LDPC_EXTRA_SYMBOL_SEGMENT = 0x00000800, + IEEE80211_RADIOTAP_UHR_DATA0_PRE_FEC_PADDING_FACTOR = 0x00003000, + IEEE80211_RADIOTAP_UHR_DATA0_PE_DISAMBIGUITY = 0x00004000, + IEEE80211_RADIOTAP_UHR_DATA0_DISREGARD_OFDMA = 0x00078000, + IEEE80211_RADIOTAP_UHR_DATA0_CRC1 = 0x00780000, + IEEE80211_RADIOTAP_UHR_DATA0_TAIL1 = 0x1f800000, + /* data[1] */ + IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_DRU_SIZE = 0x0000001f, + IEEE80211_RADIOTAP_UHR_DATA1_RU_MRU_INDEX = 0x00001fe0, + IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1 = 0x003fe000, + IEEE80211_RADIOTAP_UHR_DATA1_RU_ALLOC_CC_1_1_1_KNOWN = 0x00400000, + IEEE80211_RADIOTAP_UHR_DATA1_PRI80_CHAN_POS = 0xc0000000, + /* data[2] */ + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_1_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_1_1_2_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA2_RU_ALLOC_CC_2_1_2_KNOWN = 0x20000000, + /* data[3] */ + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_1_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_2_2_1_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA3_RU_ALLOC_CC_1_2_2_KNOWN = 0x20000000, + /* data[4] */ + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_2_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_1_2_3_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA4_RU_ALLOC_CC_2_2_3_KNOWN = 0x20000000, + /* data[5] */ + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_4_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_2_2_4_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA5_RU_ALLOC_CC_1_2_5_KNOWN = 0x20000000, + /* data[6] */ + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5 = 0x000001ff, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_5_KNOWN = 0x00000200, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6 = 0x0007fc00, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_1_2_6_KNOWN = 0x00080000, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6 = 0x1ff00000, + IEEE80211_RADIOTAP_UHR_DATA6_RU_ALLOC_CC_2_2_6_KNOWN = 0x20000000, + /* data[7] */ + IEEE80211_RADIOTAP_UHR_DATA7_CRC2 = 0x0000000f, + IEEE80211_RADIOTAP_UHR_DATA7_TAIL2 = 0x000003f0, + IEEE80211_RADIOTAP_UHR_DATA7_INTERFERENCE_MITIGATION = 0x00000400, + IEEE80211_RADIOTAP_UHR_DATA7_DISREGARD_NON_OFDMA = 0x00001800, + IEEE80211_RADIOTAP_UHR_DATA7_NUMBER_OF_NON_OFDMA_USERS = 0x0000e000, + IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_CRC = 0x000f0000, + IEEE80211_RADIOTAP_UHR_DATA7_COMMON_ENCODING_BLOCK_TAIL = 0x03f00000, + /* data[8] */ + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_PS_160= 0x00000001, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B0 = 0x00000002, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_ALLOC_TB_FMT_B7_B1 = 0x000001fc, + IEEE80211_RADIOTAP_UHR_DATA8_DRU_RRU_INDICATION = 0x00000200, +}; + +enum ieee80211_radiotap_uhr_user_known { + IEEE80211_RADIOTAP_UHR_USER_KNOWN_STA_ID = 0x00000001, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_MCS = 0x00000002, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_NSS = 0x00000004, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM = 0x00000008, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_BF = 0x00000010, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_CODING = 0x00000020, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_UEQM_PATTERN = 0x00000040, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_2X_LDPC = 0x00000080, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_SPATIAL_CONFIG = 0x00000100, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DISREGARD = 0x00000200, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_BSS_COLOR_INDICATION = 0x00000400, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_CRC = 0x00000800, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USR_ENC_BLK_TAIL = 0x00001000, + /* really 'known' but actual data */ + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_CRC = 0x000f0000, + IEEE80211_RADIOTAP_UHR_USER_KNOWN_DATA_USR_ENC_BLK_TAIL = 0x03f00000, + /* indicates this user was captured */ + IEEE80211_RADIOTAP_UHR_USER_KNOWN_USER_CAPTURED = 0x80000000, +}; + +enum ieee80211_radiotap_uhr_user_info { + IEEE80211_RADIOTAP_UHR_USER_INFO_STA_ID = 0x000007ff, + IEEE80211_RADIOTAP_UHR_USER_INFO_MCS = 0x0000f800, + IEEE80211_RADIOTAP_UHR_USER_INFO_NSS = 0x00070000, + IEEE80211_RADIOTAP_UHR_USER_INFO_SPATIAL_CONFIG = 0x000f0000, + IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM = 0x00100000, + IEEE80211_RADIOTAP_UHR_USER_INFO_DISREGARD = 0x00100000, + IEEE80211_RADIOTAP_UHR_USER_INFO_BF = 0x00200000, + IEEE80211_RADIOTAP_UHR_USER_INFO_BSS_COLOR_INDICATION = 0x00200000, + IEEE80211_RADIOTAP_UHR_USER_INFO_UEQM_PATTERN = 0x00c00000, + IEEE80211_RADIOTAP_UHR_USER_INFO_CODING = 0x01000000, + IEEE80211_RADIOTAP_UHR_USER_INFO_2X_LDPC = 0x02000000, +}; + /** * ieee80211_get_radiotap_len - get radiotap header length * @data: pointer to the header -- cgit v1.2.3 From 4ab9b637b94a3821acfcd6d6037dffe33669245a Mon Sep 17 00:00:00 2001 From: Priyansha Tiwari Date: Thu, 11 Jun 2026 11:52:22 +0530 Subject: wifi: nl80211/cfg80211: rename probe_client to probe_peer Rename NL80211_CMD_PROBE_CLIENT to NL80211_CMD_PROBE_PEER in the UAPI enum and retain NL80211_CMD_PROBE_CLIENT as a compatibility alias. Rename the .probe_client cfg80211_ops callback to .probe_peer and update all in-tree users (wil6210, mwifiex) and mac80211 so the tree continues to build after this change. Signed-off-by: Priyansha Tiwari Link: https://patch.msgid.link/20260611062225.2144241-2-pritiwa@qti.qualcomm.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 8 ++++---- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 8 ++++---- include/net/cfg80211.h | 6 +++--- include/uapi/linux/nl80211.h | 5 +++-- net/mac80211/cfg.c | 6 +++--- net/wireless/nl80211.c | 17 ++++++++--------- net/wireless/rdev-ops.h | 10 +++++----- net/wireless/trace.h | 2 +- 8 files changed, 31 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index d6ef92cfcbaf..a85ff2a4316b 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2379,9 +2379,9 @@ void wil_probe_client_flush(struct wil6210_vif *vif) mutex_unlock(&vif->probe_client_mutex); } -static int wil_cfg80211_probe_client(struct wiphy *wiphy, - struct net_device *dev, - const u8 *peer, u64 *cookie) +static int wil_cfg80211_probe_peer(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u64 *cookie) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wil6210_vif *vif = ndev_to_vif(dev); @@ -2660,7 +2660,7 @@ static const struct cfg80211_ops wil_cfg80211_ops = { .add_station = wil_cfg80211_add_station, .del_station = wil_cfg80211_del_station, .change_station = wil_cfg80211_change_station, - .probe_client = wil_cfg80211_probe_client, + .probe_peer = wil_cfg80211_probe_peer, .change_bss = wil_cfg80211_change_bss, /* P2P device */ .start_p2p_device = wil_cfg80211_start_p2p_device, diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index c9daf893472f..99d96088e364 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4558,9 +4558,9 @@ mwifiex_cfg80211_disassociate(struct wiphy *wiphy, } static int -mwifiex_cfg80211_probe_client(struct wiphy *wiphy, - struct net_device *dev, const u8 *peer, - u64 *cookie) +mwifiex_cfg80211_probe_peer(struct wiphy *wiphy, + struct net_device *dev, const u8 *peer, + u64 *cookie) { /* hostapd looks for NL80211_CMD_PROBE_CLIENT support; otherwise, * it requires monitor-mode support (which mwifiex doesn't support). @@ -4726,7 +4726,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) ops->disassoc = mwifiex_cfg80211_disassociate; ops->disconnect = NULL; ops->connect = NULL; - ops->probe_client = mwifiex_cfg80211_probe_client; + ops->probe_peer = mwifiex_cfg80211_probe_peer; } wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8188ad200de5..549b2214e833 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5086,7 +5086,7 @@ struct mgmt_frame_regs { * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * - * @probe_client: probe an associated client, must return a cookie that it + * @probe_peer: probe an associated client, must return a cookie that it * later passes to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. @@ -5488,8 +5488,8 @@ struct cfg80211_ops { int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); - int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie); + int (*probe_peer)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie); int (*set_noack_map)(struct wiphy *wiphy, struct net_device *dev, diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9998f6c0a665..d1907dd12a80 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -922,7 +922,7 @@ * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * - * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * @NL80211_CMD_PROBE_PEER: Probe an associated station on an AP interface * by sending a null data frame to it and reporting when the frame is * acknowledged. This is used to allow timing out inactive clients. Uses * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a @@ -1558,7 +1558,7 @@ enum nl80211_commands { NL80211_CMD_UNEXPECTED_FRAME, - NL80211_CMD_PROBE_CLIENT, + NL80211_CMD_PROBE_PEER, NL80211_CMD_REGISTER_BEACONS, @@ -1729,6 +1729,7 @@ enum nl80211_commands { #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG #define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE +#define NL80211_CMD_PROBE_CLIENT NL80211_CMD_PROBE_PEER /** * enum nl80211_attrs - nl80211 netlink attributes diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3b58af59f7e4..9c311c8290f7 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -4949,8 +4949,8 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, return 0; } -static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u64 *cookie) +static int ieee80211_probe_peer(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -6060,7 +6060,7 @@ const struct cfg80211_ops mac80211_config_ops = { .tdls_mgmt = ieee80211_tdls_mgmt, .tdls_channel_switch = ieee80211_tdls_channel_switch, .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch, - .probe_client = ieee80211_probe_client, + .probe_peer = ieee80211_probe_peer, .set_noack_map = ieee80211_set_noack_map, #ifdef CONFIG_PM .set_wakeup = ieee80211_set_wakeup, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 53b4b3f76697..0d651a46b9d6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2444,7 +2444,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, } if (rdev->wiphy.max_sched_scan_reqs) CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); + CMD(probe_peer, PROBE_PEER); CMD(set_noack_map, SET_NOACK_MAP); if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { i++; @@ -16150,8 +16150,7 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb, return 0; } -static int nl80211_probe_client(struct sk_buff *skb, - struct genl_info *info) +static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; @@ -16169,7 +16168,7 @@ static int nl80211_probe_client(struct sk_buff *skb, if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; - if (!rdev->ops->probe_client) + if (!rdev->ops->probe_peer) return -EOPNOTSUPP; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -16177,7 +16176,7 @@ static int nl80211_probe_client(struct sk_buff *skb, return -ENOMEM; hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, - NL80211_CMD_PROBE_CLIENT); + NL80211_CMD_PROBE_PEER); if (!hdr) { err = -ENOBUFS; goto free_msg; @@ -16185,7 +16184,7 @@ static int nl80211_probe_client(struct sk_buff *skb, addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - err = rdev_probe_client(rdev, dev, addr, &cookie); + err = rdev_probe_peer(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -20042,9 +20041,9 @@ static const struct genl_small_ops nl80211_small_ops[] = { .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), }, { - .cmd = NL80211_CMD_PROBE_CLIENT, + .cmd = NL80211_CMD_PROBE_PEER, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = nl80211_probe_client, + .doit = nl80211_probe_peer, .flags = GENL_UNS_ADMIN_PERM, .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), }, @@ -22614,7 +22613,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, if (!msg) return; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_PEER); if (!hdr) { nlmsg_free(msg); return; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 63c26e8b1139..6c3bad8b2d6f 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -948,13 +948,13 @@ static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev, return ret; } -static inline int rdev_probe_client(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *peer, - u64 *cookie) +static inline int rdev_probe_peer(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + u64 *cookie) { int ret; - trace_rdev_probe_client(&rdev->wiphy, dev, peer); - ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie); + trace_rdev_probe_peer(&rdev->wiphy, dev, peer); + ret = rdev->ops->probe_peer(&rdev->wiphy, dev, peer, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 94944f2a39a4..8c2a91b85c39 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2132,7 +2132,7 @@ DECLARE_EVENT_CLASS(rdev_pmksa, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->bssid) ); -TRACE_EVENT(rdev_probe_client, +TRACE_EVENT(rdev_probe_peer, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer), TP_ARGS(wiphy, netdev, peer), -- cgit v1.2.3 From 010e955c203e435d5bdba228fdc1556297d3d7ff Mon Sep 17 00:00:00 2001 From: Priyansha Tiwari Date: Thu, 11 Jun 2026 11:52:23 +0530 Subject: wifi: cfg80211/nl80211: add STA-mode peer probing Add NL80211_EXT_FEATURE_PROBE_AP to allow drivers to advertise support for probing the associated AP from STA/P2P-client mode. Extend nl80211_probe_peer() to accept STA/P2P-client interfaces when the driver advertises NL80211_EXT_FEATURE_PROBE_AP; in that case the MAC attribute must be omitted (the peer is implied by the association). Update cfg80211_probe_status() to accept an optional peer address and a link_id parameter (-1 for non-MLO), and include NL80211_ATTR_MLO_LINK_ID in the event when link_id >= 0. Update all callers. Signed-off-by: Priyansha Tiwari Link: https://patch.msgid.link/20260611062225.2144241-3-pritiwa@qti.qualcomm.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 2 +- include/net/cfg80211.h | 14 ++++---- include/uapi/linux/nl80211.h | 20 +++++++---- net/mac80211/status.c | 2 +- net/wireless/nl80211.c | 52 ++++++++++++++++++++--------- 5 files changed, 59 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index a85ff2a4316b..5f2bd9a31faf 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -2326,7 +2326,7 @@ static void wil_probe_client_handle(struct wil6210_priv *wil, */ bool alive = (sta->status == wil_sta_connected); - cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, + cfg80211_probe_status(ndev, sta->addr, req->cookie, -1, alive, 0, false, GFP_KERNEL); } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 549b2214e833..ddefe5acc5ae 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5086,8 +5086,8 @@ struct mgmt_frame_regs { * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). * - * @probe_peer: probe an associated client, must return a cookie that it - * later passes to cfg80211_probe_status(). + * @probe_peer: probe a connected peer (AP: STA MAC required; STA: no MAC), + * must return a cookie that is later passed to cfg80211_probe_status(). * * @set_noack_map: Set the NoAck Map for the TIDs. * @@ -9846,15 +9846,17 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, /** * cfg80211_probe_status - notify userspace about probe status * @dev: the device the probe was sent on - * @addr: the address of the peer - * @cookie: the cookie filled in @probe_client previously + * @peer: The peer MAC address (or MLD address for MLO) or %NULL if not + * applicable (e.g. for STA/P2P-client) + * @cookie: the cookie filled in @probe_peer previously + * @link_id: The link ID on which the probe was sent (or -1 for non-MLO) * @acked: indicates whether probe was acked or not * @ack_signal: signal strength (in dBm) of the ACK frame. * @is_valid_ack_signal: indicates the ack_signal is valid or not. * @gfp: allocation flags */ -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, s32 ack_signal, +void cfg80211_probe_status(struct net_device *dev, const u8 *peer, u64 cookie, + int link_id, bool acked, s32 ack_signal, bool is_valid_ack_signal, gfp_t gfp); /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d1907dd12a80..6b8071606e6f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -922,13 +922,15 @@ * and wasn't already in a 4-addr VLAN. The event will be sent similarly * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. * - * @NL80211_CMD_PROBE_PEER: Probe an associated station on an AP interface - * by sending a null data frame to it and reporting when the frame is - * acknowledged. This is used to allow timing out inactive clients. Uses - * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a - * direct reply with an %NL80211_ATTR_COOKIE that is later used to match - * up the event with the request. The event includes the same data and - * has %NL80211_ATTR_ACK set if the frame was ACKed. + * @NL80211_CMD_PROBE_PEER: Probe a connected peer by sending a null data + * frame and reporting when the frame is acknowledged. + * In AP/GO mode, %NL80211_ATTR_MAC is required to identify the client. + * In STA/P2P-client mode, %NL80211_ATTR_MAC must be omitted (the AP is + * implied); the driver must advertise %NL80211_EXT_FEATURE_PROBE_AP. + * The command returns a direct reply with an %NL80211_ATTR_COOKIE that + * is later used to match up the event with the request. The event + * includes the same data and has %NL80211_ATTR_ACK set if the frame + * was ACKed. * * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from * other BSSes when any interfaces are in AP mode. This helps implement @@ -7086,6 +7088,9 @@ enum nl80211_feature_flags { * LTF key seed via %NL80211_KEY_LTF_SEED. The seed is used to generate * secure LTF keys for secure LTF measurement sessions. * + * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP + * in STA mode using @NL80211_CMD_PROBE_PEER. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7167,6 +7172,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_IEEE8021X_AUTH, NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, + NL80211_EXT_FEATURE_PROBE_AP, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index dd1dbba06838..c3d29aed93fe 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -655,7 +655,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, GFP_ATOMIC); else if (ieee80211_is_any_nullfunc(hdr->frame_control)) cfg80211_probe_status(sdata->dev, hdr->addr1, - cookie, acked, + cookie, -1, acked, info->status.ack_signal, is_valid_ack_signal, GFP_ATOMIC); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0d651a46b9d6..a62e319f6aec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -16157,16 +16157,32 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) struct wireless_dev *wdev = dev->ieee80211_ptr; struct sk_buff *msg; void *hdr; - const u8 *addr; + const u8 *addr = NULL; u64 cookie; int err; - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) + /* Allow in AP, STA, and their P2P counterparts */ + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_PROBE_AP)) + return -EOPNOTSUPP; + if (!wdev->connected) + return -ENOLINK; + /* STA/P2P-client probes the currently associated AP/GO. */ + if (info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + break; + default: return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; + } if (!rdev->ops->probe_peer) return -EOPNOTSUPP; @@ -16182,8 +16198,6 @@ static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info) goto free_msg; } - addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - err = rdev_probe_peer(rdev, dev, addr, &cookie); if (err) goto free_msg; @@ -22597,8 +22611,8 @@ nla_put_failure: } EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, s32 ack_signal, +void cfg80211_probe_status(struct net_device *dev, const u8 *peer, u64 cookie, + int link_id, bool acked, s32 ack_signal, bool is_valid_ack_signal, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -22606,7 +22620,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, struct sk_buff *msg; void *hdr; - trace_cfg80211_probe_status(dev, addr, cookie, acked); + trace_cfg80211_probe_status(dev, peer, cookie, acked); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); @@ -22621,12 +22635,18 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + (peer && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) || nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie, - NL80211_ATTR_PAD) || - (acked && nla_put_flag(msg, NL80211_ATTR_ACK)) || - (is_valid_ack_signal && nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, - ack_signal))) + NL80211_ATTR_PAD)) + goto nla_put_failure; + + if (link_id >= 0 && + nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) + goto nla_put_failure; + + if ((acked && nla_put_flag(msg, NL80211_ATTR_ACK)) || + (is_valid_ack_signal && + nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, ack_signal))) goto nla_put_failure; genlmsg_end(msg, hdr); -- cgit v1.2.3 From f83378e6ce497eda7e9a7490de4e1a46459febb2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 15 Jun 2026 09:39:48 +0200 Subject: wifi: nl80211: clarify NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA content This is currently __le16, but really the whole content of the corresponding 802.11 element, which is even extensible and could, in theory, be increased in size. Clarify the docs. Link: https://patch.msgid.link/20260615093948.0f730833a6d5.I1c8c5c09dfe16b0b1dcb10d54fc030f6b1d4fc8c@changeid Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6b8071606e6f..d9a8c693457f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4477,8 +4477,8 @@ enum nl80211_mpath_info { * capabilities IE * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as * defined in HE capabilities IE - * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16), - * given for all 6 GHz band channels + * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities, + * given for all 6 GHz band channels (binary, element content) * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are * advertised on this band/for this iftype (binary) * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT -- cgit v1.2.3 From 84442442e04be58a8977fb10debcbbfc1649d962 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 19 Jun 2026 14:21:07 +0200 Subject: wifi: cfg80211: remove WIPHY_FLAG_DISABLE_WEXT There are only two drivers left setting it, but they're both also setting WIPHY_FLAG_SUPPORTS_MLO for the relevant devices, so we can now remove WIPHY_FLAG_DISABLE_WEXT. Link: https://patch.msgid.link/20260619142107.150f1bbe3b83.I9ff3d419bad54313c76fa4c3485148c122e67fb3@changeid Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath12k/mac.c | 6 ------ drivers/net/wireless/realtek/rtw89/core.c | 3 --- include/net/cfg80211.h | 3 +-- net/wireless/wext-core.c | 6 ++---- 4 files changed, 3 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index af354bef5c0d..9775a87b3db3 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -14871,12 +14871,6 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; - /* MLO is not yet supported so disable Wireless Extensions for now - * to make sure ath12k users don't use it. This flag can be removed - * once WIPHY_FLAG_SUPPORTS_MLO is enabled. - */ - wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - /* Copy over MLO related capabilities received from * WMI_SERVICE_READY_EXT2_EVENT if single_chip_mlo_supp is set. */ diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 68dad6090f87..0f0e46cb4260 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -7432,9 +7432,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) if (!chip->support_rnr) hw->wiphy->flags |= WIPHY_FLAG_SPLIT_SCAN_6GHZ; - if (chip->chip_gen == RTW89_CHIP_BE) - hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - if (rtwdev->support_mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ddefe5acc5ae..d91533a66712 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5690,7 +5690,6 @@ struct cfg80211_ops { * set this flag to update channels on beacon hints. * @WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY: support connection to non-primary link * of an NSTR mobile AP MLD. - * @WIPHY_FLAG_DISABLE_WEXT: disable wireless extensions for this device */ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK = BIT(0), @@ -5702,7 +5701,7 @@ enum wiphy_flags { WIPHY_FLAG_4ADDR_STATION = BIT(6), WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), - WIPHY_FLAG_DISABLE_WEXT = BIT(9), + /* reuse bit 9 */ WIPHY_FLAG_MESH_AUTH = BIT(10), WIPHY_FLAG_SUPPORTS_EXT_KCK_32 = BIT(11), WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY = BIT(12), diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c19dece2bc6e..db77912b3994 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -660,8 +660,7 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev) dev->ieee80211_ptr->wiphy->wext && dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); } @@ -703,8 +702,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) #ifdef CONFIG_CFG80211_WEXT if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) { wireless_warn_cfg80211_wext(); - if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO | - WIPHY_FLAG_DISABLE_WEXT)) + if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) return NULL; handlers = dev->ieee80211_ptr->wiphy->wext; } -- cgit v1.2.3 From 1c29c67bc7a9962e2ef91e8c65b7fc4fa227eded Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 26 Jun 2026 16:28:57 +1000 Subject: wifi: cfg80211: introduce helper to get S1G primary width This is needed for drivers and will be needed for mac80211/cfg80211 in the future so introduce a generic accessor to retrieve the chandefs S1G primary channel width. Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20260626063014.1275235-2-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d91533a66712..b8e9fbb89e69 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1236,6 +1236,26 @@ ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef) return chandef->chan->max_power; } +/** + * cfg80211_chandef_s1g_pri_width - return S1G primary width in MHz + * + * An S1G interface may have a primary channel width of either 1 + * or 2MHz depending on whether chandef::s1g_primary_2mhz is set. + * + * Note: There is _always_ a 1MHz primary subchannel, regardless + * of the primary width. So chandef::chan always points to this + * 1MHz primary channel. + * + * @chandef: the chandef to use + * + * Returns: width in MHz of the S1G primary channel in use + */ +static inline int +cfg80211_chandef_s1g_pri_width(struct cfg80211_chan_def *chandef) +{ + return chandef->s1g_primary_2mhz ? 2 : 1; +} + /** * cfg80211_any_usable_channels - check for usable channels * @wiphy: the wiphy to check for -- cgit v1.2.3 From cc61825060b01077da2b9796dc1047ec383e53c0 Mon Sep 17 00:00:00 2001 From: Lachlan Hodges Date: Fri, 26 Jun 2026 16:28:58 +1000 Subject: wifi: ieee80211: introduce generic KHZ_TO_HZ helper Useful for S1G drivers due to the increased required granularity, but may be useful for others so include it as a generic helper. Signed-off-by: Lachlan Hodges Link: https://patch.msgid.link/20260626063014.1275235-3-lachlan.hodges@morsemicro.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d40484451e9a..084ad45aa2d8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2616,6 +2616,7 @@ static inline int ieee80211_get_tdls_action(struct sk_buff *skb) /* convert frequencies */ #define MHZ_TO_KHZ(freq) ((freq) * 1000) #define KHZ_TO_MHZ(freq) ((freq) / 1000) +#define KHZ_TO_HZ(x) ((x) * 1000) #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000 #define KHZ_F "%d.%03d" -- cgit v1.2.3 From aef91f72994cffd6393a8372d38f5611e6c12ba2 Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Sun, 14 Jun 2026 10:47:34 +0530 Subject: wifi: cfg80211: Fragment per-link station stats in nl80211_dump_station() In MLO scenarios, stations may have multiple links, each with distinct statistics. When userspace tools like iw or hostapd request station dumps, attempting to pack all per-link stats into a single netlink message can easily exceed the default 4KB buffer limit, especially when more than two links are active. This results in -EMSGSIZE errors and incomplete data delivery. To address this, fragment per-link station statistics across multiple netlink messages to ensure reliable delivery of complete MLO station information. Extend the stateful context with a two-phase dump mechanism: phase 0 (AGGREGATED) sends combined MLO-level statistics and phase 1 (PER_LINK) sends individual per-link statistics for each active link. The dump loop is structured to produce exactly one netlink message per iteration, with a common header (ifindex, wdev, mac, generation) built once and phase-specific payload added via a switch statement. This keeps header construction in one place and makes the EMSGSIZE bail-out uniform. Add a new request flag attribute, NL80211_ATTR_STA_DUMP_LINK_STATS (NLA_FLAG), for NL80211_CMD_GET_STATION dump. Userspace can set this flag to request per-link station statistics for MLO stations. Extract this flag during the first dump invocation by passing an attrbuf to nl80211_prepare_wdev_dump(); use __free(kfree) to avoid scattered manual kfree() calls. Cache the boolean in the dump context to avoid repeated parsing on subsequent invocations. Per-link messages carry a single NL80211_ATTR_MLO_LINKS nest with the link ID, link-specific MAC, and per-link NL80211_ATTR_STA_INFO payload. The link-specific validity (is_valid_ether_addr) and null pointer guard are checked in nl80211_put_link_station_payload() before any message construction begins. Also fix all nla_nest_start_noflag() calls in nl80211_fill_link_station() for nested attribute types (STA_INFO, BSS_PARAM, TID_STATS, per-tid) to use nla_nest_start() so the NLA_F_NESTED flag is set correctly. Propagate the actual return value from nl80211_put_sta_info_common() in the AGGREGATED phase rather than returning skb->len. Returning skb->len signals netlink to re-invoke the dump with the same sta_idx, causing an infinite loop when the aggregated payload is too large to fit; returning the real error code (-EMSGSIZE or otherwise) terminates the dump cleanly. Backward compatibility is seamlessly preserved for non-MLO stations. Signed-off-by: P Praneesh Link: https://patch.msgid.link/20260614051739.3979947-5-praneesh.p@oss.qualcomm.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 19 +++++ net/wireless/nl80211.c | 170 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 157 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d9a8c693457f..020387d76412 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3168,6 +3168,23 @@ enum nl80211_commands { * @NL80211_ATTR_NPCA_PRIMARY_FREQ: NPCA primary channel (u32) * @NL80211_ATTR_NPCA_PUNCT_BITMAP: NPCA puncturing bitmap (u32) * + * @NL80211_ATTR_STA_DUMP_LINK_STATS: Request flag for %NL80211_CMD_GET_STATION + * (dump mode only). When set on an MLD station, the dump produces two + * %NL80211_CMD_NEW_STATION messages per station per dump call: + * + * 1. An aggregated-stats message whose top-level %NL80211_ATTR_STA_INFO + * contains MLO-combined statistics (same content as a dump without + * this flag). + * + * 2. For each active link, a per-link message containing + * %NL80211_ATTR_MLO_LINKS with a single link entry. Each entry holds + * %NL80211_ATTR_MLO_LINK_ID, the link-specific %NL80211_ATTR_MAC, + * and %NL80211_ATTR_STA_INFO with per-link statistics (see + * &enum nl80211_sta_info). + * + * The aggregated message always precedes the per-link messages for the + * same station within a dump sequence. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3766,6 +3783,8 @@ enum nl80211_attrs { NL80211_ATTR_NPCA_PRIMARY_FREQ, NL80211_ATTR_NPCA_PUNCT_BITMAP, + NL80211_ATTR_STA_DUMP_LINK_STATS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ccc4341a0aea..be16a40132ff 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1093,6 +1093,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 }, [NL80211_ATTR_NPCA_PUNCT_BITMAP] = NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), + [NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -7870,7 +7871,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, goto nla_put_failure; \ } while (0) - link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO); + link_sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!link_sinfoattr) goto nla_put_failure; @@ -7936,8 +7937,8 @@ static int nl80211_fill_link_station(struct sk_buff *msg, PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32); if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { - bss_param = nla_nest_start_noflag(msg, - NL80211_STA_INFO_BSS_PARAM); + bss_param = nla_nest_start(msg, + NL80211_STA_INFO_BSS_PARAM); if (!bss_param) goto nla_put_failure; @@ -7979,8 +7980,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, struct nlattr *tidsattr; int tid; - tidsattr = nla_nest_start_noflag(msg, - NL80211_STA_INFO_TID_STATS); + tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS); if (!tidsattr) goto nla_put_failure; @@ -7993,7 +7993,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg, if (!tidstats->filled) continue; - tidattr = nla_nest_start_noflag(msg, tid + 1); + tidattr = nla_nest_start(msg, tid + 1); if (!tidattr) goto nla_put_failure; @@ -8464,21 +8464,74 @@ static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo) sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); } +enum nl80211_dump_station_phase { + NL80211_DUMP_STA_PHASE_AGGREGATED = 0, + NL80211_DUMP_STA_PHASE_PER_LINK = 1, +}; + struct nl80211_dump_station_ctx { int sta_idx; + int link_idx; + enum nl80211_dump_station_phase phase; + bool dump_link_stats; u8 mac_addr[ETH_ALEN]; struct station_info sinfo; }; +static int nl80211_put_link_station_payload(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct station_info *sinfo, + int link_idx) +{ + struct link_station_info *link_sinfo = sinfo->links[link_idx]; + struct nlattr *links, *link; + + if (WARN_ON_ONCE(!link_sinfo)) + return -ENOENT; + + if (!is_valid_ether_addr(link_sinfo->addr)) + return -EADDRNOTAVAIL; + + links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!links) + return -EMSGSIZE; + + link = nla_nest_start(msg, link_idx + 1); + if (!link) + goto nla_put_failure; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_idx) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, link_sinfo->addr)) + goto nla_put_failure; + + if (nl80211_fill_link_station(msg, rdev, link_sinfo)) + goto nla_put_failure; + + nla_nest_end(msg, link); + nla_nest_end(msg, links); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, links); + return -EMSGSIZE; +} + static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2]; + struct nlattr **attrbuf __free(kfree) = NULL; int err; - err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL); + if (!ctx) { + attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR); + if (!attrbuf) + return -ENOMEM; + } + + err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, attrbuf); if (err) return err; /* nl80211_prepare_wdev_dump acquired it in the successful case */ @@ -8490,6 +8543,9 @@ static int nl80211_dump_station(struct sk_buff *skb, err = -ENOMEM; goto out_err; } + ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED; + ctx->dump_link_stats = + !!attrbuf[NL80211_ATTR_STA_DUMP_LINK_STATS]; cb->args[2] = (long)ctx; } @@ -8505,34 +8561,53 @@ static int nl80211_dump_station(struct sk_buff *skb, while (true) { void *hdr; + int ret; - memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); - for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - ctx->sinfo.links[i] = - kzalloc_obj(*ctx->sinfo.links[0]); - if (!ctx->sinfo.links[i]) { - err = -ENOMEM; + /* AGGREGATED phase: fetch sinfo from driver once per station */ + if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) { + memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + ctx->sinfo.links[i] = + kzalloc_obj(*ctx->sinfo.links[0]); + if (!ctx->sinfo.links[i]) { + err = -ENOMEM; + goto out_err_release; + } + } + + err = rdev_dump_station(rdev, wdev, ctx->sta_idx, + ctx->mac_addr, &ctx->sinfo); + if (err == -ENOENT) { + err = skb->len; goto out_err_release; } - } + if (err) + goto out_err_release; - err = rdev_dump_station(rdev, wdev, ctx->sta_idx, - ctx->mac_addr, &ctx->sinfo); - if (err == -ENOENT) { - err = skb->len; - goto out_err_release; + if (ctx->sinfo.valid_links) + cfg80211_sta_set_mld_sinfo(&ctx->sinfo); + } else { + /* PER_LINK phase: advance to next valid link */ + while (ctx->link_idx < IEEE80211_MLD_MAX_NUM_LINKS && + !(ctx->sinfo.valid_links & BIT(ctx->link_idx))) + ctx->link_idx++; + + if (ctx->link_idx >= IEEE80211_MLD_MAX_NUM_LINKS) { + cfg80211_sinfo_release_content(&ctx->sinfo); + ctx->sta_idx++; + ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED; + continue; + } } - if (err) - goto out_err_release; - - if (ctx->sinfo.valid_links) - cfg80211_sta_set_mld_sinfo(&ctx->sinfo); + /* Build common header for both phases */ hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, NL80211_CMD_NEW_STATION); if (!hdr) { err = skb->len; + if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK) + goto out_err; goto out_err_release; } @@ -8546,18 +8621,49 @@ static int nl80211_dump_station(struct sk_buff *skb, ctx->sinfo.generation)) { genlmsg_cancel(skb, hdr); err = skb->len; + if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK) + goto out_err; goto out_err_release; } - if (nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo)) { - genlmsg_cancel(skb, hdr); - err = skb->len; - goto out_err_release; - } + switch (ctx->phase) { + case NL80211_DUMP_STA_PHASE_AGGREGATED: + ret = nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo); + if (ret) { + genlmsg_cancel(skb, hdr); + err = ret; + goto out_err_release; + } + genlmsg_end(skb, hdr); + + if (ctx->dump_link_stats && ctx->sinfo.valid_links) { + ctx->phase = NL80211_DUMP_STA_PHASE_PER_LINK; + ctx->link_idx = 0; + } else { + cfg80211_sinfo_release_content(&ctx->sinfo); + ctx->sta_idx++; + } + break; - genlmsg_end(skb, hdr); - cfg80211_sinfo_release_content(&ctx->sinfo); - ctx->sta_idx++; + case NL80211_DUMP_STA_PHASE_PER_LINK: + ret = nl80211_put_link_station_payload(skb, rdev, + &ctx->sinfo, + ctx->link_idx); + if (ret == -EMSGSIZE) { + genlmsg_cancel(skb, hdr); + err = skb->len; + goto out_err; + } + if (ret) { + /* skip invalid link, do not abort the dump */ + genlmsg_cancel(skb, hdr); + ctx->link_idx++; + continue; + } + genlmsg_end(skb, hdr); + ctx->link_idx++; + break; + } } out_err_release: -- cgit v1.2.3