[PATCH] wcn36xx: initiate TX BA sessions

Bob Copeland me at bobcopeland.com
Fri Mar 7 12:58:55 EST 2014


Currently, wcn36xx only asks for a TX BA session if it has
already established one for RX.  Thus, two wcn36xx devices cannot
do a-mpdu between themselves since they both wait for the other
to go first.  Fix this by starting a BA session after a few QoS
data frames have been sent to a STA.

Signed-off-by: Bob Copeland <bob at cozybit.com>
---
This applies on top of my previous three patches.

 main.c    |   13 ++++++++++++-
 txrx.c    |   41 +++++++++++++++++++++++++++++++++++++++++
 wcn36xx.h |   20 ++++++++++++++++++++
 3 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/main.c b/main.c
index 10ef672..17f4aaa 100644
--- a/main.c
+++ b/main.c
@@ -915,21 +915,32 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
 			get_sta_index(vif, sta_priv));
 		wcn36xx_smd_add_ba(wcn);
 		wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
-		ieee80211_start_tx_ba_session(sta, tid, 0);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
 		wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
 		break;
 	case IEEE80211_AMPDU_TX_START:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
 			get_sta_index(vif, sta_priv));
 		break;
 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 	case IEEE80211_AMPDU_TX_STOP_CONT:
+		spin_lock_bh(&sta_priv->ampdu_lock);
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
+		spin_unlock_bh(&sta_priv->ampdu_lock);
+
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
 	default:
diff --git a/txrx.c b/txrx.c
index ce87313..2bfe43a 100644
--- a/txrx.c
+++ b/txrx.c
@@ -199,6 +199,45 @@ static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
 	*vif_priv = __vif_priv;
 }
 
+/*
+ * Initiate TX a-mpdu with the station if we can.
+ */
+static void wcn36xx_tx_start_ampdu(struct wcn36xx *wcn,
+				   struct wcn36xx_sta *sta_priv,
+				   struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr;
+	struct ieee80211_sta *sta;
+	u8 *qc, tid;
+
+	if (!sta_priv || !conf_is_ht(&wcn->hw->conf))
+		return;
+
+	sta = wcn36xx_priv_to_sta(sta_priv);
+
+	hdr = (struct ieee80211_hdr *) skb->data;
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return;
+
+	if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
+		return;
+
+	qc = ieee80211_get_qos_ctl(hdr);
+	tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+
+	spin_lock(&sta_priv->ampdu_lock);
+	if (sta_priv->ampdu_state[tid] != WCN36XX_AMPDU_NONE)
+		goto out_unlock;
+
+	if (sta_priv->non_agg_frame_ct++ >= WCN36XX_AMPDU_START_THRESH) {
+		sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
+		sta_priv->non_agg_frame_ct = 0;
+		ieee80211_start_tx_ba_session(sta, tid, 0);
+	}
+out_unlock:
+	spin_unlock(&sta_priv->ampdu_lock);
+}
+
 int wcn36xx_start_tx(struct wcn36xx *wcn,
 		     struct wcn36xx_sta *sta_priv,
 		     struct sk_buff *skb)
@@ -212,6 +251,8 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
 		is_multicast_ether_addr(hdr->addr1);
 	struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
 
+	wcn36xx_tx_start_ampdu(wcn, sta_priv, skb);
+
 	if (!bd) {
 		/*
 		 * TX DXE are used in pairs. One for the BD and one for the
diff --git a/wcn36xx.h b/wcn36xx.h
index 70b0d06..725389e 100644
--- a/wcn36xx.h
+++ b/wcn36xx.h
@@ -33,6 +33,9 @@
 #define WLAN_NV_FILE               "wlan/prima/WCNSS_qcom_wlan_nv.bin"
 #define WCN36XX_AGGR_BUFFER_SIZE 64
 
+/* How many frames until we start a-mpdu TX session */
+#define WCN36XX_AMPDU_START_THRESH	20
+
 extern unsigned int wcn36xx_dbg_mask;
 
 enum wcn36xx_debug_mask {
@@ -75,6 +78,13 @@ enum wcn36xx_debug_mask {
 			       buf, len, false);		\
 } while (0)
 
+enum wcn36xx_ampdu_state {
+	WCN36XX_AMPDU_NONE,
+	WCN36XX_AMPDU_INIT,
+	WCN36XX_AMPDU_START,
+	WCN36XX_AMPDU_OPERATIONAL,
+};
+
 #define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value)
 #define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
 #define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
@@ -166,6 +176,10 @@ struct wcn36xx_sta {
 	bool is_data_encrypted;
 	/* Rates */
 	struct wcn36xx_hal_supported_rates supported_rates;
+
+	spinlock_t ampdu_lock;		/* protects next two fields */
+	enum wcn36xx_ampdu_state ampdu_state[16];
+	int non_agg_frame_ct;
 };
 struct wcn36xx_dxe_ch;
 struct wcn36xx {
@@ -262,4 +276,10 @@ struct wcn36xx_sta *wcn36xx_sta_to_priv(struct ieee80211_sta *sta)
 	return (struct wcn36xx_sta *)sta->drv_priv;
 }
 
+static inline
+struct ieee80211_sta *wcn36xx_priv_to_sta(struct wcn36xx_sta *sta_priv)
+{
+	return container_of((void *) sta_priv, struct ieee80211_sta, drv_priv);
+}
+
 #endif	/* _WCN36XX_H_ */
-- 
1.7.10.4




More information about the wcn36xx mailing list