#ifndef LINUX_IEEE80211_UHR_H
#define LINUX_IEEE80211_UHR_H
#include <linux/types.h>
#include <linux/bitfield.h>
#include <linux/if_ether.h>
#include "ieee80211-eht.h"
#define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001
#define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA 0x0002
#define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0004
#define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0008
#define IEEE80211_UHR_OPER_PARAMS_DBE_BW 0x0070
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;
#define IEEE80211_UHR_DPS_PADDING_DELAY 0x0000003F
#define IEEE80211_UHR_DPS_TRANSITION_DELAY 0x00003F00
#define IEEE80211_UHR_DPS_ICF_REQUIRED 0x00010000
#define IEEE80211_UHR_DPS_PARAMETERIZED_FLAG 0x00020000
#define IEEE80211_UHR_DPS_LC_MODE_BW 0x001C0000
#define IEEE80211_UHR_DPS_LC_MODE_NSS 0x01E00000
#define IEEE80211_UHR_DPS_LC_MODE_MCS 0x1E000000
#define IEEE80211_UHR_DPS_MOBILE_AP_DPS_STATIC_HCM 0x20000000
struct ieee80211_uhr_dps_info {
__le32 params;
} __packed;
#define IEEE80211_UHR_DBE_OPER_BANDWIDTH 0x07
#define IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES 0x08
enum ieee80211_uhr_dbe_oper_bw {
IEEE80211_UHR_DBE_OPER_BW_40 = 1,
IEEE80211_UHR_DBE_OPER_BW_80 = 2,
IEEE80211_UHR_DBE_OPER_BW_160 = 3,
IEEE80211_UHR_DBE_OPER_BW_320_1 = 4,
IEEE80211_UHR_DBE_OPER_BW_320_2 = 5,
};
static inline int ieee80211_uhr_dbe_bw_mhz(enum ieee80211_uhr_dbe_oper_bw bw)
{
switch (bw) {
case IEEE80211_UHR_DBE_OPER_BW_40:
return 40;
case IEEE80211_UHR_DBE_OPER_BW_80:
return 80;
case IEEE80211_UHR_DBE_OPER_BW_160:
return 160;
case IEEE80211_UHR_DBE_OPER_BW_320_1:
case IEEE80211_UHR_DBE_OPER_BW_320_2:
return 320;
default:
return -1;
}
}
struct ieee80211_uhr_dbe_info {
u8 params;
__le16 dis_subch_bmap[];
} __packed;
#define IEEE80211_UHR_P_EDCA_ECWMIN 0x0F
#define IEEE80211_UHR_P_EDCA_ECWMAX 0xF0
#define IEEE80211_UHR_P_EDCA_AIFSN 0x000F
#define IEEE80211_UHR_P_EDCA_CW_DS 0x0030
#define IEEE80211_UHR_P_EDCA_PSRC_THRESHOLD 0x01C0
#define IEEE80211_UHR_P_EDCA_QSRC_THRESHOLD 0x0600
struct ieee80211_uhr_p_edca_info {
u8 p_edca_ec;
__le16 params;
} __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;
if (beacon)
return true;
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA)) {
needed += sizeof(struct ieee80211_uhr_dps_info);
if (len < needed)
return false;
}
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) {
const struct ieee80211_uhr_npca_info *npca =
(const void *)(data + needed);
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]);
if (len < needed)
return false;
}
}
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA)) {
needed += sizeof(struct ieee80211_uhr_p_edca_info);
if (len < needed)
return false;
}
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DBE_ENA)) {
const struct ieee80211_uhr_dbe_info *dbe =
(const void *)(data + needed);
needed += sizeof(*dbe);
if (len < needed)
return false;
if (dbe->params &
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) {
needed += sizeof(dbe->dis_subch_bmap[0]);
if (len < needed)
return false;
}
}
return len >= needed;
}
static inline const struct ieee80211_uhr_npca_info *
ieee80211_uhr_npca_info(const struct ieee80211_uhr_operation *oper)
{
const u8 *pos = oper->variable;
if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)))
return NULL;
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA))
pos += sizeof(struct ieee80211_uhr_dps_info);
return (const void *)pos;
}
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;
}
static inline const struct ieee80211_uhr_dbe_info *
ieee80211_uhr_oper_dbe_info(const struct ieee80211_uhr_operation *oper)
{
const u8 *pos = oper->variable;
if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DBE_ENA)))
return NULL;
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA))
pos += sizeof(struct ieee80211_uhr_dps_info);
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) {
const struct ieee80211_uhr_npca_info *npca = (const void *)pos;
pos += sizeof(*npca);
if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
pos += sizeof(npca->dis_subch_bmap[0]);
}
if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA))
pos += sizeof(struct ieee80211_uhr_p_edca_info);
return (const void *)pos;
}
#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_dbe {
u8 cap;
struct ieee80211_eht_mcs_nss_supp_bw eht_mcs_map[];
} __packed;
enum ieee80211_uhr_dbe_max_supported_bw {
IEEE80211_UHR_DBE_MAX_BW_40 = 1,
IEEE80211_UHR_DBE_MAX_BW_80 = 2,
IEEE80211_UHR_DBE_MAX_BW_160 = 3,
IEEE80211_UHR_DBE_MAX_BW_320 = 4,
};
struct ieee80211_uhr_cap_mac {
u8 mac_cap[6];
} __packed;
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x00000001
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x00000002
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x00000004
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x00000008
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x00000010
#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x00000020
#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x00000040
#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x00000080
#define IEEE80211_UHR_PHY_CAP_PART_BW_DL_MUMIMO 0x00000100
#define IEEE80211_UHR_PHY_CAP_PART_BW_UL_MUMIMO 0x00000200
#define IEEE80211_UHR_PHY_CAP_MCS15 0x00000400
#define IEEE80211_UHR_PHY_CAP_2XLDPC_TX 0x00000800
#define IEEE80211_UHR_PHY_CAP_2XLDPC_RX 0x00001000
#define IEEE80211_UHR_PHY_CAP_UEQM_TX_MAX_NSS 0x00006000
#define IEEE80211_UHR_PHY_CAP_UEQM_RX_MAX_NSS 0x00018000
#define IEEE80211_UHR_PHY_CAP_CO_BF_JOINT_SOUNDING 0x00040000
#define IEEE80211_UHR_PHY_CAP_IM_TX 0x00080000
#define IEEE80211_UHR_PHY_CAP_IM_RX 0x00100000
#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_1 0x00200000
#define IEEE80211_UHR_PHY_CAP_CO_SR_MODE_2 0x00400000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_20 0x00800000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_40 0x01000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_80 0x02000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_160 0x04000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_80_IN_PBW_320 0x08000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_20_IN_PBW_GE80 0x10000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_40_IN_PBW_GE80 0x20000000
#define IEEE80211_UHR_PHY_CAP_DRU_DBW_60_IN_PBW_GE80 0x40000000
#define IEEE80211_UHR_PHY_CAP_DRU_RRU_HYBRID_MODE 0x80000000
struct ieee80211_uhr_cap_phy {
__le32 cap;
u8 reserved;
} __packed;
struct ieee80211_uhr_cap {
struct ieee80211_uhr_cap_mac mac;
struct ieee80211_uhr_cap_phy phy;
u8 variable[];
} __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);
if (len < needed)
return false;
if (from_ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
const struct ieee80211_uhr_cap_dbe *dbe;
needed += sizeof(struct ieee80211_uhr_cap_dbe);
if (len < needed)
return false;
dbe = (const void *)cap->variable;
if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
needed += sizeof(dbe->eht_mcs_map[0]);
if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
needed += sizeof(dbe->eht_mcs_map[0]);
}
return len >= needed;
}
#define IEEE80211_UHR_OM_PU_TO_128TU 11
static inline int
ieee80211_uhr_capa_get_om_pu_to_us(const struct ieee80211_uhr_cap *cap)
{
u8 timeout;
timeout = u8_get_bits(cap->mac.mac_cap[3],
IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH);
timeout <<= 2;
timeout |= u8_get_bits(cap->mac.mac_cap[2],
IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW);
if (timeout > IEEE80211_UHR_OM_PU_TO_128TU)
return -1;
if (!timeout)
return 0;
return 128 << (timeout - 1);
}
static inline const struct ieee80211_uhr_cap_dbe *
ieee80211_uhr_dbe_cap(const struct ieee80211_uhr_cap *cap)
{
if (!(cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP))
return NULL;
return (const void *)cap->variable;
}
#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;
enum ieee80211_protected_uhr_action {
IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST = 0,
IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_RESPONSE = 1,
IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_NOTIFY = 2,
};
enum ieee80211_uhr_link_reconfig_request_type {
IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_PREP = 0,
IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_EXEC = 1,
IEEE80211_UHR_LINK_RECONFIG_REQUEST_OMP_REQUEST = 3,
};
enum ieee80211_uhr_link_reconfig_response_type {
IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_PREP = 0,
IEEE80211_UHR_LINK_RECONFIG_RESPONSE_ST_EXEC = 1,
};
enum ieee80211_uhr_link_reconfig_notify_type {
IEEE80211_UHR_LINK_RECONFIG_NOTIFY_DL_DRAINED = 2,
IEEE80211_UHR_LINK_RECONFIG_NOTIFY_OMP_RESPONSE = 3,
};
enum ieee80211_uhr_mode_change_control {
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ID = 0x003f,
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_ENABLE = 0x0040,
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_UPDATE = 0x0080,
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH = 0x0f00,
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_SPECIFIC = 0xf000,
};
enum ieee80211_uhr_mode_change_mode_id {
IEEE80211_UHR_MODE_CHANGE_MODE_ID_DPS = 0,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_NPCA = 1,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_DUO = 2,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_DSO = 3,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_P_EDCA = 4,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_ELR_RX = 5,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_AOM = 6,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_LLI = 7,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_BF = 8,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_CO_SR = 9,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_EMLSR = 10,
IEEE80211_UHR_MODE_CHANGE_MODE_ID_DBE = 11,
};
struct ieee80211_uhr_mode_change_tuple {
__le16 control;
u8 variable[];
} __packed;
static inline int
ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tuple *tuple)
{
return sizeof(*tuple) +
le16_get_bits(tuple->control,
IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH);
}
#define for_each_uhr_mode_change_tuple(data, len, tuple) \
for (tuple = (const void *)(data); \
(len) - ((const u8 *)tuple - (data)) >= sizeof(*tuple) && \
(len) - ((const u8 *)tuple - (data)) >= \
ieee80211_uhr_mode_change_tuple_size(tuple); \
tuple = (const void *)((const u8 *)tuple + \
ieee80211_uhr_mode_change_tuple_size(tuple)))
#endif