wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/332-0002-ath11k-Add-tid-qos-configuration-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

1388 lines
45 KiB
Diff

From 56e7d6333f5a4b485960a4a58b8458bc00509f1e Mon Sep 17 00:00:00 2001
From: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>
Date: Sun, 10 Oct 2021 23:22:10 +0530
Subject: [PATCH 2/2] ath11k: Add tid qos configuration support for ath11k
Add provision to configure tid based noack, retry, aggregation,
rtscts and bitrate for single peer and for all peer in a vap.
iw command is used to set these parameters per peer/per vap,
this is passed to the driver from where it is configured in fw
through wmi. Driver disables rate control, aggregation and retry
when noack is set.
Only single bitrate can be set for a peer.Bitrate should be set only
according to peer capabilit, HE peer cannot be configured with vht,
ht or legacy rate.
In addition added support to enable/disable rtscts for unicast
management frames
commands:
iw dev <devname> set tidconf [peer <MAC address>] tids <mask> [override]
[lretry <num>] [ampdu [on <num>|off]] [amsdu [on <num>|off]]
[noack [on|off]] [rtscts [on|off]][bitrates <type [auto|fixed|limit]>
<legacy rate in Mbps>*] [ht-mcs-<2.4|5> <MCS index>*]
[vht-mcs-<2.4|5> <NSS:MCSx] [he-mcs-<2.4|5> <NSS:MCSx]
Signed-off-by: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>
---
drivers/net/wireless/ath/ath11k/core.h | 21 +
drivers/net/wireless/ath/ath11k/dp_tx.c | 9 +-
drivers/net/wireless/ath/ath11k/hw.c | 5 +-
drivers/net/wireless/ath/ath11k/mac.c | 930 +++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath11k/wmi.c | 49 ++
drivers/net/wireless/ath/ath11k/wmi.h | 78 +++
6 files changed, 1087 insertions(+), 5 deletions(-)
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -83,6 +83,7 @@ enum wme_ac {
#define ATH11K_HT_MCS_MAX 7
#define ATH11K_VHT_MCS_MAX 9
#define ATH11K_HE_MCS_MAX 11
+#define ATH11K_TID_MAX 8
enum ath11k_crypt_mode {
/* Only use hardware crypto engine */
@@ -95,6 +96,7 @@ enum ath11k_crypt_mode {
#define ATH11K_FREE_GROUP_IDX_MAP_BITS 32
#define ATH11K_FREE_GROUP_IDX_MAP_MAX (ATH11K_GROUP_KEYS_NUM_MAX / \
ATH11K_FREE_GROUP_IDX_MAP_BITS)
+#define ATH11K_MAX_RETRY_COUNT 30
static inline enum wme_ac ath11k_tid_to_ac(u32 tid)
{
@@ -108,5 +110,6 @@ enum ath11k_skb_flags {
ATH11K_SKB_CIPHER_SET = BIT(1),
ATH11K_SKB_TX_STATUS = BIT(2),
+ ATH11K_SKB_F_NOACK_TID = BIT(3),
};
struct ath11k_skb_cb {
@@ -246,6 +249,18 @@ struct ath11k_mac_filter {
u8 peer_mac[ETH_ALEN];
};
+struct ath11k_tid_qos_config {
+ int noack;
+ int retry_long;
+ int aggr_ctrl;
+ int amsdu_count;
+ int ampdu_count;
+ u8 rate_ctrl;
+ u32 rate_code;
+ int rtscts;
+ int ext_tid_cfg_bitmap;
+};
+
struct ath11k_vif {
u32 vdev_id;
enum wmi_vdev_type vdev_type;
@@ -308,6 +323,9 @@ struct ath11k_vif {
struct list_head ap_vlan_arvifs;
/* VLAN keyidx map required for Dynamic VLAN */
u16 *vlan_keyid_map;
+ u32 tid_conf_changed[ATH11K_TID_MAX];
+ struct ath11k_tid_qos_config tid_cfg[ATH11K_TID_MAX];
+ u32 tids_rst;
DECLARE_BITMAP(free_groupidx_map, ATH11K_GROUP_KEYS_NUM_MAX);
};
@@ -511,6 +529,8 @@ struct ath11k_sta {
/*bytes count for bit error rate computation*/
u32 ber_succ_bytes;
u32 ber_fail_bytes;
+ struct work_struct tid_config_wk;
+ struct ath11k_tid_qos_config tid_cfg[ATH11K_TID_MAX];
#ifdef CPTCFG_ATH11K_CFR
struct ath11k_per_peer_cfr_capture cfr_capture;
#endif
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -456,7 +456,8 @@ ath11k_dp_tx_htt_tx_complete_buf(struct
memset(&info->status, 0, sizeof(info->status));
if (ts->acked) {
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ !(flags & ATH11K_SKB_F_NOACK_TID)) {
info->flags |= IEEE80211_TX_STAT_ACK;
info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR +
ts->ack_rssi;
@@ -591,7 +592,8 @@ static void ath11k_dp_tx_complete_msdu(s
info->status.rates[0].idx = -1;
if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED &&
- !(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ !(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ !(flags & ATH11K_SKB_F_NOACK_TID)) {
info->flags |= IEEE80211_TX_STAT_ACK;
info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR +
ts->ack_rssi;
@@ -599,7 +601,8 @@ static void ath11k_dp_tx_complete_msdu(s
}
if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX &&
- (info->flags & IEEE80211_TX_CTL_NO_ACK))
+ (info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (flags & ATH11K_SKB_F_NOACK_TID))
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) {
--- a/drivers/net/wireless/ath/ath11k/hw.c
+++ b/drivers/net/wireless/ath/ath11k/hw.c
@@ -157,6 +157,10 @@ static void ath11k_init_wmi_config_ipq80
config->num_wow_filters = 0x16;
config->num_keep_alive_pattern = 0;
config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64;
+ if (test_bit(WMI_TLV_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
+ ab->wmi_ab.svc_map)) {
+ config->peer_tid_ext |= WMI_RSRC_CFG_FLAG_PEER_TID_EXT;
+ }
}
static int ath11k_hw_mac_id_to_pdev_id_ipq8074(struct ath11k_hw_params *hw,
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -4310,6 +4310,180 @@ exit:
return ret;
}
+static int ath11k_new_peer_tid_config(struct ath11k *ar,
+ struct ieee80211_sta *sta,
+ struct ath11k_vif *arvif)
+{
+ struct wmi_per_peer_per_tid_cfg_arg arg = {};
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+ bool config_apply;
+ int ret, i;
+
+ for (i = 0; i < ATH11K_TID_MAX; i++) {
+ config_apply = false;
+ if (arvif->tid_cfg[i].retry_long || arvif->tid_cfg[i].aggr_ctrl ||
+ arvif->tid_cfg[i].rate_ctrl || arvif->tid_cfg[i].rate_code ||
+ arvif->tid_cfg[i].rtscts) {
+ config_apply = true;
+ arg.tid = i;
+ arg.vdev_id = arvif->vdev_id;
+ arg.retry_count = arvif->tid_cfg[i].retry_long;
+ arg.aggr_control = arvif->tid_cfg[i].aggr_ctrl;
+ arg.rate_ctrl = arvif->tid_cfg[i].rate_ctrl;
+ arg.rcode_flags = arvif->tid_cfg[i].rate_code;
+ if (arvif->tid_cfg[i].rtscts)
+ arg.ext_tid_cfg_bitmap |=
+ WMI_EXT_TID_RTS_CTS_CONFIG;
+ arg.rtscts_ctrl = arvif->tid_cfg[i].rtscts;
+ if (arvif->tid_cfg[i].aggr_ctrl) {
+ if (arvif->tid_cfg[i].ampdu_count) {
+ arg.ext_tid_cfg_bitmap |=
+ WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ arg.max_num_mpdu_in_ppdu = arvif->tid_cfg[i].ampdu_count;
+ }
+ if (arvif->tid_cfg[i].amsdu_count) {
+ arg.ext_tid_cfg_bitmap |=
+ WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ arg.max_num_msdu_in_mpdu = arvif->tid_cfg[i].amsdu_count;
+ }
+ }
+ }
+
+ if (arvif->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg.ack_policy = arvif->tid_cfg[i].noack;
+ arg.rate_ctrl =
+ WMI_TID_CONFIG_RATE_CONTROL_DEFAULT_LOWEST_RATE;
+ arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ config_apply = true;
+ }
+
+ /* Assign default value(-1) to newly connected station.
+ * This is to identify station specific tid configuration not
+ * configured for the station.
+ */
+ arsta->tid_cfg[i].retry_long = -1;
+ arsta->tid_cfg[i].noack = -1;
+ arsta->tid_cfg[i].aggr_ctrl = -1;
+
+ if (!config_apply)
+ continue;
+
+ ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+ ret = ath11k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set per tid retry/aggr config for sta %pM: %d\n",
+ sta->addr, ret);
+ return ret;
+ }
+ memset(&arg, 0, sizeof(arg));
+ }
+ return 0;
+}
+
+static int ath11k_mac_validate_rate_mask(struct ath11k *ar,
+ struct ieee80211_sta *sta,
+ u32 rate_ctrl_flag, u8 nss)
+{
+ if (nss > sta->deflink.rx_nss) {
+ ath11k_warn(ar->ab, "Invalid nss field, configured %u limit %u\n",
+ nss, sta->deflink.rx_nss);
+ return -EINVAL;
+ }
+
+ if (ATH11K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HE) {
+ if (!sta->deflink.he_cap.has_he) {
+ ath11k_warn(ar->ab, "Invalid HE rate for sta %pM\n",
+ sta->addr);
+ return -EINVAL;
+ }
+ } else if (ATH11K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_VHT) {
+ if (!sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he) {
+ ath11k_warn(ar->ab, "Invalid VHT rate for sta %pM\n",
+ sta->addr);
+ return -EINVAL;
+ }
+ } else if (ATH11K_HW_PREAMBLE(rate_ctrl_flag) == WMI_RATE_PREAMBLE_HT) {
+ if (!sta->deflink.ht_cap.ht_supported || sta->deflink.vht_cap.vht_supported) {
+ ath11k_warn(ar->ab, "Invalid HT rate for sta %pM\n",
+ sta->addr);
+ return -EINVAL;
+ }
+ } else {
+ if (sta->deflink.ht_cap.ht_supported ||
+ sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he) {
+ ath11k_warn(ar->ab, "Invalid rate cap\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ath11k_mac_get_single_rate(struct ath11k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask,
+ u32 *rate, u8 *nss)
+{
+ int rate_idx;
+ int i;
+ u16 bitrate;
+ u8 preamble;
+ u8 hw_rate;
+
+ if (hweight32(mask->control[band].legacy) == 1) {
+ rate_idx = ffs(mask->control[band].legacy) - 1;
+ if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ)
+ rate_idx += ATH11K_MAC_FIRST_OFDM_RATE_IDX;
+
+ hw_rate = ath11k_legacy_rates[rate_idx].hw_value;
+ bitrate = ath11k_legacy_rates[rate_idx].bitrate;
+
+ if (ath11k_mac_bitrate_is_cck(bitrate))
+ preamble = WMI_RATE_PREAMBLE_CCK;
+ else
+ preamble = WMI_RATE_PREAMBLE_OFDM;
+
+ *nss = 1;
+ *rate = ATH11K_HW_RATE_CODE(hw_rate, 0, preamble);
+
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].ht_mcs); i++) {
+ if (hweight16(mask->control[band].ht_mcs[i]) == 1) {
+ *nss = i + 1;
+ hw_rate = ffs(mask->control[band].ht_mcs[i]) - 1;
+ *rate = ATH11K_HW_RATE_CODE(hw_rate, (*nss - 1),
+ WMI_RATE_PREAMBLE_HT);
+
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].vht_mcs); i++) {
+ if (hweight16(mask->control[band].vht_mcs[i]) == 1) {
+ *nss = i + 1;
+ hw_rate = ffs(mask->control[band].vht_mcs[i]) - 1;
+ *rate = ATH11K_HW_RATE_CODE(hw_rate, (*nss - 1),
+ WMI_RATE_PREAMBLE_VHT);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].he_mcs); i++) {
+ if (hweight16(mask->control[band].he_mcs[i]) == 1) {
+ *nss = i + 1;
+ hw_rate = ffs(mask->control[band].he_mcs[i]) - 1;
+ *rate = ATH11K_HW_RATE_CODE(hw_rate, (*nss - 1),
+ WMI_RATE_PREAMBLE_HE);
+
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
static int
ath11k_mac_bitrate_mask_num_vht_rates(struct ath11k *ar,
enum nl80211_band band,
@@ -4564,8 +4738,10 @@ static int ath11k_station_assoc(struct a
return ret;
}
}
+ if (!test_bit(WMI_TLV_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->ab->wmi_ab.svc_map))
+ return 0;
- return 0;
+ return ath11k_new_peer_tid_config(ar, sta, arvif);
}
static int ath11k_station_disassoc(struct ath11k *ar,
@@ -5091,6 +5267,201 @@ static int ath11k_mac_cfg_dyn_vlan(struc
return ret;
}
+static int ath11k_mac_reset_tid_config(struct ath11k *ar,
+ struct ieee80211_sta *sta,
+ struct ath11k_vif *arvif,
+ u8 tids)
+{
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+ struct wmi_per_peer_per_tid_cfg_arg arg;
+ int ret = 0, i = 0;
+
+ arg.vdev_id = arvif->vdev_id;
+ while (i < ATH11K_TID_MAX) {
+ if (!(tids & BIT(i))) {
+ i++;
+ continue;
+ }
+
+ arg.tid = i;
+ arg.ack_policy = WMI_PEER_TID_CONFIG_ACK;
+ arg.retry_count = ATH11K_MAX_RETRY_COUNT;
+ arg.rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_AUTO;
+ arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arg.rtscts_ctrl = WMI_TID_CONFIG_RTSCTS_CTRL_RESET;
+ arg.max_num_mpdu_in_ppdu = 0;
+ arg.max_num_msdu_in_mpdu = 0;
+ arg.ext_tid_cfg_bitmap = WMI_EXT_TID_RTS_CTS_CONFIG |
+ WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID |
+ WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+ ret = ath11k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
+ if (ret)
+ return ret;
+
+ if (!arvif->tids_rst) {
+ arsta->tid_cfg[i].retry_long = -1;
+ arsta->tid_cfg[i].noack = -1;
+ arsta->tid_cfg[i].aggr_ctrl = -1;
+ arsta->tid_cfg[i].rate_code = -1;
+ arsta->tid_cfg[i].rate_ctrl = 0;
+ arsta->tid_cfg[i].rtscts = -1;
+ } else {
+ arvif->tid_cfg[i].retry_long = 0;
+ arvif->tid_cfg[i].noack = 0;
+ arvif->tid_cfg[i].aggr_ctrl = 0;
+ arvif->tid_cfg[i].rate_code = 0;
+ arvif->tid_cfg[i].rate_ctrl = 0;
+ arvif->tid_cfg[i].rtscts = 0;
+ arvif->tid_cfg[i].ampdu_count = 0;
+ arvif->tid_cfg[i].amsdu_count = 0;
+ }
+ i++;
+ }
+ return ret;
+}
+
+static void ath11k_sta_tid_cfg_wk(struct work_struct *wk)
+{
+ struct wmi_per_peer_per_tid_cfg_arg arg = {};
+ struct ieee80211_sta *sta;
+ struct ath11k_sta *arsta;
+ struct ath11k_vif *arvif;
+ struct ath11k *ar;
+ bool config_apply;
+ int ret, i;
+ u32 changed;
+ u8 nss;
+
+ arsta = container_of(wk, struct ath11k_sta, tid_config_wk);
+ sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+ arvif = arsta->arvif;
+ ar = arvif->ar;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (arvif->tids_rst) {
+ ret = ath11k_mac_reset_tid_config(ar, sta, arvif,
+ arvif->tids_rst);
+ goto exit;
+ }
+
+ ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+ for (i = 0; i < ATH11K_TID_MAX; i++) {
+ config_apply = false;
+ changed = arvif->tid_conf_changed[i];
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
+ if (arsta->tid_cfg[i].noack != -1) {
+ arg.ack_policy = 0;
+ } else {
+ config_apply = true;
+ arg.ack_policy = arvif->tid_cfg[i].noack;
+ arg.aggr_control = arvif->tid_cfg[i].aggr_ctrl;
+ arg.rate_ctrl = arvif->tid_cfg[i].rate_ctrl;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG)) {
+ if (arsta->tid_cfg[i].retry_long != -1 ||
+ arsta->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg.retry_count = 0;
+ } else {
+ arg.retry_count = arvif->tid_cfg[i].retry_long;
+ config_apply = true;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
+ if (arsta->tid_cfg[i].aggr_ctrl != -1 ||
+ arsta->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else {
+ arg.aggr_control = arvif->tid_cfg[i].aggr_ctrl;
+ config_apply = true;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL)) {
+ if (arsta->tid_cfg[i].aggr_ctrl != -1 ||
+ arsta->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg.aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else {
+ arg.aggr_control = arvif->tid_cfg[i].aggr_ctrl;
+ config_apply = true;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
+ arg.max_num_mpdu_in_ppdu = arvif->tid_cfg[i].ampdu_count;
+ arg.ext_tid_cfg_bitmap = arvif->tid_cfg[i].ext_tid_cfg_bitmap;
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL)) {
+ arg.max_num_msdu_in_mpdu = arvif->tid_cfg[i].amsdu_count;
+ arg.ext_tid_cfg_bitmap = arvif->tid_cfg[i].ext_tid_cfg_bitmap;
+ }
+
+ if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
+ nss = ATH11K_HW_NSS(arvif->tid_cfg[i].rate_code);
+ ret = ath11k_mac_validate_rate_mask(ar, sta,
+ arvif->tid_cfg[i].rate_code,
+ nss);
+ if (ret &&
+ arvif->tid_cfg[i].rate_ctrl > WMI_TID_CONFIG_RATE_CONTROL_AUTO) {
+ arg.rate_ctrl = 0;
+ arg.rcode_flags = 0;
+ }
+
+ if (arsta->tid_cfg[i].rate_ctrl >
+ WMI_TID_CONFIG_RATE_CONTROL_AUTO ||
+ arsta->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[i].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg.rate_ctrl = 0;
+ arg.rcode_flags = 0;
+ } else {
+ arg.rate_ctrl = arvif->tid_cfg[i].rate_ctrl;
+ arg.rcode_flags = arvif->tid_cfg[i].rate_code;
+ config_apply = true;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
+ if (arsta->tid_cfg[i].rtscts) {
+ arg.rtscts_ctrl = 0;
+ arg.ext_tid_cfg_bitmap &= ~WMI_EXT_TID_RTS_CTS_CONFIG;
+ } else {
+ arg.rtscts_ctrl = arvif->tid_cfg[i].rtscts;
+ arg.ext_tid_cfg_bitmap |=
+ WMI_EXT_TID_RTS_CTS_CONFIG;
+ config_apply = true;
+ }
+ }
+
+ arg.tid = i;
+
+ if (config_apply) {
+ ret = ath11k_wmi_set_per_peer_per_tid_cfg(ar, &arg);
+ if (ret)
+ ath11k_warn(ar->ab, "failed to set per tid config for sta %pM: %d\n",
+ sta->addr, ret);
+ }
+
+ arg.ack_policy = 0;
+ arg.retry_count = 0;
+ arg.aggr_control = 0;
+ arg.rate_ctrl = 0;
+ arg.rcode_flags = 0;
+ arg.max_num_msdu_in_mpdu = 0;
+ arg.max_num_mpdu_in_ppdu = 0;
+ }
+exit:
+ mutex_unlock(&ar->conf_mutex);
+}
+
static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -5108,6 +5479,7 @@ static int ath11k_mac_op_sta_state(struc
new_state == IEEE80211_STA_NOTEXIST)) {
cancel_work_sync(&arsta->update_wk);
cancel_work_sync(&arsta->set_4addr_wk);
+ cancel_work_sync(&arsta->tid_config_wk);
}
mutex_lock(&ar->conf_mutex);
@@ -5119,6 +5491,7 @@ static int ath11k_mac_op_sta_state(struc
arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk);
+ INIT_WORK(&arsta->tid_config_wk, ath11k_sta_tid_cfg_wk);
ret = ath11k_mac_station_add(ar, vif, sta);
if (ret)
@@ -6302,6 +6675,8 @@ static void ath11k_mac_op_tx(struct ieee
struct ath11k_sta *arsta = NULL;
bool is_prb_rsp;
u16 frm_type = 0;
+ u8 tid, *qos_ctl;
+ bool noack = false;
int ret;
memset(skb_cb, 0, sizeof(*skb_cb));
@@ -6356,6 +6731,29 @@ static void ath11k_mac_op_tx(struct ieee
ar->monitor_vdev_created)
skb_cb->flags |= ATH11K_SKB_TX_STATUS;
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qos_ctl = ieee80211_get_qos_ctl(hdr);
+ tid = (*qos_ctl) & IEEE80211_QOS_CTL_TID_MASK;
+
+ if (arvif->tid_cfg[tid].noack == WMI_PEER_TID_CONFIG_NOACK)
+ noack = true;
+
+ /* sta specific config has high precedence over vif specific config
+ * Hence overwrite sta specific config if it is present
+ */
+ if (sta) {
+ arsta = (struct ath11k_sta *)sta->drv_priv;
+ if (arsta->tid_cfg[tid].noack == WMI_PEER_TID_CONFIG_NOACK)
+ noack = true;
+
+ if (arsta->tid_cfg[tid].noack == WMI_PEER_TID_CONFIG_ACK)
+ noack = false;
+ }
+
+ if (noack)
+ skb_cb->flags |= ATH11K_SKB_F_NOACK_TID;
+ }
+
if (ar->ab->nss.enabled)
ret = ath11k_nss_tx(arvif, skb);
else
@@ -8041,6 +8439,24 @@ ath11k_mac_bitrate_mask_num_ht_rates(str
}
static bool
+ath11k_mac_has_single_rate(struct ath11k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int num_rates = 0;
+
+ num_rates = hweight32(mask->control[band].legacy);
+
+ num_rates += ath11k_mac_bitrate_mask_num_ht_rates(ar, band, mask);
+
+ num_rates += ath11k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
+
+ num_rates += ath11k_mac_bitrate_mask_num_he_rates(ar, band, mask);
+
+ return num_rates == 1;
+}
+
+static bool
ath11k_mac_has_single_legacy_rate(struct ath11k *ar,
enum nl80211_band band,
const struct cfg80211_bitrate_mask *mask)
@@ -8172,6 +8588,533 @@ ath11k_mac_get_single_legacy_rate(struct
}
static int
+ath11k_mac_tid_bitrate_config(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 *rate_ctrl_flag, u8 *rate_ctrl,
+ enum nl80211_tx_rate_setting txrate_type,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct cfg80211_chan_def def;
+ enum nl80211_band band;
+ u8 nss;
+ u32 rate;
+ int ret;
+
+ if (WARN_ON(ath11k_mac_vif_chan(vif, &def)))
+ return -EINVAL;
+
+ if (txrate_type == NL80211_TX_RATE_AUTOMATIC) {
+ *rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_AUTO;
+ *rate_ctrl_flag = 0;
+ return 0;
+ }
+
+ band = def.chan->band;
+
+ if (!ath11k_mac_has_single_rate(ar, band, mask)) {
+ ath11k_warn(ar->ab, "Multiple rates configured for tidconf\n");
+ return -EINVAL;
+ }
+
+ ret = ath11k_mac_get_single_rate(ar, band, mask, &rate, &nss);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to get single rate: %d\n",
+ ret);
+ return ret;
+ }
+ *rate_ctrl_flag = rate;
+
+ if (sta && ath11k_mac_validate_rate_mask(ar, sta, *rate_ctrl_flag, nss)) {
+ ath11k_warn(ar->ab, "Rate mask validation failed\n");
+ return -EINVAL;
+ }
+
+ if (txrate_type == NL80211_TX_RATE_FIXED)
+ *rate_ctrl = WMI_TID_CONFIG_RATE_CONTROL_FIXED_RATE;
+ else if ((txrate_type == NL80211_TX_RATE_LIMITED) &&
+ (test_bit(WMI_TLV_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
+ ar->ab->wmi_ab.svc_map)))
+ *rate_ctrl = WMI_PEER_TID_CONFIG_RATE_UPPER_CAP;
+ else
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static int ath11k_mac_set_tid_config(struct ath11k *ar, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, u32 changed,
+ struct wmi_per_peer_per_tid_cfg_arg *arg)
+{
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ath11k_sta *arsta;
+ int ret;
+
+ if (sta) {
+ if (!sta->wme)
+ return -EOPNOTSUPP;
+
+ arsta = (struct ath11k_sta *)sta->drv_priv;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arg->rate_ctrl = 0;
+ arg->rcode_flags = 0;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
+ if (arsta->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arg->ext_tid_cfg_bitmap &= ~WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ changed &= ~BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG);
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL)) {
+ if (arsta->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arg->ext_tid_cfg_bitmap &= ~WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ changed &= ~BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG);
+ }
+ }
+
+ if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
+ if (arsta->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK ||
+ arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->rate_ctrl = 0;
+ arg->rcode_flags = 0;
+ }
+ }
+
+ if (arg->aggr_control == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arg->aggr_control_ampdu = 0;
+ arg->aggr_control_amsdu = 0;
+ arg->max_num_mpdu_in_ppdu = 0;
+ arg->max_num_msdu_in_mpdu = 0;
+ arg->ext_tid_cfg_bitmap &=
+ ~(WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID |
+ WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID);
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE &&
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else if ((arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_IGNORE) &&
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arg->max_num_msdu_in_mpdu = 1;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE &&
+ (arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_IGNORE)) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arg->max_num_mpdu_in_ppdu = 1;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE) {
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ }
+
+ ether_addr_copy(arg->peer_macaddr.addr, sta->addr);
+ ret = ath11k_wmi_set_per_peer_per_tid_cfg(ar, arg);
+ if (ret)
+ return ret;
+
+ /* Store the configured parameters in success case */
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK))
+ arsta->tid_cfg[arg->tid].noack = arg->ack_policy;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG))
+ arsta->tid_cfg[arg->tid].retry_long = arg->retry_count;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL))
+ arsta->tid_cfg[arg->tid].aggr_ctrl = arg->aggr_control;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL))
+ arsta->tid_cfg[arg->tid].aggr_ctrl = arg->aggr_control;
+
+ if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
+ arsta->tid_cfg[arg->tid].rate_ctrl = arg->rate_ctrl;
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL))
+ arsta->tid_cfg[arg->tid].rtscts = arg->rtscts_ctrl;
+
+ } else {
+ /* Below is per vif config, all configs are saved in arvif and then
+ * configured for every peer
+ */
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
+ arvif->tid_cfg[arg->tid].noack =
+ WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arvif->tid_cfg[arg->tid].rate_ctrl = 0;
+ arvif->tid_cfg[arg->tid].rate_code = 0;
+ } else {
+ arvif->tid_cfg[arg->tid].noack = arg->ack_policy;
+ arvif->tid_cfg[arg->tid].rate_ctrl = arg->rate_ctrl;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG)) {
+ if (arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK)
+ changed &= ~BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG);
+ else
+ arvif->tid_cfg[arg->tid].retry_long = arg->retry_count;
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
+ if (arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ changed &= ~BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL);
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arg->ext_tid_cfg_bitmap &=
+ ~WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_COUNT))
+ arvif->tid_cfg[arg->tid].ampdu_count = arg->max_num_mpdu_in_ppdu;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL)) {
+ if (arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ changed &= ~BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL);
+ arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ arg->ext_tid_cfg_bitmap &=
+ ~WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_COUNT))
+ arvif->tid_cfg[arg->tid].amsdu_count = arg->max_num_msdu_in_mpdu;
+
+ if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
+ if (arvif->tid_cfg[arg->tid].noack == WMI_PEER_TID_CONFIG_NOACK) {
+ changed &= ~(BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE));
+ } else {
+ arvif->tid_cfg[arg->tid].rate_ctrl = arg->rate_ctrl;
+ arvif->tid_cfg[arg->tid].rate_code = arg->rcode_flags;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
+ arvif->tid_cfg[arg->tid].rtscts = arg->rtscts_ctrl;
+ arg->ext_tid_cfg_bitmap |= WMI_EXT_TID_RTS_CTS_CONFIG;
+ }
+
+ if (arg->aggr_control == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arvif->tid_cfg[arg->tid].aggr_ctrl = arg->aggr_control;
+ arg->ext_tid_cfg_bitmap &=
+ ~(WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID |
+ WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID);
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE &&
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arvif->tid_cfg[arg->tid].aggr_ctrl = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else if ((arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_IGNORE) &&
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arvif->tid_cfg[arg->tid].aggr_ctrl = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arvif->tid_cfg[arg->tid].amsdu_count = 1;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE &&
+ (arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_IGNORE)) {
+ arvif->tid_cfg[arg->tid].aggr_ctrl = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arvif->tid_cfg[arg->tid].ampdu_count = 1;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ } else if (arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE ||
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_ENABLE) {
+ arvif->tid_cfg[arg->tid].aggr_ctrl = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ }
+
+ arvif->tid_cfg[arg->tid].ext_tid_cfg_bitmap = arg->ext_tid_cfg_bitmap;
+ if (changed)
+ arvif->tid_conf_changed[arg->tid] = changed;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "Per Vif tidconfig tid %d ack_policy %d aggr %u rate_ctrl %u rcflag 0x%x retry_count %d\n rtscts %d ext_tid_cfg_bitmap %d ampdu_count %d amsdu count %d tid_conf_changed 0x%x\n",
+ arg->tid, arvif->tid_cfg[arg->tid].noack,
+ arvif->tid_cfg[arg->tid].aggr_ctrl,
+ arvif->tid_cfg[arg->tid].rate_ctrl,
+ arvif->tid_cfg[arg->tid].rate_code,
+ arvif->tid_cfg[arg->tid].retry_long,
+ arvif->tid_cfg[arg->tid].rtscts,
+ arvif->tid_cfg[arg->tid].ext_tid_cfg_bitmap,
+ arvif->tid_cfg[arg->tid].ampdu_count,
+ arvif->tid_cfg[arg->tid].amsdu_count,
+ arvif->tid_conf_changed[arg->tid]);
+ }
+
+ return 0;
+}
+
+struct ath11k_mac_iter_tid_conf_data {
+ struct ieee80211_vif *curr_vif;
+ struct ath11k *ar;
+ bool reset_config;
+};
+
+static void ath11k_mac_vif_stations_tid_conf(void *data,
+ struct ieee80211_sta *sta)
+{
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+ struct ath11k_mac_iter_tid_conf_data *iter_data = data;
+ struct ieee80211_vif *sta_vif = arsta->arvif->vif;
+
+ if (sta_vif != iter_data->curr_vif || !sta->wme)
+ return;
+
+ ieee80211_queue_work(iter_data->ar->hw, &arsta->tid_config_wk);
+}
+
+static int ath11k_mac_op_reset_tid_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u8 tids)
+{
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ath11k_mac_iter_tid_conf_data data = {};
+ struct ath11k *ar = hw->priv;
+ int ret = 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (sta) {
+ arvif->tids_rst = 0;
+ ret = ath11k_mac_reset_tid_config(ar, sta, arvif, tids);
+ goto exit;
+ }
+
+ arvif->tids_rst = tids;
+ data.curr_vif = vif;
+ data.ar = ar;
+ ieee80211_iterate_stations_atomic(hw, ath11k_mac_vif_stations_tid_conf,
+ &data);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int
+ath11k_mac_parse_tid_config(struct ath11k *ar,
+ struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif,
+ struct cfg80211_tid_cfg *tid_conf,
+ struct wmi_per_peer_per_tid_cfg_arg *arg)
+{
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ u32 changed = tid_conf->mask;
+ int ret = 0, i = 0;
+
+ memset(arvif->tid_conf_changed, 0, sizeof(u32) * ATH11K_TID_MAX);
+ if (!changed)
+ return -EINVAL;
+
+ while (i < ATH11K_TID_MAX) {
+ if (!(tid_conf->tids & BIT(i))) {
+ i++;
+ continue;
+ }
+
+ memset(arg, 0, sizeof(struct wmi_per_peer_per_tid_cfg_arg));
+ arg->ack_policy = WMI_NOACK_TID_CONFIG_IGNORE_ACK_POLICY;
+ arg->tid = i;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_NOACK)) {
+ if (tid_conf->noack == NL80211_TID_CONFIG_ENABLE) {
+ arg->ack_policy = WMI_PEER_TID_CONFIG_NOACK;
+ arg->rate_ctrl =
+ WMI_TID_CONFIG_RATE_CONTROL_DEFAULT_LOWEST_RATE;
+ arg->aggr_control =
+ WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else {
+ arg->ack_policy =
+ WMI_PEER_TID_CONFIG_ACK;
+ arg->rate_ctrl =
+ WMI_TID_CONFIG_RATE_CONTROL_AUTO;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG))
+ arg->retry_count = tid_conf->retry_long;
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->aggr_control_ampdu = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else if (tid_conf->ampdu == NL80211_TID_CONFIG_ENABLE) {
+ arg->aggr_control_ampdu = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID;
+ } else if (tid_conf->ampdu == NL80211_TID_CONFIG_DISABLE) {
+ arg->aggr_control_ampdu = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else {
+ arg->aggr_control_ampdu = WMI_TID_CONFIG_AGGR_CONTROL_IGNORE;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMPDU_COUNT)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK ||
+ arg->aggr_control_ampdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arg->max_num_mpdu_in_ppdu = 0;
+ } else if (tid_conf->ampdu_count < 1 ||
+ tid_conf->ampdu_count > 1024) {
+ ath11k_warn(ar->ab, "Failed to configure ampdu count\n");
+ return -EINVAL;
+ } else {
+ arg->max_num_mpdu_in_ppdu = tid_conf->ampdu_count;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK) {
+ arg->aggr_control_amsdu = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else if (tid_conf->amsdu == NL80211_TID_CONFIG_ENABLE) {
+ arg->aggr_control_amsdu = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+ arg->ext_tid_cfg_bitmap |= WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID;
+ } else if (tid_conf->amsdu == NL80211_TID_CONFIG_DISABLE) {
+ arg->aggr_control_amsdu = WMI_TID_CONFIG_AGGR_CONTROL_DISABLE;
+ } else {
+ arg->aggr_control_amsdu = WMI_TID_CONFIG_AGGR_CONTROL_IGNORE;
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_AMSDU_COUNT)) {
+ if (arg->ack_policy == WMI_PEER_TID_CONFIG_NOACK ||
+ arg->aggr_control_amsdu == WMI_TID_CONFIG_AGGR_CONTROL_DISABLE) {
+ arg->max_num_msdu_in_mpdu = 0;
+ } else if (tid_conf->amsdu_count < 1 ||
+ tid_conf->amsdu_count > 7) {
+ ath11k_warn(ar->ab, "Failed to configure amsdu count\n");
+ return -EINVAL;
+ } else {
+ arg->max_num_msdu_in_mpdu = tid_conf->amsdu_count;
+ }
+ }
+
+ if (changed & (BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE))) {
+ ret = ath11k_mac_tid_bitrate_config(ar, vif, sta,
+ &arg->rcode_flags,
+ &arg->rate_ctrl,
+ tid_conf->txrate_type,
+ &tid_conf->txrate_mask);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to configure bitrate mask %d\n",
+ ret);
+ arg->rcode_flags = 0;
+ arg->rate_ctrl = 0;
+ changed &= ~(BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE));
+ }
+ }
+
+ if (changed & BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL)) {
+ if (tid_conf->rtscts == NL80211_TID_CONFIG_ENABLE) {
+ arg->rtscts_ctrl = WMI_TID_CONFIG_RTSCTS_CTRL_ENABLE;
+ } else if (tid_conf->rtscts == NL80211_TID_CONFIG_DISABLE) {
+ arg->rtscts_ctrl = WMI_TID_CONFIG_RTSCTS_CTRL_DISABLE;
+ } else {
+ ath11k_warn(ar->ab, "Invalid RTSCTS config\n");
+ return -EINVAL;
+ }
+
+ arg->ext_tid_cfg_bitmap |= WMI_EXT_TID_RTS_CTS_CONFIG;
+ }
+
+ ret = ath11k_mac_set_tid_config(ar, sta, vif, changed, arg);
+ if (ret)
+ return ret;
+ i++;
+ }
+ return ret;
+}
+
+static int ath11k_mac_op_set_tid_config(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct cfg80211_tid_config *tid_config)
+{
+ struct ath11k *ar = hw->priv;
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ struct ath11k_mac_iter_tid_conf_data data = {};
+ struct wmi_per_peer_per_tid_cfg_arg arg = {};
+ int ret, i;
+
+ mutex_lock(&ar->conf_mutex);
+ arg.vdev_id = arvif->vdev_id;
+
+ arvif->tids_rst = 0;
+ memset(arvif->tid_conf_changed, 0, sizeof(arvif->tid_conf_changed));
+
+ for (i = 0; i < tid_config->n_tid_conf; i++) {
+ ret = ath11k_mac_parse_tid_config(ar, sta, vif,
+ &tid_config->tid_conf[i],
+ &arg);
+ if (ret)
+ goto exit;
+ }
+
+ if (sta)
+ goto exit;
+
+ ret = 0;
+ arvif->tids_rst = 0;
+ data.curr_vif = vif;
+ data.ar = ar;
+
+ ieee80211_iterate_stations_atomic(hw, ath11k_mac_vif_stations_tid_conf,
+ &data);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int ath11k_mac_op_sta_set_mgmt_rts_cts(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct ath11k *ar = hw->priv;
+ struct ath11k_vif *arvif = (void *)vif->drv_priv;
+ int ret = 0;
+ int enable_rts_cts;
+
+ if (!test_bit(WMI_TLV_SERVICE_RTSCTS_FOR_UNICAST_MGMT_SUPPORT,
+ ar->ab->wmi_ab.svc_map)) {
+ ath11k_warn(ar->ab, "RTSCTS for unicast management frame no supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (sta->mgmt_rts_cts == NL80211_MGMT_RTS_CTS_ENABLE)
+ enable_rts_cts = 1;
+ else if (sta->mgmt_rts_cts == NL80211_MGMT_RTS_CTS_DISABLE)
+ enable_rts_cts = 0;
+ else
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ ret = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id,
+ WMI_PEER_PARAM_EN_RTSCTS_FOR_UNICAST_MGMT,
+ enable_rts_cts);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to set rts/cts for management unicaste frames %d\n",
+ ret);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int
ath11k_mac_set_fixed_rate_gi_ltf(struct ath11k_vif *arvif, u8 he_gi, u8 he_ltf)
{
struct ath11k *ar = arvif->ar;
@@ -9005,6 +9948,9 @@ static const struct ieee80211_ops ath11k
.get_survey = ath11k_mac_op_get_survey,
.flush = ath11k_mac_op_flush,
.sta_statistics = ath11k_mac_op_sta_statistics,
+ .set_tid_config = ath11k_mac_op_set_tid_config,
+ .reset_tid_config = ath11k_mac_op_reset_tid_config,
+ .sta_set_mgmt_rts_cts = ath11k_mac_op_sta_set_mgmt_rts_cts,
CFG80211_TESTMODE_CMD(ath11k_tm_cmd)
#ifdef CPTCFG_ATH11K_DEBUGFS
.sta_add_debugfs = ath11k_debugfs_sta_op_add,
@@ -9486,6 +10432,35 @@ static int __ath11k_mac_register(struct
wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ if (test_bit(WMI_TLV_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->ab->wmi_ab.svc_map)) {
+ ar->hw->wiphy->tid_config_support.vif |=
+ BIT(NL80211_TID_CONFIG_ATTR_NOACK) |
+ BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG) |
+ BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL) |
+ BIT(NL80211_TID_CONFIG_ATTR_AMPDU_COUNT) |
+ BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL) |
+ BIT(NL80211_TID_CONFIG_ATTR_AMSDU_COUNT) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE) |
+ BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE);
+
+ if (test_bit(WMI_TLV_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
+ ar->ab->wmi_ab.svc_map)) {
+ ar->hw->wiphy->tid_config_support.vif |=
+ BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL);
+ }
+
+ ar->hw->wiphy->tid_config_support.peer =
+ ar->hw->wiphy->tid_config_support.vif;
+ ar->hw->wiphy->max_data_retry_count = ATH11K_MAX_RETRY_COUNT;
+ } else {
+ ar->ops->set_tid_config = NULL;
+ }
+
+ if (test_bit(WMI_TLV_SERVICE_RTSCTS_FOR_UNICAST_MGMT_SUPPORT,
+ ar->ab->wmi_ab.svc_map))
+ wiphy_ext_feature_set(ar->hw->wiphy,
+ NL80211_EXT_FEATURE_STA_MGMT_RTS_CTS);
+
if (test_bit(WMI_TLV_SERVICE_SCAN_PHYMODE_SUPPORT,
ar->ab->wmi_ab.svc_map))
wiphy_ext_feature_set(ar->hw->wiphy,
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -724,6 +724,55 @@ int ath11k_wmi_qos_null_send(struct ath1
return ret;
}
+int ath11k_wmi_set_per_peer_per_tid_cfg(struct ath11k *ar,
+ const struct wmi_per_peer_per_tid_cfg_arg *arg)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_peer_per_tid_cfg_cmd *cmd;
+ struct sk_buff *skb = NULL;
+ int ret = 0;
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ memset(skb->data, 0, sizeof(*cmd));
+
+ cmd = (struct wmi_peer_per_tid_cfg_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_TID_CONFIGURATIONS_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+ cmd->vdev_id = arg->vdev_id;
+ ether_addr_copy(cmd->peer_macaddr.addr, arg->peer_macaddr.addr);
+ cmd->tid = arg->tid;
+ cmd->ack_policy = arg->ack_policy;
+ cmd->aggr_control = arg->aggr_control;
+ cmd->rate_control = arg->rate_ctrl;
+ cmd->retry_count = arg->retry_count;
+ cmd->rcode_flags = arg->rcode_flags;
+ cmd->ext_tid_cfg_bitmap = arg->ext_tid_cfg_bitmap;
+ cmd->rtscts_ctrl = arg->rtscts_ctrl;
+ cmd->max_num_mpdu_in_ppdu = arg->max_num_mpdu_in_ppdu;
+ cmd->max_num_msdu_in_mpdu = arg->max_num_msdu_in_mpdu;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi noack tid %d vdev id %d ack_policy %d aggr %u rate_ctrl %u rcflag 0x%x retry_count %d\n rtscts %d ext_tid_cfg_bitmap %d mac_addr %pM ampdu_count %d amsdu count %d\n",
+ arg->tid, arg->vdev_id, arg->ack_policy, arg->aggr_control,
+ arg->rate_ctrl, arg->rcode_flags, arg->retry_count, arg->rtscts_ctrl,
+ arg->ext_tid_cfg_bitmap, arg->peer_macaddr.addr,
+ arg->max_num_mpdu_in_ppdu, arg->max_num_msdu_in_mpdu);
+
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_PEER_TID_CONFIGURATIONS_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to submit WMI_PEER_TID_CONFIGURATIONS_CMDID cmd\n");
+ dev_kfree_skb(skb);
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi peer tid configuration cmd sent successfully\n");
+ return ret;
+}
+
int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr,
struct vdev_create_params *param)
{
@@ -4380,6 +4429,7 @@ ath11k_wmi_copy_resource_config(struct w
wmi_cfg->host_service_flags |= tg_cfg->is_reg_cc_ext_event_supported <<
WMI_RSRC_CFG_HOST_SERVICE_FLAG_REG_CC_EXT_SUPPORT_BIT;
wmi_cfg->smart_ant_cap = 1;
+ wmi_cfg->flag1 |= tg_cfg->peer_tid_ext;
}
static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi,
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -2189,6 +2189,7 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
WMI_TLV_SERVICE_DCS_AWGN_INT_SUPPORT = 286,
WMI_TLV_SERVICE_DYN_NSS_MASK_SUPPORT = 303,
+ WMI_TLV_SERVICE_RTSCTS_FOR_UNICAST_MGMT_SUPPORT = 309,
WMI_MAX_EXT2_SERVICE
};
@@ -2229,6 +2230,7 @@ enum {
#define WMI_PEER_SET_MIN_TX_RATE 0x12
#define WMI_PEER_SET_DEFAULT_ROUTING 0x13
#define WMI_PEER_PARAM_DYN_NSS_EN_MASK 0x24
+#define WMI_PEER_PARAM_EN_RTSCTS_FOR_UNICAST_MGMT 0x25
/* slot time long */
#define WMI_VDEV_SLOT_TIME_LONG 0x1
@@ -2410,6 +2412,7 @@ struct wmi_init_cmd {
#define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
#define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18)
#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_REG_CC_EXT_SUPPORT_BIT 4
+#define WMI_RSRC_CFG_FLAG_PEER_TID_EXT BIT(22)
struct wmi_resource_config {
u32 tlv_header;
@@ -6178,6 +6181,7 @@ struct target_resource_config {
u32 num_msdu_desc;
u32 max_frag_entries;
u32 max_peer_ext_stats;
+ u32 peer_tid_ext;
u32 smart_ant_cap;
u32 bk_minfree;
u32 be_minfree;
@@ -6404,6 +6408,78 @@ struct wmi_qos_null_tx_cmd {
u32 tx_params_valid;
} __packed;
+enum wmi_tid_aggr_control_conf {
+ WMI_TID_CONFIG_AGGR_CONTROL_IGNORE,
+ WMI_TID_CONFIG_AGGR_CONTROL_ENABLE,
+ WMI_TID_CONFIG_AGGR_CONTROL_DISABLE,
+};
+
+enum wmi_noack_tid_conf {
+ WMI_NOACK_TID_CONFIG_IGNORE_ACK_POLICY,
+ WMI_PEER_TID_CONFIG_ACK,
+ WMI_PEER_TID_CONFIG_NOACK,
+};
+
+enum wmi_tid_rate_ctrl_conf {
+ WMI_TID_CONFIG_RATE_CONTROL_IGNORE,
+ WMI_TID_CONFIG_RATE_CONTROL_AUTO,
+ WMI_TID_CONFIG_RATE_CONTROL_FIXED_RATE,
+ WMI_TID_CONFIG_RATE_CONTROL_DEFAULT_LOWEST_RATE,
+ WMI_PEER_TID_CONFIG_RATE_UPPER_CAP,
+};
+
+enum wmi_tid_rtscts_control_conf {
+ WMI_TID_CONFIG_RTSCTS_CTRL_RESET,
+ WMI_TID_CONFIG_RTSCTS_CTRL_DISABLE,
+ WMI_TID_CONFIG_RTSCTS_CTRL_ENABLE,
+};
+
+enum wmi_ext_tid_config_map {
+ WMI_EXT_TID_RTS_CTS_CONFIG = BIT(0),
+ WMI_PEER_TID_MAX_NUM_MPDU_IN_PPDU_VALID = BIT(1),
+ WMI_PEER_TID_MAX_NUM_MSDU_IN_MPDU_VALID = BIT(2),
+};
+
+struct wmi_per_peer_per_tid_cfg_arg {
+ u32 vdev_id;
+ struct wmi_mac_addr peer_macaddr;
+ u32 tid;
+ enum wmi_noack_tid_conf ack_policy;
+ enum wmi_tid_aggr_control_conf aggr_control;
+ enum wmi_tid_aggr_control_conf aggr_control_ampdu;
+ enum wmi_tid_aggr_control_conf aggr_control_amsdu;
+ u8 rate_ctrl;
+ u32 rcode_rcflags;
+ u32 retry_count;
+ u32 rcode_flags;
+ u32 ext_tid_cfg_bitmap;
+ u32 rtscts_ctrl;
+ u32 max_num_mpdu_in_ppdu;
+ u32 max_num_msdu_in_mpdu;
+};
+
+struct wmi_peer_per_tid_cfg_cmd {
+ u32 tlv_header;
+ u32 vdev_id;
+ struct wmi_mac_addr peer_macaddr;
+ u32 tid;
+
+ /* see enum wmi_noack_tid_conf */
+ u32 ack_policy;
+ /* see enum wmi_tid_aggr_control_conf */
+ u32 aggr_control;
+ /* see enum wmi_tid_rate_ctrl_conf */
+ u32 rate_control;
+ u32 rcode_flags;
+ u32 retry_count;
+ /* See enum wmi_ext_tid_config_map */
+ u32 ext_tid_cfg_bitmap;
+ /* see enum wmi_tid_rtscts_control_conf */
+ u32 rtscts_ctrl;
+ u32 max_num_mpdu_in_ppdu;
+ u32 max_num_msdu_in_mpdu;
+} __packed;
+
#define WMI_MAX_MEM_REQS 32
#define MAX_RADIOS 3
@@ -6740,6 +6816,8 @@ int ath11k_wmi_mgmt_send(struct ath11k *
struct sk_buff *frame, bool tx_params_valid);
int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
struct sk_buff *frame);
+int ath11k_wmi_set_per_peer_per_tid_cfg(struct ath11k *ar,
+ const struct wmi_per_peer_per_tid_cfg_arg *arg);
int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id,
struct ieee80211_mutable_offsets *offs,
struct sk_buff *bcn, u32 ema_param);