root/include/linux/ieee80211-uhr.h
/* 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 <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 - npca operation information
 *
 * This structure is the "NPCA Operation Parameters field format" of "UHR
 * Operation Element" fields as described in P802.11bn_D1.3
 * subclause 9.4.2.353. See Figure 9-aa4.
 *
 * Refer to IEEE80211_UHR_NPCA*
 * @params:
 *      NPCA Primary Channel - NPCA primary channel
 *      NPCA_Min Duration Threshold - Minimum duration of inter-BSS activity
 *      NPCA Switching Delay -
 *              Time needed by an NPCA AP to switch from the
 *              BSS primary channel to the NPCA primary channel
 *              in the unit of 4 µs.
 *      NPCA Switching Back Delay -
 *              Time to switch from the NPCA primary channel
 *              to the BSS primary channel in the unit of 4 µs.
 *      NPCA Initial QSRC -
 *              Initialize the EDCAF QSRC[AC] variables
 *              when an NPCA STA in the BSS
 *              switches to NPCA operation.
 *      NPCA MOPLEN -
 *              Indicates which conditions can be used to
 *              initiate an NPCA operation,
 *              1 -> both PHYLEN NPCA operation and MOPLEN
 *              NPCA operation are
 *              permitted in the BSS
 *              0 -> only PHYLEN NPCA operation is allowed in the BSS.
 *      NPCA Disabled Subchannel Bitmap Present -
 *              Indicates whether the NPCA Disabled Subchannel
 *              Bitmap field is present. A 1 in this field indicates that
 *              the NPCA Disabled Subchannel Bitmap field is present
 * @dis_subch_bmap:
 *              A bit in the bitmap that lies within the BSS bandwidth is set
 *              to 1 to indicate that the corresponding 20 MHz subchannel is
 *              punctured and is set to 0 to indicate that the corresponding
 *              20 MHz subchannel is not punctured. A bit in the bitmap that
 *              falls outside of the BSS bandwidth is reserved. This field is
 *              present when the value of the NPCA Disabled Subchannel Bitmap
 *              Field Present field is equal to 1, and not present, otherwise
 */
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 - DPS operation information
 *
 * This structure is the "DPS Operation Parameter field" of "UHR
 * Operation Element" fields as described in P802.11bn_D1.3
 * subclause 9.4.1.87. See Figure 9-207u.
 *
 * Refer to IEEE80211_UHR_DPS*
 * @params:
 *      DPS Padding Delay -
 *              Indicates the minimum MAC padding
 *              duration that is required by a DPS STA
 *              in an ICF to cause the STA to transition
 *              from the lower capability mode to the
 *              higher capability mode. The DPS Padding
 *              Delay field is in units of 4 µs.
 *      DPS Transition Delay -
 *              Indicates the amount of time required by a
 *              DPS STA to transition from the higher
 *              capability mode to the lower capability
 *              mode. The DPS Transition Delay field is in
 *              units of 4 µs.
 *      ICF Required -
 *              Indicates when the DPS assisting STA needs
 *              to transmit an ICF frame to the peer DPS STA
 *              before performing the frame exchanges with
 *              the peer DPS STA in a TXOP.
 *                      1 -> indicates that the transmission of the
 *                      ICF frame to the peer DPS STA prior to
 *                      any frame exchange is needed.
 *                      0 -> ICF transmission before the frame
 *                      exchanges with the peer DPS STA is only
 *                      needed if the frame exchange is performed
 *                      in the HC mode.
 *      Parameterized Flag -
 *              0 -> indicates that only 20 MHz, 1 SS,
 *              non-HT PPDU format with the data
 *              rate of 6, 12, and 24 Mb/s as the
 *              default mode are supported by the
 *              DPS STA in the LC mode
 *              1 -> indicates that a bandwidth up to the
 *              bandwidth indicated in the LC Mode
 *              Bandwidth field, a number of spatial
 *              streams up to the NSS indicated in
 *              the LC Mode Nss field, and an MCS up
 *              to the MCS indicated in the LC Mode
 *              MCS fields are supported by the DPS
 *              STA in the LC mode as the
 *              parameterized mode.
 *      LC Mode Bandwidth -
 *              Indicates the maximum bandwidth supported
 *              by the STA in the LC mode.
 *      LC Mode NSS -
 *              Indicates the maximum number of the spatial
 *              streams supported by the STA in the LC mode.
 *      LC Mode MCS -
 *              Indicates the highest MCS supported by the STA
 *              in the LC mode.
 *      Mobile AP DPS Static HCM -
 *              1 -> indicates that it will remain in the DPS high
 *              capability mode until the next TBTT on that
 *              link.
 *              0 -> otherwise.
 */
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 - DBE Operational Bandwidth
 *
 * Encoding for the DBE Operational Bandwidth field in the UHR Operation
 * element (DBE Operation Parameters).
 *
 * @IEEE80211_UHR_DBE_OPER_BW_40: 40 MHz operational DBE bandwidth
 * @IEEE80211_UHR_DBE_OPER_BW_80: 80 MHz operational DBE bandwidth
 * @IEEE80211_UHR_DBE_OPER_BW_160: 160 MHz operational DBE bandwidth
 * @IEEE80211_UHR_DBE_OPER_BW_320_1: 320-1 MHz operational DBE bandwidth
 * @IEEE80211_UHR_DBE_OPER_BW_320_2: 320-2 MHz operational DBE bandwidth
 */
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,
};

/**
 * ieee80211_uhr_dbe_bw_mhz - get bandwidth in MHz from UHR DBE bandwidth
 * @bw: UHR DBE bandwidth
 *
 * Return: the bandwidth in MHz, or -1 for invalid values
 */
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 - DBE operation information
 *
 * This structure is the "DBE Operation Parameters field" of
 * "UHR Operation Element" fields as described in P802.11bn_D1.3
 * subclause 9.4.2.353. See Figure 9-aa6.
 *
 * Refer to IEEE80211_UHR_DBE_OPER*
 * @params:
 *      B0-B2 - DBE Operational Bandwidth field, see
 *      "enum ieee80211_uhr_dbe_oper_bw" for values.
 *      Value 0 is reserved.
 *      Value 1 indicates 40 MHz operational DBE bandwidth.
 *      Value 2 indicates 80 MHz operational DBE bandwidth.
 *      Value 3 indicates 160 MHz operational DBE bandwidth.
 *      Value 4 indicates 320-1 MHz operational DBE bandwidth.
 *      Value 5 indicates 320-2 MHz operational DBE bandwidth.
 *      Values 6 to 7 are reserved.
 *      B3 - DBE Disabled Subchannel Bitmap Present.
 * @dis_subch_bmap: DBE Disabled Subchannel Bitmap field is set to indicate
 *      disabled 20 MHz subchannels within the DBE Bandwidth.
 */
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 - P-EDCA operation information
 *
 * This structure is the "P-EDCA Operation Parameters field" of
 * "UHR Operation Element" fields as described in P802.11bn_D1.3
 * subclause 9.4.2.353. See Figure 9-aa5.
 *
 * Refer to IEEE80211_UHR_P_EDCA*
 * @p_edca_ec: P-EDCA ECWmin and ECWmax.
 *      These fields indicate the CWmin and CWmax values used by a
 *      P-EDCA STA during P-EDCA contention.
 * @params: AIFSN, CW DS, PSRC threshold, and QSRC threshold.
 *      - The AIFSN field indicates the AIFSN value used by a P-EDCA STA
 *        during P-EDCA contention.
 *      - The CW DS field indicates the value used for randomization of the
 *        transmission slot of the DS-CTS frame. The value 3 is reserved.
 *        The value 0 indicates that randomization is not enabled.
 *      - The P-EDCA PSRC threshold field indicates the maximum number of
 *        allowed consecutive DS-CTS transmissions. The value 0 and values
 *        greater than 4 are reserved.
 *      - The P-EDCA QSRC threshold field indicates the value of the
 *        QSRC[AC_VO] counter required to start P-EDCA contention. The
 *        value 0 is reserved.
 */
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;

        /* nothing else present in beacons */
        if (beacon)
                return true;

        /* DPS Operation Parameters (fixed 4 bytes) */
        if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_ENA)) {
                needed += sizeof(struct ieee80211_uhr_dps_info);
                if (len < needed)
                        return false;
        }

        /* NPCA Operation Parameters (fixed 4 bytes + optional 2 bytes) */
        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;
                }
        }

        /* P-EDCA Operation Parameters (fixed 3 bytes) */
        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;
        }

        /* DBE Operation Parameters (fixed 1 byte + optional 2 bytes) */
        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;
}

/*
 * 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)
{
        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;
}

/*
 * 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_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;
        /* present 0, 1 or 2 times depending on _PRES bits */
        struct ieee80211_eht_mcs_nss_supp_bw eht_mcs_map[];
} __packed;

/**
 * enum ieee80211_uhr_dbe_max_supported_bw - DBE Maximum Supported Bandwidth
 *
 * As per spec P802.11bn_D1.3 "Table 9-bb5—Encoding of the DBE Maximum
 * Supported Bandwidth field".
 *
 * @IEEE80211_UHR_DBE_MAX_BW_40: Indicates 40 MHz DBE max supported bw
 * @IEEE80211_UHR_DBE_MAX_BW_80: Indicates 80 MHz DBE max supported bw
 * @IEEE80211_UHR_DBE_MAX_BW_160: Indicates 160 MHz DBE max supported bw
 * @IEEE80211_UHR_DBE_MAX_BW_320: Indicates 320 MHz DBE max supported bw
 */
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;
        /* optional DBE capabilities */
        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;

        /*
         * 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) {
                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

/**
 * ieee80211_uhr_capa_get_om_pu_to_us - get OM parameter update timeout in usec
 * @cap: the UHR capability element, size must be validated
 *
 * Return: the OM parameter update timeout in usec, or -1 if it's not valid
 */
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);
}

/* only valid from AP, must check ieee80211_uhr_capa_size_ok(..., true) */
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 /* LINUX_IEEE80211_UHR_H */