[PATCH] Refactor TX path

Eugene Krasnikov k.eugene.e at gmail.com
Fri Jul 5 15:19:33 EDT 2013


With this patch all parameters will be first passed to txrx
module and only after that to dxe. This will allow passing
less parameters to the functions. Also make sure that data
frames will get as fast as possible to the HW.

Short function description:

wcn36xx_start_tx – fills in BD and trigger dxe to start
transmitting.

wcn36xx_fill_tx_pdu – fills is PDU part of BD.

wcn36xx_dxe_tx_frame – transmits one frame from the head
of the descriptors.

Signed-off-by: Eugene Krasnikov <k.eugene.e at gmail.com>
---
 dxe.c     |  59 +++++---------------------
 dxe.h     |  13 ++----
 main.c    |  35 +---------------
 txrx.c    | 139 +++++++++++++++++++++++++++++++++++++++++++++-----------------
 txrx.h    |  16 ++++++--
 wcn36xx.h |   1 +
 6 files changed, 130 insertions(+), 133 deletions(-)

diff --git a/dxe.c b/dxe.c
index 7237f4f..5e0dd89 100644
--- a/dxe.c
+++ b/dxe.c
@@ -24,6 +24,13 @@
 #include "wcn36xx.h"
 #include "txrx.h"
 
+void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low)
+{
+	struct wcn36xx_dxe_ch *ch = is_low ?
+		&wcn->dxe_tx_l_ch :
+		&wcn->dxe_tx_h_ch;
+	return ch->head_blk_ctl->bd_cpu_addr;
+}
 static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
 {
 	wcn36xx_dbg(WCN36XX_DBG_DXE,
@@ -525,63 +532,19 @@ void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
 	}
 }
 
-int wcn36xx_dxe_tx(struct wcn36xx *wcn,
-		   struct sk_buff *skb,
-		   u8 broadcast,
-		   bool is_high,
-		   u32 header_len,
-		   bool tx_ack,
-		   struct wcn_sta *sta_priv)
+int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
+			 struct sk_buff *skb,
+			 bool is_low)
 {
 	struct wcn36xx_dxe_ctl *ctl = NULL;
 	struct wcn36xx_dxe_desc *desc = NULL;
 	struct wcn36xx_dxe_ch *ch = NULL;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-	unsigned long flags;
-
-	ch = is_high ? &wcn->dxe_tx_h_ch : &wcn->dxe_tx_l_ch;
-
-	if (tx_ack) {
-		wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested");
-		spin_lock_irqsave(&wcn->dxe_lock, flags);
-		if (wcn->tx_ack_skb) {
-			spin_unlock_irqrestore(&wcn->dxe_lock, flags);
-			wcn36xx_warn("tx_ack_skb already set");
-			ieee80211_free_txskb(wcn->hw, skb);
-			return -EINVAL;
-		}
-
-		wcn->tx_ack_skb = skb;
-		spin_unlock_irqrestore(&wcn->dxe_lock, flags);
 
-		/* Only one at a time is supported by fw. Stop the TX queues
-		 * until the ack status gets back.
-		 *
-		 * TODO: Add watchdog in case FW does not answer
-		 */
-		ieee80211_stop_queues(wcn->hw);
-	}
+	ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
 
 	ctl = ch->head_blk_ctl;
 	ctl->skb = NULL;
 	desc = ctl->desc;
-	if (!ctl->bd_cpu_addr) {
-		/* TX DXE are used in pairs. One for the BD and one for the
-		   actual frame. The BD DXE's has a preallocated buffer while
-		   the skb ones does not. If this isn't true something is really
-		   wierd. TODO: Recover from this situation
-		 */
-
-		wcn36xx_error("bd_cpu_addr may not be NULL for BD DXE");
-		return -EINVAL;
-	}
-
-	wcn36xx_prepare_tx_bd(ctl->bd_cpu_addr, skb->len, header_len);
-	wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast,
-			   hdr, tx_ack, sta_priv);
-
-	ctl = ch->head_blk_ctl;
-	desc = ctl->desc;
 
 	/* Set source address of the BD we send */
 	desc->src_addr_l = ctl->bd_phy_addr;
diff --git a/dxe.h b/dxe.h
index 9274d51..0b154bb 100644
--- a/dxe.h
+++ b/dxe.h
@@ -222,8 +222,6 @@ struct wcn36xx_dxe_mem_pool {
 	void		*virt_addr;
 	dma_addr_t	phy_addr;
 };
-struct wcn36xx;
-struct wcn_sta;
 int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn);
 void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn);
 void wcn36xx_rx_ready_work(struct work_struct *work);
@@ -232,12 +230,9 @@ void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn);
 int wcn36xx_dxe_init(struct wcn36xx *wcn);
 void wcn36xx_dxe_deinit(struct wcn36xx *wcn);
 int wcn36xx_dxe_init_channels(struct wcn36xx *wcn);
-int wcn36xx_dxe_tx(struct wcn36xx *wcn,
-		   struct sk_buff *skb,
-		   u8 broadcast,
-		   bool is_high,
-		   u32 header_len,
-		   bool tx_compl,
-		   struct wcn_sta *sta_priv);
+int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
+			 struct sk_buff *skb,
+			 bool is_low);
 void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
+void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low);
 #endif	/* _DXE_H_ */
diff --git a/main.c b/main.c
index 74646b0..cbfe403 100644
--- a/main.c
+++ b/main.c
@@ -14,7 +14,6 @@
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/etherdevice.h>
 #include <linux/module.h>
 #include <linux/wcnss_wlan.h>
 #include "wcn36xx.h"
@@ -309,46 +308,14 @@ static void wcn36xx_tx(struct ieee80211_hw *hw,
 		       struct ieee80211_tx_control *control,
 		       struct sk_buff *skb)
 {
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	struct ieee80211_mgmt *mgmt;
-	bool high, bcast, tx_compl;
-	u32 header_len = 0;
 	struct wcn36xx *wcn = hw->priv;
 	struct wcn_sta *sta_priv = NULL;
 
 	if (control->sta)
 		sta_priv = (struct wcn_sta *)control->sta->drv_priv;
 
-	tx_compl = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
-	mgmt = (struct ieee80211_mgmt *)skb->data;
-
-	high = !(ieee80211_is_data(mgmt->frame_control) ||
-		 ieee80211_is_data_qos(mgmt->frame_control));
-
-	bcast = is_broadcast_ether_addr(mgmt->da) ||
-		is_multicast_ether_addr(mgmt->da);
-
-	/*
-	 * In joining state trick hardware that probe is sent as unicast even
-	 * if address is broadcast.
-	 */
-	if (wcn->is_joining && ieee80211_is_probe_req(mgmt->frame_control))
-		bcast = false;
-	wcn36xx_dbg(WCN36XX_DBG_TX,
-		    "tx skb %p len %d fc %04x sn %d %s %s",
-		    skb, skb->len, __le16_to_cpu(mgmt->frame_control),
-		    IEEE80211_SEQ_TO_SN(__le16_to_cpu(mgmt->seq_ctrl)),
-		    high ? "high" : "low", bcast ? "bcast" : "ucast");
-
-	wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
-
-	header_len = ieee80211_is_data_qos(mgmt->frame_control) ?
-		sizeof(struct ieee80211_qos_hdr) :
-		sizeof(struct ieee80211_hdr_3addr);
-	wcn36xx_dxe_tx(hw->priv, skb, bcast, high,
-		       header_len, tx_compl, sta_priv);
+	wcn36xx_start_tx(wcn, sta_priv, skb);
 }
-
 static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 			   struct ieee80211_vif *vif,
 			   struct ieee80211_sta *sta,
diff --git a/txrx.c b/txrx.c
index 3b85727..5c35ec0 100644
--- a/txrx.c
+++ b/txrx.c
@@ -73,52 +73,85 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
 
 	return 0;
 }
-void wcn36xx_prepare_tx_bd(struct wcn36xx_tx_bd *bd, u32 len, u32 header_len)
+
+void wcn36xx_fill_tx_pdu(struct wcn36xx_tx_bd *bd,
+			 u32 mpdu_header_len,
+			 u32 len)
 {
-	memset(bd, 0, sizeof(*bd));
-	bd->pdu.mpdu_header_len = header_len;
+	bd->pdu.mpdu_header_len = mpdu_header_len;
 	bd->pdu.mpdu_header_off = sizeof(*bd);
 	bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len +
 		bd->pdu.mpdu_header_off;
 	bd->pdu.mpdu_len = len;
+	bd->pdu.tid = WCN36XX_TID;
 }
-void wcn36xx_fill_tx_bd(struct wcn36xx *wcn, struct wcn36xx_tx_bd *bd,
-			u8 broadcast, struct ieee80211_hdr *hdr,
-			bool tx_compl, struct wcn_sta *sta_priv)
+
+int wcn36xx_start_tx(struct wcn36xx *wcn,
+		     struct wcn_sta *sta_priv,
+		     struct sk_buff *skb)
 {
-	bd->dpu_rf = WCN36XX_BMU_WQ_TX;
-	bd->pdu.tid   = WCN36XX_TID;
-	bd->pdu.reserved3 = 0xd;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	unsigned long flags;
+	bool is_low = ieee80211_is_data(hdr->frame_control);
+	bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
+		is_multicast_ether_addr(hdr->addr1);
+	struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
+
+	if (!bd) {
+		/*
+		 * TX DXE are used in pairs. One for the BD and one for the
+		 * actual frame. The BD DXE's has a preallocated buffer while
+		 * the skb ones does not. If this isn't true something is really
+		 * wierd. TODO: Recover from this situation
+		 */
+
+		wcn36xx_error("bd address may not be NULL for BD DXE");
+		return -EINVAL;
+	}
 
-	if (broadcast) {
-		/* broadcast */
-		bd->ub = 1;
-		bd->queue_id = WCN36XX_TX_B_WQ_ID;
+	wcn36xx_dbg(WCN36XX_DBG_TX,
+		    "tx skb %p len %d fc %04x sn %d %s %s",
+		    skb, skb->len, __le16_to_cpu(hdr->frame_control),
+		    IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
+		    is_low ? "low" : "high", bcast ? "bcast" : "ucast");
 
-		/* default rate for broadcast */
-		if (ieee80211_is_mgmt(hdr->frame_control))
-			bd->bd_rate = (wcn->band == IEEE80211_BAND_5GHZ) ?
-				WCN36XX_BD_RATE_CTRL :
-				WCN36XX_BD_RATE_MGMT;
-		/* No ack needed not unicast */
-		bd->ack_policy = 1;
-	} else {
-		bd->queue_id = WCN36XX_TX_U_WQ_ID;
-		/* default rate for unicast */
-		bd->ack_policy = 0;
-		if (ieee80211_is_data(hdr->frame_control))
-			bd->bd_rate = WCN36XX_BD_RATE_DATA;
-		else if (ieee80211_is_mgmt(hdr->frame_control))
-			bd->bd_rate = (wcn->band == IEEE80211_BAND_5GHZ) ?
-				WCN36XX_BD_RATE_CTRL :
-				WCN36XX_BD_RATE_MGMT;
-		else if (ieee80211_is_ctl(hdr->frame_control))
-			bd->bd_rate = WCN36XX_BD_RATE_CTRL;
-		else
-			wcn36xx_warn("frame control type unknown");
+	wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
+
+	memset(bd, 0, sizeof(*bd));
+
+	bd->dpu_rf = WCN36XX_BMU_WQ_TX;
+
+	bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
+	if (bd->tx_comp) {
+		wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested");
+		spin_lock_irqsave(&wcn->dxe_lock, flags);
+		if (wcn->tx_ack_skb) {
+			spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+			wcn36xx_warn("tx_ack_skb already set");
+			ieee80211_free_txskb(wcn->hw, skb);
+			return -EINVAL;
+		}
+
+		wcn->tx_ack_skb = skb;
+		spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+		/* Only one at a time is supported by fw. Stop the TX queues
+		 * until the ack status gets back.
+		 *
+		 * TODO: Add watchdog in case FW does not answer
+		 */
+		ieee80211_stop_queues(wcn->hw);
 	}
 
-	if (ieee80211_is_data(hdr->frame_control)) {
+	wcn36xx_fill_tx_pdu(bd, ieee80211_is_data_qos(hdr->frame_control) ?
+			    sizeof(struct ieee80211_qos_hdr) :
+			    sizeof(struct ieee80211_hdr_3addr),
+			    skb->len);
+
+	/* Data frames served first*/
+	if (is_low) {
+		bd->bd_rate = WCN36XX_BD_RATE_DATA;
 		bd->dpu_sign = wcn->current_vif->ucast_dpu_signature;
 		bd->queue_id = 0;
 		bd->sta_index = wcn->current_vif->sta_index;
@@ -126,15 +159,45 @@ void wcn36xx_fill_tx_bd(struct wcn36xx *wcn, struct wcn36xx_tx_bd *bd,
 		if (ieee80211_is_nullfunc(hdr->frame_control) ||
 		    (sta_priv && !sta_priv->is_data_encrypted))
 			bd->dpu_ne = 1;
-
+		if (bcast) {
+			bd->ub = 1;
+			bd->ack_policy = 1;
+		}
 	} else {
 		bd->sta_index = wcn->current_vif->self_sta_index;
 		bd->dpu_desc_idx = wcn->current_vif->self_dpu_desc_index;
 		bd->dpu_ne = 1;
-	}
 
-	bd->tx_comp = tx_compl;
+		/* default rate for unicast */
+		if (ieee80211_is_mgmt(hdr->frame_control))
+			bd->bd_rate = (wcn->band == IEEE80211_BAND_5GHZ) ?
+				WCN36XX_BD_RATE_CTRL :
+				WCN36XX_BD_RATE_MGMT;
+		else if (ieee80211_is_ctl(hdr->frame_control))
+			bd->bd_rate = WCN36XX_BD_RATE_CTRL;
+		else
+			wcn36xx_warn("frame control type unknown");
+
+		/*
+		 * In joining state trick hardware that probe is sent as
+		 * unicast even if address is broadcast.
+		 */
+		if (wcn->is_joining &&
+		    ieee80211_is_probe_req(hdr->frame_control))
+			bcast = false;
+
+		if (bcast) {
+			/* broadcast */
+			bd->ub = 1;
+			/* No ack needed not unicast */
+			bd->ack_policy = 1;
+			bd->queue_id = WCN36XX_TX_B_WQ_ID;
+		} else
+			bd->queue_id = WCN36XX_TX_U_WQ_ID;
+	}
 
 	buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
 	bd->tx_bd_sign = 0xbdbdbdbd;
+
+	return wcn36xx_dxe_tx_frame(wcn, skb, is_low);
 }
diff --git a/txrx.h b/txrx.h
index ffcbd17..bf2994f 100644
--- a/txrx.h
+++ b/txrx.h
@@ -17,6 +17,7 @@
 #ifndef _TXRX_H_
 #define _TXRX_H_
 
+#include <linux/etherdevice.h>
 #include "wcn36xx.h"
 
 /* TODO describe all properties */
@@ -147,9 +148,16 @@ struct wcn36xx_tx_bd {
 	u32	header_cks:16;
 	u32	reserved7:6;*/
 };
+
+struct wcn_sta;
+struct wcn36xx;
+
 int  wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
-void wcn36xx_prepare_tx_bd(struct wcn36xx_tx_bd *bd, u32 len, u32 header_len);
-void wcn36xx_fill_tx_bd(struct wcn36xx *wcn, struct wcn36xx_tx_bd *bd,
-			u8 broadcast, struct ieee80211_hdr *hdr,
-			bool tx_compl, struct wcn_sta *sta_priv);
+void wcn36xx_fill_tx_pdu(struct wcn36xx_tx_bd *bd,
+			 u32 mpdu_header_len,
+			 u32 len);
+int wcn36xx_start_tx(struct wcn36xx *wcn,
+		     struct wcn_sta *sta_priv,
+		     struct sk_buff *skb);
+
 #endif	/* _TXRX_H_ */
diff --git a/wcn36xx.h b/wcn36xx.h
index 78a0f7b..89ffc2f 100644
--- a/wcn36xx.h
+++ b/wcn36xx.h
@@ -27,6 +27,7 @@
 
 #include "hal.h"
 #include "smd.h"
+#include "txrx.h"
 #include "dxe.h"
 
 #define DRIVER_PREFIX "wcn36xx: "
-- 
1.7.11.3




More information about the wcn36xx mailing list