[PATCH V3] Add IEEE80211_TX_CTL_REQ_TX_STATUS support
Pontus Fuchs
pontus.fuchs at gmail.com
Fri Jun 7 04:03:59 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 | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
dxe.h | 3 ++-
hal.h | 2 +-
main.c | 9 ++++++---
smd.c | 18 +++++++++++++++++
txrx.c | 4 +++-
txrx.h | 3 ++-
wcn36xx.h | 6 ++++++
8 files changed, 100 insertions(+), 12 deletions(-)
diff --git a/dxe.c b/dxe.c
index 0b3c444..a705be1 100644
--- a/dxe.c
+++ b/dxe.c
@@ -270,6 +270,30 @@ 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_warn("Spurious TX complete indication");
+ return;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ if (status == 1)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d", 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;
@@ -281,8 +305,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;
@@ -479,15 +506,37 @@ 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;
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);
+ }
+
ctl = ch->head_blk_ctl;
ctl->skb = NULL;
desc = ctl->desc;
@@ -505,9 +554,11 @@ 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, hdr);
+ wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 0, hdr,
+ tx_ack);
} else {
- wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 1, hdr);
+ wcn36xx_fill_tx_bd(wcn, ctl->bd_cpu_addr, broadcast, 1, hdr,
+ tx_ack);
}
ctl = ch->head_blk_ctl;
@@ -684,6 +735,12 @@ 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);
+ 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/hal.h b/hal.h
index 3c4e2cb..087914d 100644
--- a/hal.h
+++ b/hal.h
@@ -3632,7 +3632,7 @@ struct coex_ind_msg {
u32 data[WLAN_COEX_IND_DATA_SIZE];
};
-struct tx_compl_ind_msg {
+struct wcn36xx_hal_tx_compl_ind_msg {
struct wcn36xx_hal_msg_header header;
/* Tx Complete Indication Success or Failure */
diff --git a/main.c b/main.c
index 70ad27b..2c2fb6f 100644
--- a/main.c
+++ b/main.c
@@ -176,11 +176,13 @@ 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;
+ bool high, bcast, tx_compl;
u32 header_len = 0;
struct wcn36xx *wcn = hw->priv;
+ 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 +208,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 +663,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..c67a057 100644
--- a/smd.c
+++ b/smd.c
@@ -1012,6 +1012,21 @@ 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 wcn36xx_hal_tx_compl_ind_msg *rsp = buf;
+
+ if (len != sizeof(*rsp)) {
+ wcn36xx_warn("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 +1073,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 637d61d..9210ae5 100644
--- a/txrx.c
+++ b/txrx.c
@@ -84,7 +84,8 @@ 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, struct ieee80211_hdr *hdr)
+ u8 broadcast, u8 encrypt, struct ieee80211_hdr *hdr,
+ bool tx_compl)
{
bd->dpu_rf = WCN36XX_BMU_WQ_TX;
bd->pdu.tid = WCN36XX_TID;
@@ -118,6 +119,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 e5d1c75..648c210 100644
--- a/txrx.h
+++ b/txrx.h
@@ -151,5 +151,6 @@ 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, struct ieee80211_hdr *hdr);
+ u8 broadcast, u8 encrypt, struct ieee80211_hdr *hdr,
+ bool tx_compl);
#endif /* _TXRX_H_ */
diff --git a/wcn36xx.h b/wcn36xx.h
index 0b57baa..28c86ea 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 */
+ /* For synchronization of DXE resources from BH, IRQ and WQ contexts */
+ 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