| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* |
| 3 | * IEEE 802.11 S1G definitions |
| 4 | * |
| 5 | * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen |
| 6 | * <jkmaline@cc.hut.fi> |
| 7 | * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> |
| 8 | * Copyright (c) 2005, Devicescape Software, Inc. |
| 9 | * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> |
| 10 | * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH |
| 11 | * Copyright (c) 2016 - 2017 Intel Deutschland GmbH |
| 12 | * Copyright (c) 2018 - 2025 Intel Corporation |
| 13 | */ |
| 14 | |
| 15 | #ifndef LINUX_IEEE80211_S1G_H |
| 16 | #define LINUX_IEEE80211_S1G_H |
| 17 | |
| 18 | #include <linux/types.h> |
| 19 | #include <linux/if_ether.h> |
| 20 | |
| 21 | /* bits unique to S1G beacon frame control */ |
| 22 | #define IEEE80211_S1G_BCN_NEXT_TBTT 0x100 |
| 23 | #define IEEE80211_S1G_BCN_CSSID 0x200 |
| 24 | #define IEEE80211_S1G_BCN_ANO 0x400 |
| 25 | |
| 26 | /* see 802.11ah-2016 9.9 NDP CMAC frames */ |
| 27 | #define IEEE80211_S1G_1MHZ_NDP_BITS 25 |
| 28 | #define IEEE80211_S1G_1MHZ_NDP_BYTES 4 |
| 29 | #define IEEE80211_S1G_2MHZ_NDP_BITS 37 |
| 30 | #define IEEE80211_S1G_2MHZ_NDP_BYTES 5 |
| 31 | |
| 32 | /** |
| 33 | * ieee80211_is_s1g_beacon - check if IEEE80211_FTYPE_EXT && |
| 34 | * IEEE80211_STYPE_S1G_BEACON |
| 35 | * @fc: frame control bytes in little-endian byteorder |
| 36 | * Return: whether or not the frame is an S1G beacon |
| 37 | */ |
| 38 | static inline bool ieee80211_is_s1g_beacon(__le16 fc) |
| 39 | { |
| 40 | return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | |
| 41 | IEEE80211_FCTL_STYPE)) == |
| 42 | cpu_to_le16(IEEE80211_FTYPE_EXT | IEEE80211_STYPE_S1G_BEACON); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * ieee80211_s1g_has_next_tbtt - check if IEEE80211_S1G_BCN_NEXT_TBTT |
| 47 | * @fc: frame control bytes in little-endian byteorder |
| 48 | * Return: whether or not the frame contains the variable-length |
| 49 | * next TBTT field |
| 50 | */ |
| 51 | static inline bool ieee80211_s1g_has_next_tbtt(__le16 fc) |
| 52 | { |
| 53 | return ieee80211_is_s1g_beacon(fc) && |
| 54 | (fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT)); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * ieee80211_s1g_has_ano - check if IEEE80211_S1G_BCN_ANO |
| 59 | * @fc: frame control bytes in little-endian byteorder |
| 60 | * Return: whether or not the frame contains the variable-length |
| 61 | * ANO field |
| 62 | */ |
| 63 | static inline bool ieee80211_s1g_has_ano(__le16 fc) |
| 64 | { |
| 65 | return ieee80211_is_s1g_beacon(fc) && |
| 66 | (fc & cpu_to_le16(IEEE80211_S1G_BCN_ANO)); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * ieee80211_s1g_has_cssid - check if IEEE80211_S1G_BCN_CSSID |
| 71 | * @fc: frame control bytes in little-endian byteorder |
| 72 | * Return: whether or not the frame contains the variable-length |
| 73 | * compressed SSID field |
| 74 | */ |
| 75 | static inline bool ieee80211_s1g_has_cssid(__le16 fc) |
| 76 | { |
| 77 | return ieee80211_is_s1g_beacon(fc) && |
| 78 | (fc & cpu_to_le16(IEEE80211_S1G_BCN_CSSID)); |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * enum ieee80211_s1g_chanwidth - S1G channel widths |
| 83 | * These are defined in IEEE802.11-2016ah Table 10-20 |
| 84 | * as BSS Channel Width |
| 85 | * |
| 86 | * @IEEE80211_S1G_CHANWIDTH_1MHZ: 1MHz operating channel |
| 87 | * @IEEE80211_S1G_CHANWIDTH_2MHZ: 2MHz operating channel |
| 88 | * @IEEE80211_S1G_CHANWIDTH_4MHZ: 4MHz operating channel |
| 89 | * @IEEE80211_S1G_CHANWIDTH_8MHZ: 8MHz operating channel |
| 90 | * @IEEE80211_S1G_CHANWIDTH_16MHZ: 16MHz operating channel |
| 91 | */ |
| 92 | enum ieee80211_s1g_chanwidth { |
| 93 | IEEE80211_S1G_CHANWIDTH_1MHZ = 0, |
| 94 | IEEE80211_S1G_CHANWIDTH_2MHZ = 1, |
| 95 | IEEE80211_S1G_CHANWIDTH_4MHZ = 3, |
| 96 | IEEE80211_S1G_CHANWIDTH_8MHZ = 7, |
| 97 | IEEE80211_S1G_CHANWIDTH_16MHZ = 15, |
| 98 | }; |
| 99 | |
| 100 | /** |
| 101 | * enum ieee80211_s1g_pri_chanwidth - S1G primary channel widths |
| 102 | * described in IEEE80211-2024 Table 10-39. |
| 103 | * |
| 104 | * @IEEE80211_S1G_PRI_CHANWIDTH_2MHZ: 2MHz primary channel |
| 105 | * @IEEE80211_S1G_PRI_CHANWIDTH_1MHZ: 1MHz primary channel |
| 106 | */ |
| 107 | enum ieee80211_s1g_pri_chanwidth { |
| 108 | IEEE80211_S1G_PRI_CHANWIDTH_2MHZ = 0, |
| 109 | IEEE80211_S1G_PRI_CHANWIDTH_1MHZ = 1, |
| 110 | }; |
| 111 | |
| 112 | /** |
| 113 | * struct ieee80211_s1g_bcn_compat_ie - S1G Beacon Compatibility element |
| 114 | * @compat_info: Compatibility Information |
| 115 | * @beacon_int: Beacon Interval |
| 116 | * @tsf_completion: TSF Completion |
| 117 | * |
| 118 | * This structure represents the payload of the "S1G Beacon |
| 119 | * Compatibility element" as described in IEEE Std 802.11-2020 section |
| 120 | * 9.4.2.196. |
| 121 | */ |
| 122 | struct ieee80211_s1g_bcn_compat_ie { |
| 123 | __le16 compat_info; |
| 124 | __le16 beacon_int; |
| 125 | __le32 tsf_completion; |
| 126 | } __packed; |
| 127 | |
| 128 | /** |
| 129 | * struct ieee80211_s1g_oper_ie - S1G Operation element |
| 130 | * @ch_width: S1G Operation Information Channel Width |
| 131 | * @oper_class: S1G Operation Information Operating Class |
| 132 | * @primary_ch: S1G Operation Information Primary Channel Number |
| 133 | * @oper_ch: S1G Operation Information Channel Center Frequency |
| 134 | * @basic_mcs_nss: Basic S1G-MCS and NSS Set |
| 135 | * |
| 136 | * This structure represents the payload of the "S1G Operation |
| 137 | * element" as described in IEEE Std 802.11-2020 section 9.4.2.212. |
| 138 | */ |
| 139 | struct ieee80211_s1g_oper_ie { |
| 140 | u8 ch_width; |
| 141 | u8 oper_class; |
| 142 | u8 primary_ch; |
| 143 | u8 oper_ch; |
| 144 | __le16 basic_mcs_nss; |
| 145 | } __packed; |
| 146 | |
| 147 | /** |
| 148 | * struct ieee80211_aid_response_ie - AID Response element |
| 149 | * @aid: AID/Group AID |
| 150 | * @switch_count: AID Switch Count |
| 151 | * @response_int: AID Response Interval |
| 152 | * |
| 153 | * This structure represents the payload of the "AID Response element" |
| 154 | * as described in IEEE Std 802.11-2020 section 9.4.2.194. |
| 155 | */ |
| 156 | struct ieee80211_aid_response_ie { |
| 157 | __le16 aid; |
| 158 | u8 switch_count; |
| 159 | __le16 response_int; |
| 160 | } __packed; |
| 161 | |
| 162 | struct ieee80211_s1g_cap { |
| 163 | u8 capab_info[10]; |
| 164 | u8 supp_mcs_nss[5]; |
| 165 | } __packed; |
| 166 | |
| 167 | /** |
| 168 | * ieee80211_s1g_optional_len - determine length of optional S1G beacon fields |
| 169 | * @fc: frame control bytes in little-endian byteorder |
| 170 | * Return: total length in bytes of the optional fixed-length fields |
| 171 | * |
| 172 | * S1G beacons may contain up to three optional fixed-length fields that |
| 173 | * precede the variable-length elements. Whether these fields are present |
| 174 | * is indicated by flags in the frame control field. |
| 175 | * |
| 176 | * From IEEE 802.11-2024 section 9.3.4.3: |
| 177 | * - Next TBTT field may be 0 or 3 bytes |
| 178 | * - Short SSID field may be 0 or 4 bytes |
| 179 | * - Access Network Options (ANO) field may be 0 or 1 byte |
| 180 | */ |
| 181 | static inline size_t |
| 182 | ieee80211_s1g_optional_len(__le16 fc) |
| 183 | { |
| 184 | size_t len = 0; |
| 185 | |
| 186 | if (ieee80211_s1g_has_next_tbtt(fc)) |
| 187 | len += 3; |
| 188 | |
| 189 | if (ieee80211_s1g_has_cssid(fc)) |
| 190 | len += 4; |
| 191 | |
| 192 | if (ieee80211_s1g_has_ano(fc)) |
| 193 | len += 1; |
| 194 | |
| 195 | return len; |
| 196 | } |
| 197 | |
| 198 | /* S1G Capabilities Information field */ |
| 199 | #define IEEE80211_S1G_CAPABILITY_LEN 15 |
| 200 | |
| 201 | #define S1G_CAP0_S1G_LONG BIT(0) |
| 202 | #define S1G_CAP0_SGI_1MHZ BIT(1) |
| 203 | #define S1G_CAP0_SGI_2MHZ BIT(2) |
| 204 | #define S1G_CAP0_SGI_4MHZ BIT(3) |
| 205 | #define S1G_CAP0_SGI_8MHZ BIT(4) |
| 206 | #define S1G_CAP0_SGI_16MHZ BIT(5) |
| 207 | #define S1G_CAP0_SUPP_CH_WIDTH GENMASK(7, 6) |
| 208 | |
| 209 | #define S1G_SUPP_CH_WIDTH_2 0 |
| 210 | #define S1G_SUPP_CH_WIDTH_4 1 |
| 211 | #define S1G_SUPP_CH_WIDTH_8 2 |
| 212 | #define S1G_SUPP_CH_WIDTH_16 3 |
| 213 | #define S1G_SUPP_CH_WIDTH_MAX(cap) ((1 << FIELD_GET(S1G_CAP0_SUPP_CH_WIDTH, \ |
| 214 | cap[0])) << 1) |
| 215 | |
| 216 | #define S1G_CAP1_RX_LDPC BIT(0) |
| 217 | #define S1G_CAP1_TX_STBC BIT(1) |
| 218 | #define S1G_CAP1_RX_STBC BIT(2) |
| 219 | #define S1G_CAP1_SU_BFER BIT(3) |
| 220 | #define S1G_CAP1_SU_BFEE BIT(4) |
| 221 | #define S1G_CAP1_BFEE_STS GENMASK(7, 5) |
| 222 | |
| 223 | #define S1G_CAP2_SOUNDING_DIMENSIONS GENMASK(2, 0) |
| 224 | #define S1G_CAP2_MU_BFER BIT(3) |
| 225 | #define S1G_CAP2_MU_BFEE BIT(4) |
| 226 | #define S1G_CAP2_PLUS_HTC_VHT BIT(5) |
| 227 | #define S1G_CAP2_TRAVELING_PILOT GENMASK(7, 6) |
| 228 | |
| 229 | #define S1G_CAP3_RD_RESPONDER BIT(0) |
| 230 | #define S1G_CAP3_HT_DELAYED_BA BIT(1) |
| 231 | #define S1G_CAP3_MAX_MPDU_LEN BIT(2) |
| 232 | #define S1G_CAP3_MAX_AMPDU_LEN_EXP GENMASK(4, 3) |
| 233 | #define S1G_CAP3_MIN_MPDU_START GENMASK(7, 5) |
| 234 | |
| 235 | #define S1G_CAP4_UPLINK_SYNC BIT(0) |
| 236 | #define S1G_CAP4_DYNAMIC_AID BIT(1) |
| 237 | #define S1G_CAP4_BAT BIT(2) |
| 238 | #define S1G_CAP4_TIME_ADE BIT(3) |
| 239 | #define S1G_CAP4_NON_TIM BIT(4) |
| 240 | #define S1G_CAP4_GROUP_AID BIT(5) |
| 241 | #define S1G_CAP4_STA_TYPE GENMASK(7, 6) |
| 242 | |
| 243 | #define S1G_CAP5_CENT_AUTH_CONTROL BIT(0) |
| 244 | #define S1G_CAP5_DIST_AUTH_CONTROL BIT(1) |
| 245 | #define S1G_CAP5_AMSDU BIT(2) |
| 246 | #define S1G_CAP5_AMPDU BIT(3) |
| 247 | #define S1G_CAP5_ASYMMETRIC_BA BIT(4) |
| 248 | #define S1G_CAP5_FLOW_CONTROL BIT(5) |
| 249 | #define S1G_CAP5_SECTORIZED_BEAM GENMASK(7, 6) |
| 250 | |
| 251 | #define S1G_CAP6_OBSS_MITIGATION BIT(0) |
| 252 | #define S1G_CAP6_FRAGMENT_BA BIT(1) |
| 253 | #define S1G_CAP6_NDP_PS_POLL BIT(2) |
| 254 | #define S1G_CAP6_RAW_OPERATION BIT(3) |
| 255 | #define S1G_CAP6_PAGE_SLICING BIT(4) |
| 256 | #define S1G_CAP6_TXOP_SHARING_IMP_ACK BIT(5) |
| 257 | #define S1G_CAP6_VHT_LINK_ADAPT GENMASK(7, 6) |
| 258 | |
| 259 | #define S1G_CAP7_TACK_AS_PS_POLL BIT(0) |
| 260 | #define S1G_CAP7_DUP_1MHZ BIT(1) |
| 261 | #define S1G_CAP7_MCS_NEGOTIATION BIT(2) |
| 262 | #define S1G_CAP7_1MHZ_CTL_RESPONSE_PREAMBLE BIT(3) |
| 263 | #define S1G_CAP7_NDP_BFING_REPORT_POLL BIT(4) |
| 264 | #define S1G_CAP7_UNSOLICITED_DYN_AID BIT(5) |
| 265 | #define S1G_CAP7_SECTOR_TRAINING_OPERATION BIT(6) |
| 266 | #define S1G_CAP7_TEMP_PS_MODE_SWITCH BIT(7) |
| 267 | |
| 268 | #define S1G_CAP8_TWT_GROUPING BIT(0) |
| 269 | #define S1G_CAP8_BDT BIT(1) |
| 270 | #define S1G_CAP8_COLOR GENMASK(4, 2) |
| 271 | #define S1G_CAP8_TWT_REQUEST BIT(5) |
| 272 | #define S1G_CAP8_TWT_RESPOND BIT(6) |
| 273 | #define S1G_CAP8_PV1_FRAME BIT(7) |
| 274 | |
| 275 | #define S1G_CAP9_LINK_ADAPT_PER_CONTROL_RESPONSE BIT(0) |
| 276 | |
| 277 | #define S1G_OPER_CH_WIDTH_PRIMARY BIT(0) |
| 278 | #define S1G_OPER_CH_WIDTH_OPER GENMASK(4, 1) |
| 279 | #define S1G_OPER_CH_PRIMARY_LOCATION BIT(5) |
| 280 | |
| 281 | #define S1G_2M_PRIMARY_LOCATION_LOWER 0 |
| 282 | #define S1G_2M_PRIMARY_LOCATION_UPPER 1 |
| 283 | |
| 284 | #define LISTEN_INT_USF GENMASK(15, 14) |
| 285 | #define LISTEN_INT_UI GENMASK(13, 0) |
| 286 | |
| 287 | #define IEEE80211_MAX_USF FIELD_MAX(LISTEN_INT_USF) |
| 288 | #define IEEE80211_MAX_UI FIELD_MAX(LISTEN_INT_UI) |
| 289 | |
| 290 | /* S1G encoding types */ |
| 291 | #define IEEE80211_S1G_TIM_ENC_MODE_BLOCK 0 |
| 292 | #define IEEE80211_S1G_TIM_ENC_MODE_SINGLE 1 |
| 293 | #define IEEE80211_S1G_TIM_ENC_MODE_OLB 2 |
| 294 | |
| 295 | enum ieee80211_s1g_actioncode { |
| 296 | WLAN_S1G_AID_SWITCH_REQUEST, |
| 297 | WLAN_S1G_AID_SWITCH_RESPONSE, |
| 298 | WLAN_S1G_SYNC_CONTROL, |
| 299 | WLAN_S1G_STA_INFO_ANNOUNCE, |
| 300 | WLAN_S1G_EDCA_PARAM_SET, |
| 301 | WLAN_S1G_EL_OPERATION, |
| 302 | WLAN_S1G_TWT_SETUP, |
| 303 | WLAN_S1G_TWT_TEARDOWN, |
| 304 | WLAN_S1G_SECT_GROUP_ID_LIST, |
| 305 | WLAN_S1G_SECT_ID_FEEDBACK, |
| 306 | WLAN_S1G_TWT_INFORMATION = 11, |
| 307 | }; |
| 308 | |
| 309 | /** |
| 310 | * ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon |
| 311 | * @fc: frame control bytes in little-endian byteorder |
| 312 | * @variable: pointer to the beacon frame elements |
| 313 | * @variable_len: length of the frame elements |
| 314 | * Return: whether or not the frame is an S1G short beacon. As per |
| 315 | * IEEE80211-2024 11.1.3.10.1, The S1G beacon compatibility element shall |
| 316 | * always be present as the first element in beacon frames generated at a |
| 317 | * TBTT (Target Beacon Transmission Time), so any frame not containing |
| 318 | * this element must have been generated at a TSBTT (Target Short Beacon |
| 319 | * Transmission Time) that is not a TBTT. Additionally, short beacons are |
| 320 | * prohibited from containing the S1G beacon compatibility element as per |
| 321 | * IEEE80211-2024 9.3.4.3 Table 9-76, so if we have an S1G beacon with |
| 322 | * either no elements or the first element is not the beacon compatibility |
| 323 | * element, we have a short beacon. |
| 324 | */ |
| 325 | static inline bool ieee80211_is_s1g_short_beacon(__le16 fc, const u8 *variable, |
| 326 | size_t variable_len) |
| 327 | { |
| 328 | if (!ieee80211_is_s1g_beacon(fc)) |
| 329 | return false; |
| 330 | |
| 331 | /* |
| 332 | * If the frame does not contain at least 1 element (this is perfectly |
| 333 | * valid in a short beacon) and is an S1G beacon, we have a short |
| 334 | * beacon. |
| 335 | */ |
| 336 | if (variable_len < 2) |
| 337 | return true; |
| 338 | |
| 339 | return variable[0] != WLAN_EID_S1G_BCN_COMPAT; |
| 340 | } |
| 341 | |
| 342 | struct s1g_tim_aid { |
| 343 | u16 aid; |
| 344 | u8 target_blk; /* Target block index */ |
| 345 | u8 target_subblk; /* Target subblock index */ |
| 346 | u8 target_subblk_bit; /* Target subblock bit */ |
| 347 | }; |
| 348 | |
| 349 | struct s1g_tim_enc_block { |
| 350 | u8 enc_mode; |
| 351 | bool inverse; |
| 352 | const u8 *ptr; |
| 353 | u8 len; |
| 354 | |
| 355 | /* |
| 356 | * For an OLB encoded block that spans multiple blocks, this |
| 357 | * is the offset into the span described by that encoded block. |
| 358 | */ |
| 359 | u8 olb_blk_offset; |
| 360 | }; |
| 361 | |
| 362 | /* |
| 363 | * Helper routines to quickly extract the length of an encoded block. Validation |
| 364 | * is also performed to ensure the length extracted lies within the TIM. |
| 365 | */ |
| 366 | |
| 367 | static inline int ieee80211_s1g_len_bitmap(const u8 *ptr, const u8 *end) |
| 368 | { |
| 369 | u8 blkmap; |
| 370 | u8 n_subblks; |
| 371 | |
| 372 | if (ptr >= end) |
| 373 | return -EINVAL; |
| 374 | |
| 375 | blkmap = *ptr; |
| 376 | n_subblks = hweight8(blkmap); |
| 377 | |
| 378 | if (ptr + 1 + n_subblks > end) |
| 379 | return -EINVAL; |
| 380 | |
| 381 | return 1 + n_subblks; |
| 382 | } |
| 383 | |
| 384 | static inline int ieee80211_s1g_len_single(const u8 *ptr, const u8 *end) |
| 385 | { |
| 386 | return (ptr + 1 > end) ? -EINVAL : 1; |
| 387 | } |
| 388 | |
| 389 | static inline int ieee80211_s1g_len_olb(const u8 *ptr, const u8 *end) |
| 390 | { |
| 391 | if (ptr >= end) |
| 392 | return -EINVAL; |
| 393 | |
| 394 | return (ptr + 1 + *ptr > end) ? -EINVAL : 1 + *ptr; |
| 395 | } |
| 396 | |
| 397 | /* |
| 398 | * Enumerate all encoded blocks until we find the encoded block that describes |
| 399 | * our target AID. OLB is a special case as a single encoded block can describe |
| 400 | * multiple blocks as a single encoded block. |
| 401 | */ |
| 402 | static inline int ieee80211_s1g_find_target_block(struct s1g_tim_enc_block *enc, |
| 403 | const struct s1g_tim_aid *aid, |
| 404 | const u8 *ptr, const u8 *end) |
| 405 | { |
| 406 | /* need at least block-control octet */ |
| 407 | while (ptr + 1 <= end) { |
| 408 | u8 ctrl = *ptr++; |
| 409 | u8 mode = ctrl & 0x03; |
| 410 | bool contains, inverse = ctrl & BIT(2); |
| 411 | u8 span, blk_off = ctrl >> 3; |
| 412 | int len; |
| 413 | |
| 414 | switch (mode) { |
| 415 | case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: |
| 416 | len = ieee80211_s1g_len_bitmap(ptr, end); |
| 417 | contains = blk_off == aid->target_blk; |
| 418 | break; |
| 419 | case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: |
| 420 | len = ieee80211_s1g_len_single(ptr, end); |
| 421 | contains = blk_off == aid->target_blk; |
| 422 | break; |
| 423 | case IEEE80211_S1G_TIM_ENC_MODE_OLB: |
| 424 | len = ieee80211_s1g_len_olb(ptr, end); |
| 425 | /* |
| 426 | * An OLB encoded block can describe more then one |
| 427 | * block, meaning an encoded OLB block can span more |
| 428 | * then a single block. |
| 429 | */ |
| 430 | if (len > 0) { |
| 431 | /* Minus one for the length octet */ |
| 432 | span = DIV_ROUND_UP(len - 1, 8); |
| 433 | /* |
| 434 | * Check if our target block lies within the |
| 435 | * block span described by this encoded block. |
| 436 | */ |
| 437 | contains = (aid->target_blk >= blk_off) && |
| 438 | (aid->target_blk < blk_off + span); |
| 439 | } |
| 440 | break; |
| 441 | default: |
| 442 | return -EOPNOTSUPP; |
| 443 | } |
| 444 | |
| 445 | if (len < 0) |
| 446 | return len; |
| 447 | |
| 448 | if (contains) { |
| 449 | enc->enc_mode = mode; |
| 450 | enc->inverse = inverse; |
| 451 | enc->ptr = ptr; |
| 452 | enc->len = (u8)len; |
| 453 | enc->olb_blk_offset = blk_off; |
| 454 | return 0; |
| 455 | } |
| 456 | |
| 457 | ptr += len; |
| 458 | } |
| 459 | |
| 460 | return -ENOENT; |
| 461 | } |
| 462 | |
| 463 | static inline bool ieee80211_s1g_parse_bitmap(struct s1g_tim_enc_block *enc, |
| 464 | struct s1g_tim_aid *aid) |
| 465 | { |
| 466 | const u8 *ptr = enc->ptr; |
| 467 | u8 blkmap = *ptr++; |
| 468 | |
| 469 | /* |
| 470 | * If our block bitmap does not contain a set bit that corresponds |
| 471 | * to our AID, it could mean a variety of things depending on if |
| 472 | * the encoding mode is inverted or not. |
| 473 | * |
| 474 | * 1. If inverted, it means the entire subblock is present and hence |
| 475 | * our AID has been set. |
| 476 | * 2. If not inverted, it means our subblock is not present and hence |
| 477 | * it is all zero meaning our AID is not set. |
| 478 | */ |
| 479 | if (!(blkmap & BIT(aid->target_subblk))) |
| 480 | return enc->inverse; |
| 481 | |
| 482 | /* |
| 483 | * Increment ptr by the number of set subblocks that appear before our |
| 484 | * target subblock. If our target subblock is 0, do nothing as ptr |
| 485 | * already points to our target subblock. |
| 486 | */ |
| 487 | if (aid->target_subblk) |
| 488 | ptr += hweight8(blkmap & GENMASK(aid->target_subblk - 1, 0)); |
| 489 | |
| 490 | return !!(*ptr & BIT(aid->target_subblk_bit)) ^ enc->inverse; |
| 491 | } |
| 492 | |
| 493 | static inline bool ieee80211_s1g_parse_single(struct s1g_tim_enc_block *enc, |
| 494 | struct s1g_tim_aid *aid) |
| 495 | { |
| 496 | /* |
| 497 | * Single AID mode describes, as the name suggests, a single AID |
| 498 | * within the block described by the encoded block. The octet |
| 499 | * contains the 6 LSBs of the AID described in the block. The other |
| 500 | * 2 bits are reserved. When inversed, every single AID described |
| 501 | * by the current block have buffered traffic except for the AID |
| 502 | * described in the single AID octet. |
| 503 | */ |
| 504 | return ((*enc->ptr & 0x3f) == (aid->aid & 0x3f)) ^ enc->inverse; |
| 505 | } |
| 506 | |
| 507 | static inline bool ieee80211_s1g_parse_olb(struct s1g_tim_enc_block *enc, |
| 508 | struct s1g_tim_aid *aid) |
| 509 | { |
| 510 | const u8 *ptr = enc->ptr; |
| 511 | u8 blk_len = *ptr++; |
| 512 | /* |
| 513 | * Given an OLB encoded block that describes multiple blocks, |
| 514 | * calculate the offset into the span. Then calculate the |
| 515 | * subblock location normally. |
| 516 | */ |
| 517 | u16 span_offset = aid->target_blk - enc->olb_blk_offset; |
| 518 | u16 subblk_idx = span_offset * 8 + aid->target_subblk; |
| 519 | |
| 520 | if (subblk_idx >= blk_len) |
| 521 | return enc->inverse; |
| 522 | |
| 523 | return !!(ptr[subblk_idx] & BIT(aid->target_subblk_bit)) ^ enc->inverse; |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | * An S1G PVB has 3 non optional encoding types, each that can be inverted. |
| 528 | * An S1G PVB is constructed with zero or more encoded block subfields. Each |
| 529 | * encoded block represents a single "block" of AIDs (64), and each encoded |
| 530 | * block can contain one of the 3 encoding types alongside a single bit for |
| 531 | * whether the bits should be inverted. |
| 532 | * |
| 533 | * As the standard makes no guarantee about the ordering of encoded blocks, |
| 534 | * we must parse every encoded block in the worst case scenario given an |
| 535 | * AID that lies within the last block. |
| 536 | */ |
| 537 | static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim, |
| 538 | u8 tim_len, u16 aid) |
| 539 | { |
| 540 | int err; |
| 541 | struct s1g_tim_aid target_aid; |
| 542 | struct s1g_tim_enc_block enc_blk; |
| 543 | |
| 544 | if (tim_len < 3) |
| 545 | return false; |
| 546 | |
| 547 | target_aid.aid = aid; |
| 548 | target_aid.target_blk = (aid >> 6) & 0x1f; |
| 549 | target_aid.target_subblk = (aid >> 3) & 0x7; |
| 550 | target_aid.target_subblk_bit = aid & 0x7; |
| 551 | |
| 552 | /* |
| 553 | * Find our AIDs target encoded block and fill &enc_blk with the |
| 554 | * encoded blocks information. If no entry is found or an error |
| 555 | * occurs return false. |
| 556 | */ |
| 557 | err = ieee80211_s1g_find_target_block(enc: &enc_blk, aid: &target_aid, |
| 558 | ptr: tim->virtual_map, |
| 559 | end: (const u8 *)tim + tim_len + 2); |
| 560 | if (err) |
| 561 | return false; |
| 562 | |
| 563 | switch (enc_blk.enc_mode) { |
| 564 | case IEEE80211_S1G_TIM_ENC_MODE_BLOCK: |
| 565 | return ieee80211_s1g_parse_bitmap(enc: &enc_blk, aid: &target_aid); |
| 566 | case IEEE80211_S1G_TIM_ENC_MODE_SINGLE: |
| 567 | return ieee80211_s1g_parse_single(enc: &enc_blk, aid: &target_aid); |
| 568 | case IEEE80211_S1G_TIM_ENC_MODE_OLB: |
| 569 | return ieee80211_s1g_parse_olb(enc: &enc_blk, aid: &target_aid); |
| 570 | default: |
| 571 | return false; |
| 572 | } |
| 573 | } |
| 574 | |
| 575 | #endif /* LINUX_IEEE80211_H */ |
| 576 | |