[6/6] ath10k: Add support for station specific noack TID policy
diff mbox series

Message ID 1539715271-14325-7-git-send-email-tamizhr@codeaurora.org
State New
Headers show
Series
  • wireless: Per-sta NoAck and offload support
Related show

Commit Message

Tamizh chelvam Oct. 16, 2018, 6:41 p.m. UTC
This patch add ops for set_noack_tid_bitmap to support
station specific noack policy functionality for a TID.
TID will be parsed from noack_map parameter and pass to target
with station's mac address and the ack policy, and the value
is stored in arsta.

Suppose if the station entry is not mentioned in the command, the configuration
will be applied for all the connected stations in the vif which have default
value(-1) in the arsta structure. And this will be applied for newly connecting
station as well.

ATH10K_SKB_F_NOACK_TID skb cb flag introduced to avoid
setting IEEE80211_TX_STAT_ACK flag in the tx completion.

WMI_SERVICE_PEER_TID_CONFIGS_SUPPORT flag is added to
advertise this command support

Testing:
       * Tested HW: QCA9888
       * Tested FW: 10.4-3.5.1-00052

Signed-off-by: Tamizh chelvam <tamizhr@codeaurora.org>
---
 drivers/net/wireless/ath/ath10k/core.h |  11 +++
 drivers/net/wireless/ath/ath10k/mac.c  | 172 ++++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath10k/txrx.c |  12 ++-
 3 files changed, 188 insertions(+), 7 deletions(-)

Patch
diff mbox series

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 0424180..418eb19 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -90,6 +90,9 @@ 
 /* The magic used by QCA spec */
 #define ATH10K_SMBIOS_BDF_EXT_MAGIC "BDF_"
 
+/* Number of TID target accepts from host */
+#define ATH10K_MAX_TIDS	0x8
+
 struct ath10k;
 
 static inline const char *ath10k_bus_str(enum ath10k_bus bus)
@@ -116,6 +119,7 @@  enum ath10k_skb_flags {
 	ATH10K_SKB_F_DELIVER_CAB = BIT(2),
 	ATH10K_SKB_F_MGMT = BIT(3),
 	ATH10K_SKB_F_QOS = BIT(4),
+	ATH10K_SKB_F_NOACK_TID = BIT(5),
 };
 
 struct ath10k_skb_cb {
@@ -498,6 +502,10 @@  struct ath10k_sta {
 	u64 rx_duration;
 	struct ath10k_htt_tx_stats *tx_stats;
 
+	/* TID bitmap for station's NoAck policy, protected by conf_mutex */
+	int noack_map;
+	struct work_struct noack_map_wk;
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 	/* protected by conf_mutex */
 	bool aggr_mode;
@@ -573,6 +581,9 @@  struct ath10k_vif {
 	struct work_struct ap_csa_work;
 	struct delayed_work connection_loss_work;
 	struct cfg80211_bitrate_mask bitrate_mask;
+
+	/* TID bitmap for station's NoAck policy, protected by conf_mutex */
+	int noack_map;
 };
 
 struct ath10k_vif_iter {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a1c2801..54ec919 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2940,6 +2940,39 @@  static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
 	cancel_delayed_work_sync(&arvif->connection_loss_work);
 }
 
+static int
+ath10k_mac_set_noack_tid_bitmap(struct ath10k *ar,
+				struct wmi_per_peer_per_tid_cfg_arg *arg,
+				int noack_map)
+{
+	int tid, ret;
+
+	for (tid = 0; tid < ATH10K_MAX_TIDS; tid++) {
+		if (noack_map & BIT(tid)) {
+			arg->ack_policy = WMI_PEER_TID_CONFIG_NOACK;
+			/* It is preferred to transmit the noack policy frames
+			 * in basic rate. for 5G -> 6Mbps 2G -> 1Mbps
+			 */
+			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;
+			arg->aggr_control = WMI_TID_CONFIG_AGGR_CONTROL_ENABLE;
+		}
+
+		arg->tid = tid;
+		ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, arg);
+		if (ret) {
+			ath10k_warn(ar, "failed to set noack map for STA %pM for vdev %i: %d\n",
+				    arg->peer_macaddr.addr, arg->vdev_id, ret);
+			break;
+		}
+	}
+	return ret;
+}
+
 static int ath10k_station_assoc(struct ath10k *ar,
 				struct ieee80211_vif *vif,
 				struct ieee80211_sta *sta,
@@ -2947,6 +2980,7 @@  static int ath10k_station_assoc(struct ath10k *ar,
 {
 	struct ath10k_vif *arvif = (void *)vif->drv_priv;
 	struct wmi_peer_assoc_complete_arg peer_arg;
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
 	int ret = 0;
 
 	lockdep_assert_held(&ar->conf_mutex);
@@ -3005,6 +3039,23 @@  static int ath10k_station_assoc(struct ath10k *ar,
 		}
 	}
 
+	if (arvif->noack_map) {
+		struct wmi_per_peer_per_tid_cfg_arg arg = {};
+
+		arg.vdev_id = arvif->vdev_id;
+		ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+
+		ret = ath10k_mac_set_noack_tid_bitmap(ar, &arg,
+						      arvif->noack_map);
+		if (ret)
+			return ret;
+	}
+
+	/* Assign default noack_map value (-1) to newly connecting station.
+	 * This default value used to identify the station configured with
+	 * vif specific noack configuration rather than station specific.
+	 */
+	arsta->noack_map = -1;
 	return ret;
 }
 
@@ -3543,10 +3594,15 @@  static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
 static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
 				    struct ieee80211_vif *vif,
 				    struct ieee80211_txq *txq,
+				    struct ieee80211_sta *sta,
 				    struct sk_buff *skb)
 {
 	struct ieee80211_hdr *hdr = (void *)skb->data;
 	struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+	struct ath10k_vif *arvif = (void *)vif->drv_priv;
+	int noack_map = arvif->noack_map;
+	struct ath10k_sta *arsta;
+	u8 tid, *p;
 
 	cb->flags = 0;
 	if (!ath10k_tx_h_use_hwcrypto(vif, skb))
@@ -3555,8 +3611,20 @@  static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
 	if (ieee80211_is_mgmt(hdr->frame_control))
 		cb->flags |= ATH10K_SKB_F_MGMT;
 
-	if (ieee80211_is_data_qos(hdr->frame_control))
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
 		cb->flags |= ATH10K_SKB_F_QOS;
+		p = ieee80211_get_qos_ctl(hdr);
+		tid = (*p) & IEEE80211_QOS_CTL_TID_MASK;
+
+		if (sta) {
+			arsta = (struct ath10k_sta *)sta->drv_priv;
+			if (arsta->noack_map != -1)
+				noack_map = arsta->noack_map;
+		}
+
+		if (noack_map & BIT(tid))
+			cb->flags |= ATH10K_SKB_F_NOACK_TID;
+	}
 
 	cb->vif = vif;
 	cb->txq = txq;
@@ -3987,7 +4055,7 @@  int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
 		return -ENOENT;
 	}
 
-	ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
+	ath10k_mac_tx_h_fill_cb(ar, vif, txq, sta, skb);
 
 	skb_len = skb->len;
 	txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
@@ -4258,7 +4326,7 @@  static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
 	bool is_presp;
 	int ret;
 
-	ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
+	ath10k_mac_tx_h_fill_cb(ar, vif, txq, sta, skb);
 
 	txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
 	txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
@@ -6247,6 +6315,42 @@  static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif,
 	ar->num_stations--;
 }
 
+struct ath10k_mac_iter_noack_map_data {
+	struct ieee80211_vif *curr_vif;
+	struct ath10k *ar;
+};
+
+static void ath10k_sta_set_noack_tid_bitmap(struct work_struct *wk)
+{
+	struct wmi_per_peer_per_tid_cfg_arg arg = {};
+	struct ieee80211_sta *sta;
+	struct ath10k_sta *arsta;
+	struct ath10k_vif *arvif;
+	struct ath10k *ar;
+
+	arsta = container_of(wk, struct ath10k_sta, noack_map_wk);
+	sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+	arvif = arsta->arvif;
+	ar = arvif->ar;
+
+	arg.vdev_id = arvif->vdev_id;
+	ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+	ath10k_mac_set_noack_tid_bitmap(ar, &arg, arvif->noack_map);
+}
+
+static void ath10k_mac_vif_stations_noack_map(void *data,
+					      struct ieee80211_sta *sta)
+{
+	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+	struct ath10k_mac_iter_noack_map_data *iter_data = data;
+	struct ieee80211_vif *sta_vif = arsta->arvif->vif;
+
+	if (sta_vif != iter_data->curr_vif || arsta->noack_map != -1)
+		return;
+
+	ieee80211_queue_work(iter_data->ar->hw, &arsta->noack_map_wk);
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
 			    struct ieee80211_vif *vif,
 			    struct ieee80211_sta *sta,
@@ -6266,6 +6370,8 @@  static int ath10k_sta_state(struct ieee80211_hw *hw,
 		arsta->arvif = arvif;
 		arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
 		INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+		INIT_WORK(&arsta->noack_map_wk,
+			  ath10k_sta_set_noack_tid_bitmap);
 
 		for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
 			ath10k_mac_txq_init(sta->txq[i]);
@@ -6273,8 +6379,10 @@  static int ath10k_sta_state(struct ieee80211_hw *hw,
 
 	/* cancel must be done outside the mutex to avoid deadlock */
 	if ((old_state == IEEE80211_STA_NONE &&
-	     new_state == IEEE80211_STA_NOTEXIST))
+	     new_state == IEEE80211_STA_NOTEXIST)) {
 		cancel_work_sync(&arsta->update_wk);
+		cancel_work_sync(&arsta->noack_map_wk);
+	}
 
 	mutex_lock(&ar->conf_mutex);
 
@@ -7882,6 +7990,55 @@  static void ath10k_sta_statistics(struct ieee80211_hw *hw,
 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
 }
 
+static int ath10k_mac_op_set_noack_tid_bitmap(struct ieee80211_hw *hw,
+					      struct ieee80211_vif *vif,
+					      struct ieee80211_sta *sta,
+					      int noack_map)
+{
+	struct ath10k_vif *arvif = (void *)vif->drv_priv;
+	struct ath10k_mac_iter_noack_map_data data = {};
+	struct wmi_per_peer_per_tid_cfg_arg arg = {};
+	struct ath10k *ar = hw->priv;
+	struct ath10k_sta *arsta;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	arg.vdev_id = arvif->vdev_id;
+	if (sta) {
+		arsta = (struct ath10k_sta *)sta->drv_priv;
+		ether_addr_copy(arg.peer_macaddr.addr, sta->addr);
+		if (arsta->noack_map == noack_map)
+			goto exit;
+
+		if (noack_map == -1)
+			ret = ath10k_mac_set_noack_tid_bitmap(ar, &arg,
+							      arvif->noack_map);
+		else
+			ret = ath10k_mac_set_noack_tid_bitmap(ar, &arg,
+							      noack_map);
+		if (!ret)
+			arsta->noack_map = noack_map;
+
+		goto exit;
+	}
+
+	if (arvif->noack_map == noack_map)
+		goto exit;
+
+	data.curr_vif = vif;
+	data.ar = ar;
+	arvif->noack_map = noack_map;
+	ieee80211_iterate_stations_atomic(hw,
+					  ath10k_mac_vif_stations_noack_map,
+					  &data);
+	arvif->noack_map = noack_map;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_mac_op_tx,
 	.wake_tx_queue			= ath10k_mac_op_wake_tx_queue,
@@ -7924,6 +8081,7 @@  static void ath10k_sta_statistics(struct ieee80211_hw *hw,
 	.switch_vif_chanctx		= ath10k_mac_op_switch_vif_chanctx,
 	.sta_pre_rcu_remove		= ath10k_mac_op_sta_pre_rcu_remove,
 	.sta_statistics			= ath10k_sta_statistics,
+	.set_noack_tid_bitmap           = ath10k_mac_op_set_noack_tid_bitmap,
 
 	CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
 
@@ -8578,6 +8736,12 @@  int ath10k_mac_register(struct ath10k *ar)
 		wiphy_ext_feature_set(ar->hw->wiphy,
 				      NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
 
+	if (test_bit(WMI_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->wmi.svc_map))
+		wiphy_ext_feature_set(ar->hw->wiphy,
+				      NL80211_EXT_FEATURE_PER_STA_NOACK_MAP);
+	else
+		ar->ops->set_noack_tid_bitmap = NULL;
+
 	/*
 	 * on LL hardware queues are managed entirely by the FW
 	 * so we only advertise to mac we can do the queues thing
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 23606b6..3b385d4 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -61,6 +61,7 @@  int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
 	struct ath10k_skb_cb *skb_cb;
 	struct ath10k_txq *artxq;
 	struct sk_buff *msdu;
+	u8 flags;
 
 	ath10k_dbg(ar, ATH10K_DBG_HTT,
 		   "htt tx completion msdu_id %u status %d\n",
@@ -89,6 +90,8 @@  int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
 		artxq->num_fw_queued--;
 	}
 
+	flags = skb_cb->flags;
+
 	ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
 	ath10k_htt_tx_dec_pending(htt);
 	if (htt->num_pending_tx == 0)
@@ -104,18 +107,21 @@  int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
 	memset(&info->status, 0, sizeof(info->status));
 	trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
 
-	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+	    !(flags & ATH10K_SKB_F_NOACK_TID))
 		info->flags |= IEEE80211_TX_STAT_ACK;
 
 	if (tx_done->status == HTT_TX_COMPL_STATE_NOACK)
 		info->flags &= ~IEEE80211_TX_STAT_ACK;
 
 	if ((tx_done->status == HTT_TX_COMPL_STATE_ACK) &&
-	    (info->flags & IEEE80211_TX_CTL_NO_ACK))
+	    ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
+	    (flags & ATH10K_SKB_F_NOACK_TID)))
 		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
 
 	if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
-		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+		if ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
+		    (flags & ATH10K_SKB_F_NOACK_TID))
 			info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED;
 		else
 			info->flags &= ~IEEE80211_TX_STAT_ACK;