[PATCH] Add IEEE80211_TX_CTL_REQ_TX_STATUS support

Pontus Fuchs pontus.fuchs at gmail.com
Tue Jun 4 06:52:57 EDT 2013


As there seems to be no packet id in the status indication message
from the FW, I stop the queues in order to have only one frame with
IEEE80211_TX_CTL_REQ_TX_STATUS submitted to the FW.

Signed-off-by: Pontus Fuchs <pontus.fuchs at gmail.com>
---
 dxe.c     | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 dxe.h     |  3 ++-
 main.c    | 16 ++++++++++++---
 smd.c     | 17 ++++++++++++++++
 txrx.c    |  3 ++-
 txrx.h    |  2 +-
 wcn36xx.h |  6 ++++++
 7 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/dxe.c b/dxe.c
index b5940f0..db91ae3 100644
--- a/dxe.c
+++ b/dxe.c
@@ -269,6 +269,32 @@ static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn,
 	}
 }
 
+void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
+{
+	struct ieee80211_tx_info *info;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wcn->dxe_lock, flags);
+	skb = wcn->tx_ack_skb;
+	wcn->tx_ack_skb = NULL;
+	spin_unlock_irqrestore(&wcn->dxe_lock, flags);
+
+	if (!skb) {
+		wcn36xx_error("Spurious TX complete indication");
+		return;
+	}
+
+	info = IEEE80211_SKB_CB(skb);
+	if (status == 1) {
+		wcn36xx_dbg(WCN36XX_DBG_DXE, "Got positive ack status");
+		info->flags |= IEEE80211_TX_STAT_ACK;
+	} else
+		wcn36xx_dbg(WCN36XX_DBG_DXE, "Got negative ack status");
+	ieee80211_tx_status_irqsafe(wcn->hw, skb);
+	ieee80211_wake_queues(wcn->hw);
+}
+
 static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
 {
 	struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl;
@@ -280,8 +306,11 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
 			dma_unmap_single(NULL, ctl->desc->src_addr_l,
 					 ctl->skb->len, DMA_TO_DEVICE);
 			info = IEEE80211_SKB_CB(ctl->skb);
-			ieee80211_tx_info_clear_status(info);
-			ieee80211_tx_status_irqsafe(wcn->hw, ctl->skb);
+			if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
+				/* Keep frame until TX status comes */
+				ieee80211_tx_info_clear_status(info);
+				ieee80211_tx_status_irqsafe(wcn->hw, ctl->skb);
+			}
 			ctl->skb = NULL;
 		}
 		ctl = ctl->next;
@@ -478,14 +507,33 @@ int wcn36xx_dxe_tx(struct wcn36xx *wcn,
 		   struct sk_buff *skb,
 		   u8 broadcast,
 		   bool is_high,
-		   u32 header_len)
+		   u32 header_len, bool tx_ack)
 {
 	struct wcn36xx_dxe_ctl *ctl = NULL;
 	struct wcn36xx_dxe_desc *desc = NULL;
 	struct wcn36xx_dxe_ch *ch = NULL;
+	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_error("tx_ack_skb already set");
+			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.
+		 */
+		ieee80211_stop_queues(wcn->hw);
+	}
+
 	ctl = ch->head_blk_ctl;
 	ctl->skb = NULL;
 	desc = ctl->desc;
@@ -503,10 +551,9 @@ int wcn36xx_dxe_tx(struct wcn36xx *wcn,
 	wcn36xx_prepare_tx_bd(ctl->bd_cpu_addr, skb->len, header_len);
 	if (!is_high && WCN36XX_BSS_KEY == wcn->en_state) {
 		wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE Encription enabled");
-		wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 0);
-	} else {
-		wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 1);
-	}
+		wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 0, tx_ack);
+	} else
+		wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 1, tx_ack);
 
 	ctl = ch->head_blk_ctl;
 	desc = ctl->desc;
@@ -682,6 +729,13 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
 {
 	free_irq(wcn->tx_irq, wcn);
 	free_irq(wcn->rx_irq, wcn);
+
+	if (wcn->tx_ack_skb) {
+		ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
+		ieee80211_wake_queues(wcn->hw);
+		wcn->tx_ack_skb = NULL;
+	}
+
 	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch);
 	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch);
 }
diff --git a/dxe.h b/dxe.h
index 4eba4e7..5a40330 100644
--- a/dxe.h
+++ b/dxe.h
@@ -234,5 +234,6 @@ int wcn36xx_dxe_tx(struct wcn36xx *wcn,
 		   struct sk_buff *skb,
 		   u8 broadcast,
 		   bool is_high,
-		   u32 header_len);
+		   u32 header_len, bool tx_compl);
+void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
 #endif	/* _DXE_H_ */
diff --git a/main.c b/main.c
index 70ad27b..df91439 100644
--- a/main.c
+++ b/main.c
@@ -176,11 +176,20 @@ 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;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	bool high, bcast, tx_compl;
 	u32 header_len = 0;
 	struct wcn36xx *wcn = hw->priv;
 
+	if (ieee80211_is_nullfunc(hdr->frame_control) &&
+	    info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
+		/* mac80211 bug? workaround */
+		info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+	}
+	tx_compl = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
+
 	mgmt = (struct ieee80211_mgmt *)skb->data;
 
 	high = !(ieee80211_is_data(mgmt->frame_control) ||
@@ -206,7 +215,7 @@ static void wcn36xx_tx(struct ieee80211_hw *hw,
 	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);
+	wcn36xx_dxe_tx(hw->priv, skb, bcast, high, header_len, tx_compl);
 }
 
 static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -661,7 +670,8 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
 	};
 
 	wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM |
-		IEEE80211_HW_HAS_RATE_CONTROL;
+		IEEE80211_HW_HAS_RATE_CONTROL |
+		IEEE80211_HW_REPORTS_TX_ACK_STATUS;
 
 	wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 		BIT(NL80211_IFTYPE_AP) |
diff --git a/smd.c b/smd.c
index d46f5b2..397219e 100644
--- a/smd.c
+++ b/smd.c
@@ -1012,6 +1012,20 @@ static void wcn36xx_smd_notify(void *data, unsigned event)
 		break;
 	}
 }
+
+static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len)
+{
+	struct tx_compl_ind_msg *rsp = (struct tx_compl_ind_msg *)buf;
+
+	if (len != sizeof(*rsp)) {
+		wcn36xx_error("Bad TX complete indication");
+		return -EIO;
+	}
+
+	wcn36xx_dxe_tx_ack_ind(wcn, rsp->status);
+	return 0;
+}
+
 static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
 {
 	struct wcn36xx_hal_msg_header *msg_header = buf;
@@ -1058,6 +1072,9 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
 	case WCN36XX_HAL_CH_SWITCH_RSP:
 		wcn36xx_smd_switch_channel_rsp(buf, len);
 		break;
+	case WCN36XX_HAL_OTA_TX_COMPL_IND:
+		wcn36xx_smd_tx_compl_ind(wcn, buf, len);
+		break;
 	default:
 		wcn36xx_error("SMD_EVENT (%d) not supported", msg_header->msg_type);
 	}
diff --git a/txrx.c b/txrx.c
index c52cc22..27e49a6 100644
--- a/txrx.c
+++ b/txrx.c
@@ -84,7 +84,7 @@ void wcn36xx_prepare_tx_bd(struct wcn36xx_tx_bd *bd, u32 len, u32 header_len)
 	bd->pdu.mpdu_len = len;
 }
 void wcn36xx_fill_tx_bd(struct wcn36xx *wcn, struct wcn36xx_tx_bd *bd,
-			u8 broadcast, u8 encrypt)
+			u8 broadcast, u8 encrypt, bool tx_compl)
 {
 	bd->dpu_rf = WCN36XX_BMU_WQ_TX;
 	bd->pdu.tid   = WCN36XX_TID;
@@ -111,6 +111,7 @@ void wcn36xx_fill_tx_bd(struct wcn36xx *wcn, struct wcn36xx_tx_bd *bd,
 	bd->dpu_desc_idx = wcn->current_vif->dpu_desc_index;
 
 	bd->dpu_ne = encrypt;
+	bd->tx_comp = tx_compl;
 
 	buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
 	bd->tx_bd_sign = 0xbdbdbdbd;
diff --git a/txrx.h b/txrx.h
index c2b9a0f..7ea9869 100644
--- a/txrx.h
+++ b/txrx.h
@@ -146,5 +146,5 @@ struct wcn36xx_tx_bd {
 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, u8 encrypt);
+			u8 broadcast, u8 encrypt, bool tx_compl);
 #endif	/* _TXRX_H_ */
diff --git a/wcn36xx.h b/wcn36xx.h
index 0b57baa..8144dd9 100644
--- a/wcn36xx.h
+++ b/wcn36xx.h
@@ -20,6 +20,7 @@
 #include <linux/completion.h>
 #include <linux/printk.h>
 #include <linux/firmware.h>
+#include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <mach/msm_smd.h>
 #include <net/mac80211.h>
@@ -149,9 +150,14 @@ struct wcn36xx {
 	struct wcn36xx_dxe_ch	dxe_rx_l_ch;	/* RX low */
 	struct wcn36xx_dxe_ch	dxe_rx_h_ch;	/* RX high */
 
+	/* DXE lock */
+	spinlock_t	dxe_lock;
+
 	/* Memory pools */
 	struct wcn36xx_dxe_mem_pool mgmt_mem_pool;
 	struct wcn36xx_dxe_mem_pool data_mem_pool;
+
+	struct sk_buff		*tx_ack_skb;
 };
 
 #endif	/* _WCN36XX_H_ */
-- 
1.8.1.2




More information about the wcn36xx mailing list