| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * VHT handling |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Portions of this file |
|---|
| 5 | 6 | * Copyright(c) 2015 - 2016 Intel Deutschland GmbH |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 7 | + * Copyright (C) 2018 - 2020 Intel Corporation |
|---|
| 10 | 8 | */ |
|---|
| 11 | 9 | |
|---|
| 12 | 10 | #include <linux/ieee80211.h> |
|---|
| .. | .. |
|---|
| 231 | 229 | memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, |
|---|
| 232 | 230 | sizeof(struct ieee80211_vht_mcs_info)); |
|---|
| 233 | 231 | |
|---|
| 232 | + /* copy EXT_NSS_BW Support value or remove the capability */ |
|---|
| 233 | + if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW)) |
|---|
| 234 | + vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); |
|---|
| 235 | + else |
|---|
| 236 | + vht_cap->vht_mcs.tx_highest &= |
|---|
| 237 | + ~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); |
|---|
| 238 | + |
|---|
| 234 | 239 | /* but also restrict MCSes */ |
|---|
| 235 | 240 | for (i = 0; i < 8; i++) { |
|---|
| 236 | 241 | u16 own_rx, own_tx, peer_rx, peer_tx; |
|---|
| .. | .. |
|---|
| 294 | 299 | break; |
|---|
| 295 | 300 | default: |
|---|
| 296 | 301 | sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; |
|---|
| 302 | + |
|---|
| 303 | + if (!(vht_cap->vht_mcs.tx_highest & |
|---|
| 304 | + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) |
|---|
| 305 | + break; |
|---|
| 306 | + |
|---|
| 307 | + /* |
|---|
| 308 | + * If this is non-zero, then it does support 160 MHz after all, |
|---|
| 309 | + * in one form or the other. We don't distinguish here (or even |
|---|
| 310 | + * above) between 160 and 80+80 yet. |
|---|
| 311 | + */ |
|---|
| 312 | + if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) |
|---|
| 313 | + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
|---|
| 297 | 314 | } |
|---|
| 298 | 315 | |
|---|
| 299 | 316 | sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); |
|---|
| 300 | | - |
|---|
| 301 | | - /* If HT IE reported 3839 bytes only, stay with that size. */ |
|---|
| 302 | | - if (sta->sta.max_amsdu_len == IEEE80211_MAX_MPDU_LEN_HT_3839) |
|---|
| 303 | | - return; |
|---|
| 304 | 317 | |
|---|
| 305 | 318 | switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { |
|---|
| 306 | 319 | case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: |
|---|
| .. | .. |
|---|
| 316 | 329 | } |
|---|
| 317 | 330 | } |
|---|
| 318 | 331 | |
|---|
| 332 | +/* FIXME: move this to some better location - parses HE now */ |
|---|
| 319 | 333 | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct sta_info *sta) |
|---|
| 320 | 334 | { |
|---|
| 321 | 335 | struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; |
|---|
| 336 | + struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; |
|---|
| 322 | 337 | u32 cap_width; |
|---|
| 338 | + |
|---|
| 339 | + if (he_cap->has_he) { |
|---|
| 340 | + u8 info = he_cap->he_cap_elem.phy_cap_info[0]; |
|---|
| 341 | + |
|---|
| 342 | + if (sta->sdata->vif.bss_conf.chandef.chan->band == |
|---|
| 343 | + NL80211_BAND_2GHZ) { |
|---|
| 344 | + if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) |
|---|
| 345 | + return IEEE80211_STA_RX_BW_40; |
|---|
| 346 | + else |
|---|
| 347 | + return IEEE80211_STA_RX_BW_20; |
|---|
| 348 | + } |
|---|
| 349 | + |
|---|
| 350 | + if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || |
|---|
| 351 | + info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) |
|---|
| 352 | + return IEEE80211_STA_RX_BW_160; |
|---|
| 353 | + else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) |
|---|
| 354 | + return IEEE80211_STA_RX_BW_80; |
|---|
| 355 | + |
|---|
| 356 | + return IEEE80211_STA_RX_BW_20; |
|---|
| 357 | + } |
|---|
| 323 | 358 | |
|---|
| 324 | 359 | if (!vht_cap->vht_supported) |
|---|
| 325 | 360 | return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? |
|---|
| .. | .. |
|---|
| 330 | 365 | |
|---|
| 331 | 366 | if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || |
|---|
| 332 | 367 | cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) |
|---|
| 368 | + return IEEE80211_STA_RX_BW_160; |
|---|
| 369 | + |
|---|
| 370 | + /* |
|---|
| 371 | + * If this is non-zero, then it does support 160 MHz after all, |
|---|
| 372 | + * in one form or the other. We don't distinguish here (or even |
|---|
| 373 | + * above) between 160 and 80+80 yet. |
|---|
| 374 | + */ |
|---|
| 375 | + if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) |
|---|
| 333 | 376 | return IEEE80211_STA_RX_BW_160; |
|---|
| 334 | 377 | |
|---|
| 335 | 378 | return IEEE80211_STA_RX_BW_80; |
|---|
| .. | .. |
|---|
| 408 | 451 | } |
|---|
| 409 | 452 | } |
|---|
| 410 | 453 | |
|---|
| 454 | +/* FIXME: rename/move - this deals with everything not just VHT */ |
|---|
| 411 | 455 | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) |
|---|
| 412 | 456 | { |
|---|
| 413 | 457 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
|---|
| .. | .. |
|---|
| 439 | 483 | |
|---|
| 440 | 484 | void ieee80211_sta_set_rx_nss(struct sta_info *sta) |
|---|
| 441 | 485 | { |
|---|
| 442 | | - u8 ht_rx_nss = 0, vht_rx_nss = 0; |
|---|
| 486 | + u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, rx_nss; |
|---|
| 443 | 487 | |
|---|
| 444 | 488 | /* if we received a notification already don't overwrite it */ |
|---|
| 445 | 489 | if (sta->sta.rx_nss) |
|---|
| 446 | 490 | return; |
|---|
| 491 | + |
|---|
| 492 | + if (sta->sta.he_cap.has_he) { |
|---|
| 493 | + int i; |
|---|
| 494 | + u8 rx_mcs_80 = 0, rx_mcs_160 = 0; |
|---|
| 495 | + const struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap; |
|---|
| 496 | + u16 mcs_160_map = |
|---|
| 497 | + le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); |
|---|
| 498 | + u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); |
|---|
| 499 | + |
|---|
| 500 | + for (i = 7; i >= 0; i--) { |
|---|
| 501 | + u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; |
|---|
| 502 | + |
|---|
| 503 | + if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
|---|
| 504 | + rx_mcs_160 = i + 1; |
|---|
| 505 | + break; |
|---|
| 506 | + } |
|---|
| 507 | + } |
|---|
| 508 | + for (i = 7; i >= 0; i--) { |
|---|
| 509 | + u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; |
|---|
| 510 | + |
|---|
| 511 | + if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
|---|
| 512 | + rx_mcs_80 = i + 1; |
|---|
| 513 | + break; |
|---|
| 514 | + } |
|---|
| 515 | + } |
|---|
| 516 | + |
|---|
| 517 | + he_rx_nss = min(rx_mcs_80, rx_mcs_160); |
|---|
| 518 | + } |
|---|
| 447 | 519 | |
|---|
| 448 | 520 | if (sta->sta.ht_cap.ht_supported) { |
|---|
| 449 | 521 | if (sta->sta.ht_cap.mcs.rx_mask[0]) |
|---|
| .. | .. |
|---|
| 474 | 546 | /* FIXME: consider rx_highest? */ |
|---|
| 475 | 547 | } |
|---|
| 476 | 548 | |
|---|
| 477 | | - ht_rx_nss = max(ht_rx_nss, vht_rx_nss); |
|---|
| 478 | | - sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); |
|---|
| 549 | + rx_nss = max(vht_rx_nss, ht_rx_nss); |
|---|
| 550 | + rx_nss = max(he_rx_nss, rx_nss); |
|---|
| 551 | + sta->sta.rx_nss = max_t(u8, 1, rx_nss); |
|---|
| 479 | 552 | } |
|---|
| 480 | 553 | |
|---|
| 481 | 554 | u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, |
|---|
| .. | .. |
|---|
| 504 | 577 | |
|---|
| 505 | 578 | switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { |
|---|
| 506 | 579 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: |
|---|
| 580 | + /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ |
|---|
| 507 | 581 | sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; |
|---|
| 508 | 582 | break; |
|---|
| 509 | 583 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: |
|---|
| 584 | + /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ |
|---|
| 510 | 585 | sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; |
|---|
| 511 | 586 | break; |
|---|
| 512 | 587 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: |
|---|
| 513 | | - sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; |
|---|
| 588 | + if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) |
|---|
| 589 | + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
|---|
| 590 | + else |
|---|
| 591 | + sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; |
|---|
| 514 | 592 | break; |
|---|
| 515 | 593 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: |
|---|
| 594 | + /* legacy only, no longer used by newer spec */ |
|---|
| 516 | 595 | sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
|---|
| 517 | 596 | break; |
|---|
| 518 | 597 | } |
|---|