Received: by 2002:a25:1985:0:0:0:0:0 with SMTP id 127csp704044ybz; Wed, 29 Apr 2020 07:58:29 -0700 (PDT) X-Google-Smtp-Source: APiQypI0Pc35w1K1laDkMXd6iQ3PlvJMSd5D+XyJTDUviUWi3EYYYvUP4WPtIgbMfaYgtDDB+tV+ X-Received: by 2002:a17:907:7246:: with SMTP id ds6mr2747272ejc.203.1588172309815; Wed, 29 Apr 2020 07:58:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1588172309; cv=none; d=google.com; s=arc-20160816; b=Q7Hs0JjfntzvI1Dc0JHvxFTLxTMBqQmAz7It3TwIvMA2onPQSK2SaLKIp+6YZgGbB9 n3UH3VHPwtFLV/UGPir3wbzhpZQ0/6BSJ3tMcqsEtop5hKfhk6FDdLDRgsC+cbv4arRv h2qNkSmB8tBgQQsxWSSnqgeRWUQvq2VXE0DQ52hkUrettpNmqb/XDFduFEsq1NC4rVSA EqVajv9V5xp7AqBBmWwf3sAPE/r5BN8mbwgGqNf46GU0fOh4HZhZ/77MkKrYakXVY/NN 8givfDUpTlLlWeBzzcRNa3VSVnycLoAfAQztP1iDVXgFVe5pvZu+wmOTCdAYYFH0GhKy Tbhg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=8dMrF++CmoHGErBS1kgJWOCAGvhyG8AcFWCxh3mXLes=; b=J/O1d1mcvKy8QsMww0iRMgLCuNiYyJ2ERwR35snxazfBq569Sr5kXzQ1dRuvBHGcCp /5Ia2E6xtMPRcVTWUmucahY064fYKfU7sKiVhTtV0+Xm/1olMrcTv4ECzuXDOLV+yipJ TAAyp/ccweCR8K7t4KEsSamWZlgHwTJMfMRLiNXb7wWHbw3nUvQAqDkzMKw5c6cOuA+9 eZREiZbCIA51qL0D8sT/eZ+/dyzAcz0waQs7lGFQRKMF8HUm0kYgS24hEbP4RrHtzzqY InDo18VSU6Lb91LQKn50d4jo00nKClWght3+2iFb3sLLf8WlSDDE/gzcmiiQGrh9I+vM wAGA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-wireless-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-wireless-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id cn25si3531701edb.589.2020.04.29.07.58.02; Wed, 29 Apr 2020 07:58:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-wireless-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-wireless-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-wireless-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726891AbgD2O51 (ORCPT + 99 others); Wed, 29 Apr 2020 10:57:27 -0400 Received: from nbd.name ([46.4.11.11]:60148 "EHLO nbd.name" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726556AbgD2O51 (ORCPT ); Wed, 29 Apr 2020 10:57:27 -0400 Received: from [81.25.161.111] (helo=bertha8.datto.lan) by ds12 with esmtpa (Exim 4.89) (envelope-from ) id 1jTo9K-0002aB-AZ; Wed, 29 Apr 2020 16:57:22 +0200 From: John Crispin To: Johannes Berg , Kalle Valo Cc: linux-wireless@vger.kernel.org, ath11k@lists.infradead.org, Miles Hu , John Crispin Subject: [PATCH V3 1/3] nl80211: add support for setting fixed HE rate/gi/ltf Date: Wed, 29 Apr 2020 16:57:06 +0200 Message-Id: <20200429145708.25992-1-john@phrozen.org> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: Miles Hu This patch adds the nl80211 structs, definitions, policies and parsing code required to pass fixed HE rate, gi and ltf settings. Signed-off-by: Miles Hu Signed-off-by: John Crispin --- Changes in V3 * minor cleanup based on Johannes's feedback * properly make use the iftype_data array Changes in V2 * add more policies * reoder enums * remove incorrect he_cap from ieee80211_supported_band * remove _WARN from policy include/net/cfg80211.h | 25 ++++++++ include/uapi/linux/nl80211.h | 28 +++++++++ net/wireless/nl80211.c | 119 ++++++++++++++++++++++++++++++++++- 3 files changed, 170 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c78bd4ff9e33..deaa3668d47a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -495,6 +495,28 @@ ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *sband, return NULL; } +/** + * ieee80211_get_he_cap - return the first he_cap that we find for a sband + * @sband: the sband that we want to check for HE support + * + * Return: return a valid he_cap or NULL + */ +static inline const struct ieee80211_sta_he_cap * +ieee80211_get_he_cap(const struct ieee80211_supported_band *sband) +{ + int i; + + for (i = 0; i < sband->n_iftype_data; i++) { + const struct ieee80211_sband_iftype_data *data = + &sband->iftype_data[i]; + + if (data->he_cap.has_he) + return &data->he_cap; + } + + return NULL; +} + /** * ieee80211_get_he_sta_cap - return HE capabilities for an sband's STA * @sband: the sband to search for the STA on @@ -1006,7 +1028,10 @@ struct cfg80211_bitrate_mask { u32 legacy; u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; u16 vht_mcs[NL80211_VHT_NSS_MAX]; + u16 he_mcs[NL80211_HE_NSS_MAX]; enum nl80211_txrate_gi gi; + enum nl80211_he_gi he_gi; + enum nl80211_he_ltf he_ltf; } control[NUM_NL80211_BANDS]; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 2b691161830f..966887bd654c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3134,6 +3134,18 @@ enum nl80211_he_gi { NL80211_RATE_INFO_HE_GI_3_2, }; +/** + * enum nl80211_he_ltf - HE long training field + * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec + * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec + * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec + */ +enum nl80211_he_ltf { + NL80211_RATE_INFO_HE_1XLTF, + NL80211_RATE_INFO_HE_2XLTF, + NL80211_RATE_INFO_HE_4XLTF, +}; + /** * enum nl80211_he_ru_alloc - HE RU allocation values * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation @@ -4653,6 +4665,10 @@ enum nl80211_key_attributes { * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, * see &struct nl80211_txrate_vht * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi + * @NL80211_TXRATE_HE: HE rates allowed for TX rate selection, + * see &struct nl80211_txrate_he + * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us. + * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -4662,6 +4678,9 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_HT, NL80211_TXRATE_VHT, NL80211_TXRATE_GI, + NL80211_TXRATE_HE, + NL80211_TXRATE_HE_GI, + NL80211_TXRATE_HE_LTF, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -4679,6 +4698,15 @@ struct nl80211_txrate_vht { __u16 mcs[NL80211_VHT_NSS_MAX]; }; +#define NL80211_HE_NSS_MAX 8 +/** + * struct nl80211_txrate_he - HE MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_he { + __u16 mcs[NL80211_HE_NSS_MAX]; +}; + enum nl80211_txrate_gi { NL80211_TXRATE_DEFAULT_GI, NL80211_TXRATE_FORCE_SGI, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5fa402144cda..4bc652b904c1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4403,6 +4403,83 @@ static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, return true; } +static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map) +{ + switch (he_mcs_map) { + case IEEE80211_HE_MCS_NOT_SUPPORTED: + return 0; + case IEEE80211_HE_MCS_SUPPORT_0_7: + return 0x00FF; + case IEEE80211_HE_MCS_SUPPORT_0_9: + return 0x03FF; + case IEEE80211_HE_MCS_SUPPORT_0_11: + return 0xFFF; + default: + break; + } + return 0; +} + +static void he_build_mcs_mask(u16 he_mcs_map, + u16 he_mcs_mask[NL80211_HE_NSS_MAX]) +{ + u8 nss; + + for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) { + he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map & 0x03); + he_mcs_map >>= 2; + } +} + +static u16 he_get_txmcsmap(struct genl_info *info, + const struct ieee80211_sta_he_cap *he_cap) +{ + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + switch (wdev->chandef.width) { + case NL80211_CHAN_WIDTH_80P80: + return he_cap->he_mcs_nss_supp.tx_mcs_80p80; + case NL80211_CHAN_WIDTH_160: + return he_cap->he_mcs_nss_supp.tx_mcs_160; + default: + break; + } + return le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); +} + +static bool he_set_mcs_mask(struct genl_info *info, + struct ieee80211_supported_band *sband, + struct nl80211_txrate_he *txrate, + u16 mcs[NL80211_HE_NSS_MAX]) +{ + const struct ieee80211_sta_he_cap *he_cap; + u16 tx_mcs_mask[NL80211_HE_NSS_MAX] = {}; + u16 tx_mcs_map = 0; + u8 i; + + he_cap = ieee80211_get_he_cap(sband); + + if (!he_cap) + return false; + + memset(mcs, 0, sizeof(u16) * NL80211_HE_NSS_MAX); + + tx_mcs_map = he_get_txmcsmap(info, he_cap); + + /* Build he_mcs_mask from HE capabilities */ + he_build_mcs_mask(tx_mcs_map, tx_mcs_mask); + + for (i = 0; i < NL80211_HE_NSS_MAX; i++) { + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) + mcs[i] = txrate->mcs[i]; + else + return false; + } + + return true; +} + static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, @@ -4413,6 +4490,16 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { .len = sizeof(struct nl80211_txrate_vht), }, [NL80211_TXRATE_GI] = { .type = NLA_U8 }, + [NL80211_TXRATE_HE] = { + .type = NLA_EXACT_LEN, + .len = sizeof(struct nl80211_txrate_he), + }, + [NL80211_TXRATE_HE_GI] = NLA_POLICY_RANGE(NLA_U8, + NL80211_RATE_INFO_HE_GI_0_8, + NL80211_RATE_INFO_HE_GI_3_2), + [NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8, + NL80211_RATE_INFO_HE_1XLTF, + NL80211_RATE_INFO_HE_4XLTF), }; static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, @@ -4423,11 +4510,13 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, int rem, i; struct nlattr *tx_rates; struct ieee80211_supported_band *sband; - u16 vht_tx_mcs_map; + u16 vht_tx_mcs_map, he_tx_mcs_map; memset(mask, 0, sizeof(*mask)); /* Default to all rates enabled */ for (i = 0; i < NUM_NL80211_BANDS; i++) { + const struct ieee80211_sta_he_cap *he_cap; + sband = rdev->wiphy.bands[i]; if (!sband) @@ -4443,6 +4532,16 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); + + he_cap = ieee80211_get_he_cap(sband); + if (!he_cap) + continue; + + he_tx_mcs_map = he_get_txmcsmap(info, he_cap); + he_build_mcs_mask(he_tx_mcs_map, mask->control[i].he_mcs); + + mask->control[i].he_gi = 0xFF; + mask->control[i].he_ltf = 0xFF; } /* if no rates are given set it back to the defaults */ @@ -4498,13 +4597,25 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI) return -EINVAL; } + if (tb[NL80211_TXRATE_HE] && + !he_set_mcs_mask(info, sband, + nla_data(tb[NL80211_TXRATE_HE]), + mask->control[band].he_mcs)) + return -EINVAL; + if (tb[NL80211_TXRATE_HE_GI]) + mask->control[band].he_gi = + nla_get_u8(tb[NL80211_TXRATE_HE_GI]); + if (tb[NL80211_TXRATE_HE_LTF]) + mask->control[band].he_ltf = + nla_get_u8(tb[NL80211_TXRATE_HE_LTF]); if (mask->control[band].legacy == 0) { /* don't allow empty legacy rates if HT or VHT * are not even supported. */ if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || - rdev->wiphy.bands[band]->vht_cap.vht_supported)) + rdev->wiphy.bands[band]->vht_cap.vht_supported || + ieee80211_get_he_cap(rdev->wiphy.bands[band]))) return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) @@ -4515,6 +4626,10 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, if (mask->control[band].vht_mcs[i]) goto out; + for (i = 0; i < NL80211_HE_NSS_MAX; i++) + if (mask->control[band].he_mcs[i]) + goto out; + /* legacy and mcs rates may not be both empty */ return -EINVAL; } -- 2.20.1