| @@ -336,6 +336,13 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { · 335 unmodified lines |
| 336 | 336 | .len = NL80211_MAX_SUPP_HT_RATES }, |
| 337 | 337 | [NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)), |
| 338 | 338 | [NL80211_TXRATE_GI] = { .type = NLA_U8 }, |
| 339 | + [NL80211_TXRATE_HE] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_he)), |
| 340 | + [NL80211_TXRATE_HE_GI] = NLA_POLICY_RANGE(NLA_U8, |
| 341 | + NL80211_RATE_INFO_HE_GI_0_8, |
| 342 | + NL80211_RATE_INFO_HE_GI_3_2), |
| 343 | + [NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8, |
| 344 | + NL80211_RATE_INFO_HE_1XLTF, |
| 345 | + NL80211_RATE_INFO_HE_4XLTF), |
| 339 | 346 | }; |
| 340 | 347 | |
| 341 | 348 | static const struct nla_policy |
| @@ -4430,21 +4437,106 @@ static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, · 4088 unmodified lines |
| 4430 | 4437 | return true; |
| 4431 | 4438 | } |
| 4432 | 4439 | |
| 4440 | +static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map) |
| 4441 | +{ |
| 4442 | + switch (he_mcs_map) { |
| 4443 | + case IEEE80211_HE_MCS_NOT_SUPPORTED: |
| 4444 | + return 0; |
| 4445 | + case IEEE80211_HE_MCS_SUPPORT_0_7: |
| 4446 | + return 0x00FF; |
| 4447 | + case IEEE80211_HE_MCS_SUPPORT_0_9: |
| 4448 | + return 0x03FF; |
| 4449 | + case IEEE80211_HE_MCS_SUPPORT_0_11: |
| 4450 | + return 0xFFF; |
| 4451 | + default: |
| 4452 | + break; |
| 4453 | + } |
| 4454 | + return 0; |
| 4455 | +} |
| 4456 | + |
| 4457 | +static void he_build_mcs_mask(u16 he_mcs_map, |
| 4458 | + u16 he_mcs_mask[NL80211_HE_NSS_MAX]) |
| 4459 | +{ |
| 4460 | + u8 nss; |
| 4461 | + |
| 4462 | + for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) { |
| 4463 | + he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map & 0x03); |
| 4464 | + he_mcs_map >>= 2; |
| 4465 | + } |
| 4466 | +} |
| 4467 | + |
| 4468 | +static u16 he_get_txmcsmap(struct genl_info *info, |
| 4469 | + const struct ieee80211_sta_he_cap *he_cap) |
| 4470 | +{ |
| 4471 | + struct net_device *dev = info->user_ptr[1]; |
| 4472 | + struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 4473 | + __le16 tx_mcs; |
| 4474 | + |
| 4475 | + switch (wdev->chandef.width) { |
| 4476 | + case NL80211_CHAN_WIDTH_80P80: |
| 4477 | + tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80p80; |
| 4478 | + break; |
| 4479 | + case NL80211_CHAN_WIDTH_160: |
| 4480 | + tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_160; |
| 4481 | + break; |
| 4482 | + default: |
| 4483 | + tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80; |
| 4484 | + break; |
| 4485 | + } |
| 4486 | + return le16_to_cpu(tx_mcs); |
| 4487 | +} |
| 4488 | + |
| 4489 | +static bool he_set_mcs_mask(struct genl_info *info, |
| 4490 | + struct wireless_dev *wdev, |
| 4491 | + struct ieee80211_supported_band *sband, |
| 4492 | + struct nl80211_txrate_he *txrate, |
| 4493 | + u16 mcs[NL80211_HE_NSS_MAX]) |
| 4494 | +{ |
| 4495 | + const struct ieee80211_sta_he_cap *he_cap; |
| 4496 | + u16 tx_mcs_mask[NL80211_HE_NSS_MAX] = {}; |
| 4497 | + u16 tx_mcs_map = 0; |
| 4498 | + u8 i; |
| 4499 | + |
| 4500 | + he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype); |
| 4501 | + if (!he_cap) |
| 4502 | + return false; |
| 4503 | + |
| 4504 | + memset(mcs, 0, sizeof(u16) * NL80211_HE_NSS_MAX); |
| 4505 | + |
| 4506 | + tx_mcs_map = he_get_txmcsmap(info, he_cap); |
| 4507 | + |
| 4508 | + /* Build he_mcs_mask from HE capabilities */ |
| 4509 | + he_build_mcs_mask(tx_mcs_map, tx_mcs_mask); |
| 4510 | + |
| 4511 | + for (i = 0; i < NL80211_HE_NSS_MAX; i++) { |
| 4512 | + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) |
| 4513 | + mcs[i] = txrate->mcs[i]; |
| 4514 | + else |
| 4515 | + return false; |
| 4516 | + } |
| 4517 | + |
| 4518 | + return true; |
| 4519 | +} |
| 4520 | + |
| 4433 | 4521 | static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, |
| 4434 | 4522 | struct nlattr *attrs[], |
| 4435 | 4523 | enum nl80211_attrs attr, |
| 4436 | | − struct cfg80211_bitrate_mask *mask) |
| 4524 | + struct cfg80211_bitrate_mask *mask, |
| 4525 | + struct net_device *dev) |
| 4437 | 4526 | { |
| 4438 | 4527 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; |
| 4439 | 4528 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
| 4529 | + struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 4440 | 4530 | int rem, i; |
| 4441 | 4531 | struct nlattr *tx_rates; |
| 4442 | 4532 | struct ieee80211_supported_band *sband; |
| 4443 | | − u16 vht_tx_mcs_map; |
| 4533 | + u16 vht_tx_mcs_map, he_tx_mcs_map; |
| 4444 | 4534 | |
| 4445 | 4535 | memset(mask, 0, sizeof(*mask)); |
| 4446 | 4536 | /* Default to all rates enabled */ |
| 4447 | 4537 | for (i = 0; i < NUM_NL80211_BANDS; i++) { |
| 4538 | + const struct ieee80211_sta_he_cap *he_cap; |
| 4539 | + |
| 4448 | 4540 | sband = rdev->wiphy.bands[i]; |
| 4449 | 4541 | |
| 4450 | 4542 | if (!sband) |
| @@ -4460,6 +4552,16 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, · 9 unmodified lines |
| 4460 | 4552 | |
| 4461 | 4553 | vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); |
| 4462 | 4554 | vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); |
| 4555 | + |
| 4556 | + he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype); |
| 4557 | + if (!he_cap) |
| 4558 | + continue; |
| 4559 | + |
| 4560 | + he_tx_mcs_map = he_get_txmcsmap(info, he_cap); |
| 4561 | + he_build_mcs_mask(he_tx_mcs_map, mask->control[i].he_mcs); |
| 4562 | + |
| 4563 | + mask->control[i].he_gi = 0xFF; |
| 4564 | + mask->control[i].he_ltf = 0xFF; |
| 4463 | 4565 | } |
| 4464 | 4566 | |
| 4465 | 4567 | /* if no rates are given set it back to the defaults */ |
| @@ -4515,13 +4617,25 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, · 49 unmodified lines |
| 4515 | 4617 | if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI) |
| 4516 | 4618 | return -EINVAL; |
| 4517 | 4619 | } |
| 4620 | + if (tb[NL80211_TXRATE_HE] && |
| 4621 | + !he_set_mcs_mask(info, wdev, sband, |
| 4622 | + nla_data(tb[NL80211_TXRATE_HE]), |
| 4623 | + mask->control[band].he_mcs)) |
| 4624 | + return -EINVAL; |
| 4625 | + if (tb[NL80211_TXRATE_HE_GI]) |
| 4626 | + mask->control[band].he_gi = |
| 4627 | + nla_get_u8(tb[NL80211_TXRATE_HE_GI]); |
| 4628 | + if (tb[NL80211_TXRATE_HE_LTF]) |
| 4629 | + mask->control[band].he_ltf = |
| 4630 | + nla_get_u8(tb[NL80211_TXRATE_HE_LTF]); |
| 4518 | 4631 | |
| 4519 | 4632 | if (mask->control[band].legacy == 0) { |
| 4520 | | − /* don't allow empty legacy rates if HT or VHT |
| 4633 | + /* don't allow empty legacy rates if HT, VHT or HE |
| 4521 | 4634 | * are not even supported. |
| 4522 | 4635 | */ |
| 4523 | 4636 | if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || |
| 4524 | | − rdev->wiphy.bands[band]->vht_cap.vht_supported)) |
| 4637 | + rdev->wiphy.bands[band]->vht_cap.vht_supported || |
| 4638 | + ieee80211_get_he_iftype_cap(sband, wdev->iftype))) |
| 4525 | 4639 | return -EINVAL; |
| 4526 | 4640 | |
| 4527 | 4641 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) |
| @@ -4532,6 +4646,10 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, · 4 unmodified lines |
| 4532 | 4646 | if (mask->control[band].vht_mcs[i]) |
| 4533 | 4647 | goto out; |
| 4534 | 4648 | |
| 4649 | + for (i = 0; i < NL80211_HE_NSS_MAX; i++) |
| 4650 | + if (mask->control[band].he_mcs[i]) |
| 4651 | + goto out; |
| 4652 | + |
| 4535 | 4653 | /* legacy and mcs rates may not be both empty */ |
| 4536 | 4654 | return -EINVAL; |
| 4537 | 4655 | } |
| @@ -4976,7 +5094,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) · 438 unmodified lines |
| 4976 | 5094 | if (info->attrs[NL80211_ATTR_TX_RATES]) { |
| 4977 | 5095 | err = nl80211_parse_tx_bitrate_mask(info, info->attrs, |
| 4978 | 5096 | NL80211_ATTR_TX_RATES, |
| 4979 | | − ¶ms.beacon_rate); |
| 5097 | + ¶ms.beacon_rate, |
| 5098 | + dev); |
| 4980 | 5099 | if (err) |
| 4981 | 5100 | return err; |
| 4982 | 5101 | |
| @@ -10780,7 +10899,8 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, · 5797 unmodified lines |
| 10780 | 10899 | return -EOPNOTSUPP; |
| 10781 | 10900 | |
| 10782 | 10901 | err = nl80211_parse_tx_bitrate_mask(info, info->attrs, |
| 10783 | | − NL80211_ATTR_TX_RATES, &mask); |
| 10902 | + NL80211_ATTR_TX_RATES, &mask, |
| 10903 | + dev); |
| 10784 | 10904 | if (err) |
| 10785 | 10905 | return err; |
| 10786 | 10906 | |
| @@ -11388,7 +11508,8 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) · 601 unmodified lines |
| 11388 | 11508 | if (info->attrs[NL80211_ATTR_TX_RATES]) { |
| 11389 | 11509 | err = nl80211_parse_tx_bitrate_mask(info, info->attrs, |
| 11390 | 11510 | NL80211_ATTR_TX_RATES, |
| 11391 | | − &setup.beacon_rate); |
| 11511 | + &setup.beacon_rate, |
| 11512 | + dev); |
| 11392 | 11513 | if (err) |
| 11393 | 11514 | return err; |
| 11394 | 11515 | |
| @@ -14168,7 +14289,7 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, · 2773 unmodified lines |
| 14168 | 14289 | if (tid_conf->txrate_type != NL80211_TX_RATE_AUTOMATIC) { |
| 14169 | 14290 | attr = NL80211_TID_CONFIG_ATTR_TX_RATE; |
| 14170 | 14291 | err = nl80211_parse_tx_bitrate_mask(info, attrs, attr, |
| 14171 | | − &tid_conf->txrate_mask); |
| 14292 | + &tid_conf->txrate_mask, dev); |
| 14172 | 14293 | if (err) |
| 14173 | 14294 | return err; |
| 14174 | 14295 | |