developer | 66e89bc | 2024-04-23 14:50:01 +0800 | [diff] [blame] | 1 | From 1bc6ab0abbb6f26f35d826d166d06bc28ae47b6b Mon Sep 17 00:00:00 2001 |
| 2 | From: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com> |
| 3 | Date: Tue, 28 Feb 2023 12:01:27 +1300 |
| 4 | Subject: [PATCH 15/28] iw: S1G: add parsing for 802.11ah scan IE's |
| 5 | |
| 6 | In order to support scan display for 802.11ah, this change adds |
| 7 | parsing for S1G capabilities, operation, and short beacon interval |
| 8 | information elements. |
| 9 | |
| 10 | Signed-off-by: Gilad Itzkovitch <gilad.itzkovitch@morsemicro.com> |
| 11 | Link: https://lore.kernel.org/r/20230227230127.709496-1-gilad.itzkovitch@virscient.com |
| 12 | Signed-off-by: Johannes Berg <johannes.berg@intel.com> |
| 13 | --- |
| 14 | iw.h | 5 ++ |
| 15 | scan.c | 109 ++++++++++++++++++++++++++++- |
| 16 | util.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 17 | 3 files changed, 330 insertions(+), 2 deletions(-) |
| 18 | |
| 19 | diff --git a/iw.h b/iw.h |
| 20 | index 19c76cf..7e9107e 100644 |
| 21 | --- a/iw.h |
| 22 | +++ b/iw.h |
| 23 | @@ -224,6 +224,7 @@ void print_vht_info(__u32 capa, const __u8 *mcs); |
| 24 | void print_he_capability(const uint8_t *ie, int len); |
| 25 | void print_he_info(struct nlattr *nl_iftype); |
| 26 | void print_eht_info(struct nlattr *nl_iftype, int band); |
| 27 | +void print_s1g_capability(const uint8_t *caps); |
| 28 | |
| 29 | char *channel_width_name(enum nl80211_chan_width width); |
| 30 | const char *iftype_name(enum nl80211_iftype iftype); |
| 31 | @@ -261,6 +262,9 @@ int get_cf1(const struct chanmode *chanmode, unsigned long freq); |
| 32 | |
| 33 | int parse_random_mac_addr(struct nl_msg *msg, char *addrs); |
| 34 | |
| 35 | +char *s1g_ss_max_support(__u8 maxss); |
| 36 | +char *s1g_ss_min_support(__u8 minss); |
| 37 | + |
| 38 | #define SCHED_SCAN_OPTIONS "[interval <in_msecs> | scan_plans [<interval_secs:iterations>*] <interval_secs>] " \ |
| 39 | "[delay <in_secs>] [freqs <freq>+] [matches [ssid <ssid>]+]] [active [ssid <ssid>]+|passive] " \ |
| 40 | "[randomise[=<addr>/<mask>]] [coloc] [flush]" |
| 41 | @@ -274,6 +278,7 @@ char *hex2bin(const char *hex, char *buf); |
| 42 | int set_bitrates(struct nl_msg *msg, int argc, char **argv, |
| 43 | enum nl80211_attrs attr); |
| 44 | |
| 45 | +int calc_s1g_ch_center_freq(__u8 ch_index, __u8 s1g_oper_class); |
| 46 | |
| 47 | /* sections */ |
| 48 | DECLARE_SECTION(ap); |
| 49 | diff --git a/scan.c b/scan.c |
| 50 | index dc26787..7479220 100644 |
| 51 | --- a/scan.c |
| 52 | +++ b/scan.c |
| 53 | @@ -1675,6 +1675,101 @@ static void print_mesh_conf(const uint8_t type, uint8_t len, |
| 54 | printf("\t\t\t Mesh Power Save Level\n"); |
| 55 | } |
| 56 | |
| 57 | +static void print_s1g_capa(const uint8_t type, uint8_t len, |
| 58 | + const uint8_t *data, |
| 59 | + const struct print_ies_data *ie_buffer) |
| 60 | +{ |
| 61 | + printf("\n"); |
| 62 | + print_s1g_capability(data); |
| 63 | +} |
| 64 | + |
| 65 | +static void print_short_beacon_int(const uint8_t type, uint8_t len, |
| 66 | + const uint8_t *data, |
| 67 | + const struct print_ies_data *ie_buffer) |
| 68 | +{ |
| 69 | + printf(" %d\n", (data[1] << 8) | data[0]); |
| 70 | +} |
| 71 | + |
| 72 | +static void print_s1g_oper(const uint8_t type, uint8_t len, |
| 73 | + const uint8_t *data, |
| 74 | + const struct print_ies_data *ie_buffer) |
| 75 | +{ |
| 76 | + int oper_ch_width, prim_ch_width; |
| 77 | + int prim_ch_width_subfield = data[0] & 0x1; |
| 78 | + |
| 79 | + prim_ch_width = 2; |
| 80 | + |
| 81 | + /* B1-B4 BSS channel width subfield */ |
| 82 | + switch ((data[0] >> 1) & 0xf) { |
| 83 | + case 0: |
| 84 | + oper_ch_width = 1; |
| 85 | + prim_ch_width = 1; |
| 86 | + if (!prim_ch_width_subfield) { |
| 87 | + oper_ch_width = -1; |
| 88 | + prim_ch_width = -1; |
| 89 | + } |
| 90 | + break; |
| 91 | + case 1: |
| 92 | + oper_ch_width = 2; |
| 93 | + if (prim_ch_width_subfield) |
| 94 | + prim_ch_width = 1; |
| 95 | + break; |
| 96 | + case 3: |
| 97 | + oper_ch_width = 4; |
| 98 | + if (prim_ch_width_subfield) |
| 99 | + prim_ch_width = 1; |
| 100 | + break; |
| 101 | + case 7: |
| 102 | + oper_ch_width = 8; |
| 103 | + if (prim_ch_width_subfield) |
| 104 | + prim_ch_width = 1; |
| 105 | + break; |
| 106 | + case 15: |
| 107 | + oper_ch_width = 16; |
| 108 | + if (prim_ch_width_subfield) |
| 109 | + prim_ch_width = 1; |
| 110 | + break; |
| 111 | + default: |
| 112 | + oper_ch_width = -1; |
| 113 | + prim_ch_width = -1; |
| 114 | + break; |
| 115 | + } |
| 116 | + |
| 117 | + printf("\n"); |
| 118 | + printf("\t\tChannel width:\n"); |
| 119 | + if (oper_ch_width == -1 || prim_ch_width == -1) { |
| 120 | + printf("\t\t\tBSS primary channel width: invalid\n"); |
| 121 | + printf("\t\t\tBSS operating channel width: invalid\n"); |
| 122 | + } else { |
| 123 | + printf("\t\t\tBSS primary channel width: %d MHz\n", prim_ch_width); |
| 124 | + printf("\t\t\tBSS operating channel width: %d MHz\n", oper_ch_width); |
| 125 | + } |
| 126 | + if (data[0] & BIT(5)) |
| 127 | + printf("\t\t\t1 MHz primary channel located at the lower side of 2 MHz\n"); |
| 128 | + else |
| 129 | + printf("\t\t\t1 MHz primary channel located at the upper side of 2 MHz\n"); |
| 130 | + |
| 131 | + if (data[0] & BIT(7)) |
| 132 | + printf("\t\t\tMCS 10 not recommended\n"); |
| 133 | + |
| 134 | + printf("\t\t* operating class: %d\n", data[1]); |
| 135 | + printf("\t\t* primary channel number: %d\n", data[2]); |
| 136 | + |
| 137 | + printf("\t\t* channel index: %d\n", data[3]); |
| 138 | + |
| 139 | + printf("\t\tMax S1G MCS Map:\n"); |
| 140 | + printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support((data[4] >> 2) & 0x3)); |
| 141 | + printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((data[4] >> 6) & 0x3)); |
| 142 | + printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((data[5] >> 2) & 0x3)); |
| 143 | + printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support((data[5] >> 6) & 0x3)); |
| 144 | + |
| 145 | + printf("\t\tMin S1G MCS Map:\n"); |
| 146 | + printf("\t\t\tFor 1 SS: %s\n", s1g_ss_min_support(data[4] & 0x3)); |
| 147 | + printf("\t\t\tFor 2 SS: %s\n", s1g_ss_min_support((data[4] >> 4) & 0x3)); |
| 148 | + printf("\t\t\tFor 3 SS: %s\n", s1g_ss_min_support(data[5] & 0x3)); |
| 149 | + printf("\t\t\tFor 4 SS: %s\n", s1g_ss_min_support((data[5] >> 4) & 0x3)); |
| 150 | +} |
| 151 | + |
| 152 | struct ie_print { |
| 153 | const char *name; |
| 154 | void (*print)(const uint8_t type, uint8_t len, const uint8_t *data, |
| 155 | @@ -1748,6 +1843,9 @@ static const struct ie_print ieprinters[] = { |
| 156 | [108] = { "802.11u Advertisement", print_11u_advert, 0, 255, BIT(PRINT_SCAN), }, |
| 157 | [111] = { "802.11u Roaming Consortium", print_11u_rcon, 2, 255, BIT(PRINT_SCAN), }, |
| 158 | [195] = { "Transmit Power Envelope", print_tx_power_envelope, 2, 5, BIT(PRINT_SCAN), }, |
| 159 | + [214] = { "Short beacon interval", print_short_beacon_int, 2, 2, BIT(PRINT_SCAN), }, |
| 160 | + [217] = { "S1G capabilities", print_s1g_capa, 15, 15, BIT(PRINT_SCAN), }, |
| 161 | + [232] = { "S1G operation", print_s1g_oper, 6, 6, BIT(PRINT_SCAN), }, |
| 162 | }; |
| 163 | |
| 164 | static void print_wifi_wpa(const uint8_t type, uint8_t len, const uint8_t *data, |
| 165 | @@ -2326,7 +2424,8 @@ void print_ies(unsigned char *ie, int ielen, bool unknown, |
| 166 | while (ielen >= 2 && ielen - 2 >= ie[1]) { |
| 167 | if (ie[0] < ARRAY_SIZE(ieprinters) && |
| 168 | ieprinters[ie[0]].name && |
| 169 | - ieprinters[ie[0]].flags & BIT(ptype)) { |
| 170 | + ieprinters[ie[0]].flags & BIT(ptype) && |
| 171 | + ie[1] > 0) { |
| 172 | print_ie(&ieprinters[ie[0]], |
| 173 | ie[0], ie[1], ie + 2, &ie_buffer); |
| 174 | } else if (ie[0] == 221 /* vendor */) { |
| 175 | @@ -2419,6 +2518,7 @@ static int print_bss_handler(struct nl_msg *msg, void *arg) |
| 176 | static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { |
| 177 | [NL80211_BSS_TSF] = { .type = NLA_U64 }, |
| 178 | [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, |
| 179 | + [NL80211_BSS_FREQUENCY_OFFSET] = { .type = NLA_U32 }, |
| 180 | [NL80211_BSS_BSSID] = { }, |
| 181 | [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, |
| 182 | [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, |
| 183 | @@ -2491,7 +2591,12 @@ static int print_bss_handler(struct nl_msg *msg, void *arg) |
| 184 | } |
| 185 | if (bss[NL80211_BSS_FREQUENCY]) { |
| 186 | int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); |
| 187 | - printf("\tfreq: %d\n", freq); |
| 188 | + if (bss[NL80211_BSS_FREQUENCY_OFFSET]) |
| 189 | + printf("\tfreq: %d.%d\n", freq, |
| 190 | + nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET])); |
| 191 | + else |
| 192 | + printf("\tfreq: %d\n", freq); |
| 193 | + |
| 194 | if (freq > 45000) |
| 195 | is_dmg = true; |
| 196 | } |
| 197 | diff --git a/util.c b/util.c |
| 198 | index eef0332..18a97e1 100644 |
| 199 | --- a/util.c |
| 200 | +++ b/util.c |
| 201 | @@ -1841,3 +1841,221 @@ int parse_random_mac_addr(struct nl_msg *msg, char *addrs) |
| 202 | nla_put_failure: |
| 203 | return -ENOBUFS; |
| 204 | } |
| 205 | + |
| 206 | +char *s1g_ss_max_support(__u8 maxss) |
| 207 | +{ |
| 208 | + switch (maxss) { |
| 209 | + case 0: return "Max S1G-MCS 2"; |
| 210 | + case 1: return "Max S1G-MCS 7"; |
| 211 | + case 2: return "Max S1G-MCS 9"; |
| 212 | + case 3: return "Not supported"; |
| 213 | + default: return ""; |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +char *s1g_ss_min_support(__u8 minss) |
| 218 | +{ |
| 219 | + switch (minss) { |
| 220 | + case 0: return "no minimum restriction"; |
| 221 | + case 1: return "MCS 0 not recommended"; |
| 222 | + case 2: return "MCS 0 and 1 not recommended"; |
| 223 | + case 3: return "invalid"; |
| 224 | + default: return ""; |
| 225 | + } |
| 226 | +} |
| 227 | + |
| 228 | +void print_s1g_capability(const uint8_t *caps) |
| 229 | +{ |
| 230 | +#define PRINT_S1G_CAP(_cond, _str) \ |
| 231 | + do { \ |
| 232 | + if (_cond) \ |
| 233 | + printf("\t\t\t" _str "\n"); \ |
| 234 | + } while (0) |
| 235 | + |
| 236 | + static char buf[20]; |
| 237 | + int offset = 0; |
| 238 | + uint8_t cap = caps[0]; |
| 239 | + |
| 240 | + /* S1G Capabilities Information subfield */ |
| 241 | + if (cap) |
| 242 | + printf("\t\tByte[0]: 0x%02x\n", cap); |
| 243 | + |
| 244 | + PRINT_S1G_CAP((cap & BIT(0)), "S1G PHY: S1G_LONG PPDU Format"); |
| 245 | + |
| 246 | + if ((cap >> 1) & 0x1f) { |
| 247 | + offset = sprintf(buf, "SGI support:"); |
| 248 | + offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x1) ? " 1" : ""); |
| 249 | + offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x2) ? " 2" : ""); |
| 250 | + offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x4) ? " 4" : ""); |
| 251 | + offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x8) ? " 8" : ""); |
| 252 | + offset += sprintf(buf + offset, "%s", ((cap >> 1) & 0x10) ? " 16" : ""); |
| 253 | + offset += sprintf(buf + offset, " MHz"); |
| 254 | + printf("\t\t\t%s\n", buf); |
| 255 | + } |
| 256 | + |
| 257 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Channel width: 1, 2 MHz"); |
| 258 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Channel width: 1, 2, 4 MHz"); |
| 259 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Channel width: 1, 2, 4, 8 MHz"); |
| 260 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Channel width: 1, 2, 4, 8, 16 MHz"); |
| 261 | + |
| 262 | + cap = caps[1]; |
| 263 | + |
| 264 | + if (cap) |
| 265 | + printf("\t\tByte[1]: 0x%02x\n", cap); |
| 266 | + |
| 267 | + PRINT_S1G_CAP((cap & BIT(0)), "Rx LDPC"); |
| 268 | + PRINT_S1G_CAP((cap & BIT(1)), "Tx STBC"); |
| 269 | + PRINT_S1G_CAP((cap & BIT(2)), "Rx STBC"); |
| 270 | + PRINT_S1G_CAP((cap & BIT(3)), "SU Beamformer"); |
| 271 | + PRINT_S1G_CAP((cap & BIT(4)), "SU Beamformee"); |
| 272 | + if (cap & BIT(4)) |
| 273 | + printf("\t\t\tBeamformee STS: %d\n", (cap >> 5) + 1); |
| 274 | + |
| 275 | + cap = caps[2]; |
| 276 | + printf("\t\tByte[2]: 0x%02x\n", cap); |
| 277 | + |
| 278 | + if (caps[1] & BIT(3)) |
| 279 | + printf("\t\t\tSounding dimensions: %d\n", (cap & 0x7) + 1); |
| 280 | + |
| 281 | + PRINT_S1G_CAP((cap & BIT(3)), "MU Beamformer"); |
| 282 | + PRINT_S1G_CAP((cap & BIT(4)), "MU Beamformee"); |
| 283 | + PRINT_S1G_CAP((cap & BIT(5)), "+HTC-VHT Capable"); |
| 284 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "No support for Traveling Pilot"); |
| 285 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Supports 1 STS Traveling Pilot"); |
| 286 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Supports 1 and 2 STS Traveling Pilot"); |
| 287 | + |
| 288 | + cap = caps[3]; |
| 289 | + printf("\t\tByte[3]: 0x%02x\n", cap); |
| 290 | + PRINT_S1G_CAP((cap & BIT(0)), "RD Responder"); |
| 291 | + /* BIT(1) in Byte 3 or BIT(25) in all capabilities is reserved */ |
| 292 | + PRINT_S1G_CAP(((cap & BIT(2)) == 0x0), "Max MPDU length: 3895 bytes"); |
| 293 | + PRINT_S1G_CAP((cap & BIT(2)), "Max MPDU length: 7991 bytes"); |
| 294 | + |
| 295 | + if (compute_ampdu_length((cap >> 2) & 0x3)) { |
| 296 | + printf("\t\t\tMaximum AMPDU length: %d bytes (exponent: 0x0%02x)\n", |
| 297 | + compute_ampdu_length((cap >> 2) & 0x3), (cap >> 2) & 0x3); |
| 298 | + } else { |
| 299 | + printf("\t\t\tMaximum AMPDU length: unrecognized bytes (exponent: %d)\n", |
| 300 | + (cap >> 2) & 0x3); |
| 301 | + } |
| 302 | + |
| 303 | + printf("\t\t\tMinimum MPDU time spacing: %s (0x%02x)\n", |
| 304 | + print_ampdu_space((cap >> 5) & 0x7), (cap >> 5) & 0x7); |
| 305 | + |
| 306 | + cap = caps[4]; |
| 307 | + printf("\t\tByte[4]: 0x%02x\n", cap); |
| 308 | + PRINT_S1G_CAP((cap & BIT(0)), "Uplink sync capable"); |
| 309 | + PRINT_S1G_CAP((cap & BIT(1)), "Dynamic AID"); |
| 310 | + PRINT_S1G_CAP((cap & BIT(2)), "BAT"); |
| 311 | + PRINT_S1G_CAP((cap & BIT(3)), "TIM ADE"); |
| 312 | + PRINT_S1G_CAP((cap & BIT(4)), "Non-TIM"); |
| 313 | + PRINT_S1G_CAP((cap & BIT(5)), "Group AID"); |
| 314 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Sensor and non-sensor STAs"); |
| 315 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "Only sensor STAs"); |
| 316 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Only non-sensor STAs"); |
| 317 | + |
| 318 | + cap = caps[5]; |
| 319 | + printf("\t\tByte[5]: 0x%02x\n", cap); |
| 320 | + PRINT_S1G_CAP((cap & BIT(0)), "Centralized authentication control"); |
| 321 | + PRINT_S1G_CAP((cap & BIT(1)), "Distributed authentication control"); |
| 322 | + PRINT_S1G_CAP((cap & BIT(2)), "A-MSDU supported"); |
| 323 | + PRINT_S1G_CAP((cap & BIT(3)), "A-MPDU supported"); |
| 324 | + PRINT_S1G_CAP((cap & BIT(4)), "Asymmetric BA supported"); |
| 325 | + PRINT_S1G_CAP((cap & BIT(5)), "Flow control supported"); |
| 326 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Sectorization operation not supported"); |
| 327 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x1, "TXOP-based sectorization operation"); |
| 328 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "only group sectorization operation"); |
| 329 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, "Group and TXOP-based sectorization operations"); |
| 330 | + |
| 331 | + cap = caps[6]; |
| 332 | + if (cap) |
| 333 | + printf("\t\tByte[6]: 0x%02x\n", cap); |
| 334 | + |
| 335 | + PRINT_S1G_CAP((cap & BIT(0)), "OBSS mitigation"); |
| 336 | + PRINT_S1G_CAP((cap & BIT(1)), "Fragment BA"); |
| 337 | + PRINT_S1G_CAP((cap & BIT(2)), "NDP PS-Poll"); |
| 338 | + PRINT_S1G_CAP((cap & BIT(3)), "RAW operation"); |
| 339 | + PRINT_S1G_CAP((cap & BIT(4)), "Page slicing"); |
| 340 | + PRINT_S1G_CAP((cap & BIT(5)), "TXOP sharing smplicit Ack"); |
| 341 | + |
| 342 | + /* Only in case +HTC-VHT Capable is 0x1 */ |
| 343 | + if (caps[2] & BIT(5)) { |
| 344 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x0, "Not provide VHT MFB (No Feedback)"); |
| 345 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x2, "Provides only unsolicited VHT MFB"); |
| 346 | + PRINT_S1G_CAP(((cap >> 6) & 0x3) == 0x3, |
| 347 | + "Provides both feedback and unsolicited VHT MFB"); |
| 348 | + } |
| 349 | + |
| 350 | + cap = caps[7]; |
| 351 | + printf("\t\tByte[7]: 0x%02x\n", cap); |
| 352 | + PRINT_S1G_CAP((cap & BIT(0)), "TACK support as PS-Poll response"); |
| 353 | + PRINT_S1G_CAP((cap & BIT(1)), "Duplicate 1 MHz"); |
| 354 | + PRINT_S1G_CAP((cap & BIT(2)), "MCS negotiation"); |
| 355 | + PRINT_S1G_CAP((cap & BIT(3)), "1 MHz control response preamble"); |
| 356 | + PRINT_S1G_CAP((cap & BIT(4)), "NDP beamforming report poll"); |
| 357 | + PRINT_S1G_CAP((cap & BIT(5)), "Unsolicited dynamic AID"); |
| 358 | + PRINT_S1G_CAP((cap & BIT(6)), "Sector training operation"); |
| 359 | + PRINT_S1G_CAP((cap & BIT(7)), "Temporary PS mode switch"); |
| 360 | + |
| 361 | + cap = caps[8]; |
| 362 | + if (cap) |
| 363 | + printf("\t\tByte[8]: 0x%02x\n", cap); |
| 364 | + |
| 365 | + PRINT_S1G_CAP((cap & BIT(0)), "TWT grouping"); |
| 366 | + PRINT_S1G_CAP((cap & BIT(1)), "BDT capable"); |
| 367 | + printf("\t\t\tColor: %u\n", (cap >> 2) & 0x7); |
| 368 | + PRINT_S1G_CAP((cap & BIT(5)), "TWT requester"); |
| 369 | + PRINT_S1G_CAP((cap & BIT(6)), "TWT responder"); |
| 370 | + PRINT_S1G_CAP((cap & BIT(7)), "PV1 frame support"); |
| 371 | + |
| 372 | + cap = caps[9]; |
| 373 | + if (cap) |
| 374 | + printf("\t\tByte[9]: 0x%02x\n", cap); |
| 375 | + |
| 376 | + PRINT_S1G_CAP((cap & BIT(0)), "Link Adaptation without NDP CMAC PPDU capable"); |
| 377 | + /* Rest of byte 9 bits are reserved */ |
| 378 | + |
| 379 | + /* Supported S1G-MCS and NSS Set subfield */ |
| 380 | + /* Rx S1G-MCS Map */ |
| 381 | + cap = caps[10]; |
| 382 | + printf("\t\tMax Rx S1G MCS Map: 0x%02x\n", cap); |
| 383 | + printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support(cap & 0x3)); |
| 384 | + printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((cap >> 2) & 0x3)); |
| 385 | + printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((cap >> 4) & 0x3)); |
| 386 | + printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support((cap >> 6) & 0x3)); |
| 387 | + |
| 388 | + /* Rx Long GI data rate field comprises of 9 bits */ |
| 389 | + cap = caps[11]; |
| 390 | + if (cap || caps[12] & 0x1) |
| 391 | + printf("\t\t\tRx Highest Long GI Data Rate: %u Mbps\n", |
| 392 | + cap + ((caps[12] & 0x1) << 8)); |
| 393 | + |
| 394 | + /* Tx S1G-MCS Map */ |
| 395 | + cap = caps[12]; |
| 396 | + printf("\t\tMax Tx S1G MCS Map: 0x%02x\n", cap); |
| 397 | + printf("\t\t\tFor 1 SS: %s\n", s1g_ss_max_support((cap >> 1) & 0x3)); |
| 398 | + printf("\t\t\tFor 2 SS: %s\n", s1g_ss_max_support((cap >> 3) & 0x3)); |
| 399 | + printf("\t\t\tFor 3 SS: %s\n", s1g_ss_max_support((cap >> 5) & 0x3)); |
| 400 | + printf("\t\t\tFor 4 SS: %s\n", s1g_ss_max_support(((cap >> 7) & 0x1) + |
| 401 | + ((caps[13] << 1) & 0x2))); |
| 402 | + |
| 403 | + /* Tx Long GI data rate field comprises of 9 bits */ |
| 404 | + cap = caps[13]; |
| 405 | + if (((cap >> 7) & 0x7f) || (caps[14] & 0x3)) |
| 406 | + printf("\t\t\tTx Highest Long GI Data Rate: %u Mbps\n", ((cap >> 7) & 0x7f) + |
| 407 | + ((caps[14] & 0x3) << 7)); |
| 408 | + |
| 409 | + /* Rx and Tx single spatial streams and S1G MCS Map for 1 MHz */ |
| 410 | + cap = (caps[15] >> 2) & 0xf; |
| 411 | + PRINT_S1G_CAP((cap & 0x3) == 0x0, "Rx single SS for 1 MHz: as in Rx S1G MCS Map"); |
| 412 | + PRINT_S1G_CAP((cap & 0x3) == 0x1, "Rx single SS for 1 MHz: single SS and S1G-MCS 2"); |
| 413 | + PRINT_S1G_CAP((cap & 0x3) == 0x2, "Rx single SS for 1 MHz: single SS and S1G-MCS 7"); |
| 414 | + PRINT_S1G_CAP((cap & 0x3) == 0x3, "Rx single SS for 1 MHz: single SS and S1G-MCS 9"); |
| 415 | + cap = (cap >> 2) & 0x3; |
| 416 | + PRINT_S1G_CAP((cap & 0x3) == 0x0, "Tx single SS for 1 MHz: as in Tx S1G MCS Map"); |
| 417 | + PRINT_S1G_CAP((cap & 0x3) == 0x1, "Tx single SS for 1 MHz: single SS and S1G-MCS 2"); |
| 418 | + PRINT_S1G_CAP((cap & 0x3) == 0x2, "Tx single SS for 1 MHz: single SS and S1G-MCS 7"); |
| 419 | + PRINT_S1G_CAP((cap & 0x3) == 0x3, "Tx single SS for 1 MHz: single SS and S1G-MCS 9"); |
| 420 | + /* Last 2 bits are reserved */ |
| 421 | +#undef PRINT_S1G_CAP |
| 422 | +} |
| 423 | -- |
| 424 | 2.39.2 |
| 425 | |