summaryrefslogtreecommitdiff
path: root/include/linux/ieee80211-uhr.h
blob: 132acced7d798247eda400a814df60947a10e2a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/* 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/if_ether.h>

#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 */