/* SPDX-License-Identifier: GPL-2.0-only */ /* * IEEE 802.11 UHR definitions * * Copyright (c) 2025-2026 Intel Corporation */ #ifndef LINUX_IEEE80211_UHR_H #define LINUX_IEEE80211_UHR_H #include #include #define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001 #define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA 0x0002 #define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0004 #define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0008 struct ieee80211_uhr_operation { __le16 params; u8 basic_mcs_nss_set[4]; u8 variable[]; } __packed; #define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS 0x0000000F #define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH 0x000000F0 #define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY 0x00003F00 #define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY 0x000FC000 #define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC 0x00300000 #define IEEE80211_UHR_NPCA_PARAMS_MOPLEN 0x00400000 #define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES 0x00800000 struct ieee80211_uhr_npca_info { __le32 params; __le16 dis_subch_bmap[]; } __packed; static inline bool ieee80211_uhr_oper_size_ok(const u8 *data, u8 len, bool beacon) { const struct ieee80211_uhr_operation *oper = (const void *)data; u8 needed = sizeof(*oper); if (len < needed) return false; /* nothing else present in beacons */ if (beacon) return true; /* FIXME: DPS, DBE, P-EDCA (consider order, also relative to NPCA) */ if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) { const struct ieee80211_uhr_npca_info *npca = (const void *)oper->variable; needed += sizeof(*npca); if (len < needed) return false; if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)) needed += sizeof(npca->dis_subch_bmap[0]); } return len >= needed; } /* * Note: cannot call this on the element coming from a beacon, * must ensure ieee80211_uhr_oper_size_ok(..., false) first */ static inline const struct ieee80211_uhr_npca_info * ieee80211_uhr_npca_info(const struct ieee80211_uhr_operation *oper) { if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA))) return NULL; /* FIXME: DPS */ return (const void *)oper->variable; } static inline const __le16 * ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_operation *oper) { const struct ieee80211_uhr_npca_info *npca; npca = ieee80211_uhr_npca_info(oper); if (!npca) return NULL; if (!(npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))) return NULL; return npca->dis_subch_bmap; } #define IEEE80211_UHR_MAC_CAP0_DPS_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP 0x02 #define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP 0x04 #define IEEE80211_UHR_MAC_CAP0_NPCA_SUPP 0x10 #define IEEE80211_UHR_MAC_CAP0_ENH_BSR_SUPP 0x20 #define IEEE80211_UHR_MAC_CAP0_ADD_MAP_TID_SUPP 0x40 #define IEEE80211_UHR_MAC_CAP0_EOTSP_SUPP 0x80 #define IEEE80211_UHR_MAC_CAP1_DSO_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP1_PEDCA_SUPP 0x02 #define IEEE80211_UHR_MAC_CAP1_DBE_SUPP 0x04 #define IEEE80211_UHR_MAC_CAP1_UL_LLI_SUPP 0x08 #define IEEE80211_UHR_MAC_CAP1_P2P_LLI_SUPP 0x10 #define IEEE80211_UHR_MAC_CAP1_PUO_SUPP 0x20 #define IEEE80211_UHR_MAC_CAP1_AP_PUO_SUPP 0x40 #define IEEE80211_UHR_MAC_CAP1_DUO_SUPP 0x80 #define IEEE80211_UHR_MAC_CAP2_OMC_UL_MU_DIS_RX_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP2_AOM_SUPP 0x02 #define IEEE80211_UHR_MAC_CAP2_IFCS_LOC_SUPP 0x04 #define IEEE80211_UHR_MAC_CAP2_UHR_TRS_SUPP 0x08 #define IEEE80211_UHR_MAC_CAP2_TXSPG_SUPP 0x10 #define IEEE80211_UHR_MAC_CAP2_TXOP_RET_IN_TXSPG 0x20 #define IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW 0xC0 #define IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH 0x03 #define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV 0x1C #define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTV_LOW 0xE0 #define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH 0x03 #define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS 0x04 #define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE 0x08 #define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP 0x10 #define IEEE80211_UHR_MAC_CAP_DBE_MAX_BW 0x07 #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES 0x08 #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES 0x10 struct ieee80211_uhr_cap_mac { u8 mac_cap[5]; } __packed; struct ieee80211_uhr_cap { struct ieee80211_uhr_cap_mac mac; /* DBE, PHY capabilities */ u8 variable[]; } __packed; #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x01 #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x02 #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x04 #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x08 #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x10 #define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x20 #define IEEE80211_UHR_PHY_CAP_ELR_RX 0x40 #define IEEE80211_UHR_PHY_CAP_ELR_TX 0x80 struct ieee80211_uhr_cap_phy { u8 cap; } __packed; static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, bool from_ap) { const struct ieee80211_uhr_cap *cap = (const void *)data; size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_cap_phy); if (len < needed) return false; /* * A non-AP STA does not include the DBE Capability Parameters field * in the UHR MAC Capabilities Information field. */ if (from_ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) { u8 dbe; needed += 1; if (len < needed) return false; dbe = cap->variable[0]; if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) needed += 3; if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) needed += 3; } return len >= needed; } static inline const struct ieee80211_uhr_cap_phy * ieee80211_uhr_phy_cap(const struct ieee80211_uhr_cap *cap, bool from_ap) { u8 offs = 0; if (from_ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) { u8 dbe = cap->variable[0]; offs += 1; if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) offs += 3; if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) offs += 3; } return (const void *)&cap->variable[offs]; } #define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD 0x01 #define IEEE80211_SMD_INFO_CAPA_MAX_NUM_PREP 0x0E #define IEEE80211_SMD_INFO_CAPA_TYPE 0x10 #define IEEE80211_SMD_INFO_CAPA_PTK_PER_AP_MLD 0x20 struct ieee80211_smd_info { u8 id[ETH_ALEN]; u8 capa; __le16 timeout; } __packed; #endif /* LINUX_IEEE80211_UHR_H */