Commit fdf21cc3 authored by Loic Poulain's avatar Loic Poulain Committed by Kalle Valo

wcn36xx: Add TX ack support

The controller is capable of reporting TX indication which can be used
to report TX ack when IEEE80211_TX_CTL_REQ_TX_STATUS is set.
The support was only partially implemented.

The firmware can be configured for reporting event when a packet is
acked, without specifying which packet though. In order to send a
packet flagged with TX status callback, we need to stop the queue,
submit the packet and wait for the firmware ack event. Then the queue
can be restarted and mac80211 status callback called.

In case the packet is not acked, no ack event will be received,
therefore a timeout mechanism is introduced to restart the queue
and call the status cb in case no event is received after a 100ms.
Signed-off-by: default avatarLoic Poulain <loic.poulain@linaro.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1595586052-16081-3-git-send-email-loic.poulain@linaro.org
parent ffe835aa
...@@ -334,6 +334,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) ...@@ -334,6 +334,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
spin_lock_irqsave(&wcn->dxe_lock, flags); spin_lock_irqsave(&wcn->dxe_lock, flags);
skb = wcn->tx_ack_skb; skb = wcn->tx_ack_skb;
wcn->tx_ack_skb = NULL; wcn->tx_ack_skb = NULL;
del_timer(&wcn->tx_ack_timer);
spin_unlock_irqrestore(&wcn->dxe_lock, flags); spin_unlock_irqrestore(&wcn->dxe_lock, flags);
if (!skb) { if (!skb) {
...@@ -345,6 +346,8 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) ...@@ -345,6 +346,8 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
if (status == 1) if (status == 1)
info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_ACK;
else
info->flags &= ~IEEE80211_TX_STAT_ACK;
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status);
...@@ -352,6 +355,32 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) ...@@ -352,6 +355,32 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
ieee80211_wake_queues(wcn->hw); ieee80211_wake_queues(wcn->hw);
} }
static void wcn36xx_dxe_tx_timer(struct timer_list *t)
{
struct wcn36xx *wcn = from_timer(wcn, t, tx_ack_timer);
struct ieee80211_tx_info *info;
unsigned long flags;
struct sk_buff *skb;
/* TX Timeout */
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX timeout\n");
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)
return;
info = IEEE80211_SKB_CB(skb);
info->flags &= ~IEEE80211_TX_STAT_ACK;
info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED;
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) static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
{ {
struct wcn36xx_dxe_ctl *ctl; struct wcn36xx_dxe_ctl *ctl;
...@@ -397,6 +426,7 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) ...@@ -397,6 +426,7 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
{ {
struct wcn36xx *wcn = (struct wcn36xx *)dev; struct wcn36xx *wcn = (struct wcn36xx *)dev;
int int_src, int_reason; int int_src, int_reason;
bool transmitted = false;
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
...@@ -434,8 +464,10 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) ...@@ -434,8 +464,10 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
int_reason); int_reason);
if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
WCN36XX_CH_STAT_INT_ED_MASK)) WCN36XX_CH_STAT_INT_ED_MASK)) {
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
transmitted = true;
}
} }
if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
...@@ -473,9 +505,27 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) ...@@ -473,9 +505,27 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
int_reason); int_reason);
if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
WCN36XX_CH_STAT_INT_ED_MASK)) WCN36XX_CH_STAT_INT_ED_MASK)) {
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
transmitted = true;
} }
}
spin_lock(&wcn->dxe_lock);
if (wcn->tx_ack_skb && transmitted) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(wcn->tx_ack_skb);
/* TX complete, no need to wait for 802.11 ack indication */
if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS &&
info->flags & IEEE80211_TX_CTL_NO_ACK) {
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
del_timer(&wcn->tx_ack_timer);
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
wcn->tx_ack_skb = NULL;
ieee80211_wake_queues(wcn->hw);
}
}
spin_unlock(&wcn->dxe_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -916,6 +966,8 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) ...@@ -916,6 +966,8 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
if (ret < 0) if (ret < 0)
goto out_err_irq; goto out_err_irq;
timer_setup(&wcn->tx_ack_timer, wcn36xx_dxe_tx_timer, 0);
return 0; return 0;
out_err_irq: out_err_irq:
...@@ -934,6 +986,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn) ...@@ -934,6 +986,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
{ {
free_irq(wcn->tx_irq, wcn); free_irq(wcn->tx_irq, wcn);
free_irq(wcn->rx_irq, wcn); free_irq(wcn->rx_irq, wcn);
del_timer(&wcn->tx_ack_timer);
if (wcn->tx_ack_skb) { if (wcn->tx_ack_skb) {
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
......
...@@ -1175,6 +1175,7 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ...@@ -1175,6 +1175,7 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
ieee80211_hw_set(wcn->hw, REPORTS_TX_ACK_STATUS);
wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_AP) |
......
...@@ -298,9 +298,10 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, ...@@ -298,9 +298,10 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
bd->dpu_sign = __vif_priv->self_ucast_dpu_sign; bd->dpu_sign = __vif_priv->self_ucast_dpu_sign;
} }
if (ieee80211_is_nullfunc(hdr->frame_control) || if (ieee80211_is_any_nullfunc(hdr->frame_control) ||
(sta_priv && !sta_priv->is_data_encrypted)) (sta_priv && !sta_priv->is_data_encrypted)) {
bd->dpu_ne = 1; bd->dpu_ne = 1;
}
if (bcast) { if (bcast) {
bd->ub = 1; bd->ub = 1;
...@@ -394,9 +395,9 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, ...@@ -394,9 +395,9 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
bd.dpu_rf = WCN36XX_BMU_WQ_TX; bd.dpu_rf = WCN36XX_BMU_WQ_TX;
bd.tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS); if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
if (bd.tx_comp) {
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
spin_lock_irqsave(&wcn->dxe_lock, flags); spin_lock_irqsave(&wcn->dxe_lock, flags);
if (wcn->tx_ack_skb) { if (wcn->tx_ack_skb) {
spin_unlock_irqrestore(&wcn->dxe_lock, flags); spin_unlock_irqrestore(&wcn->dxe_lock, flags);
...@@ -409,10 +410,15 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, ...@@ -409,10 +410,15 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
/* Only one at a time is supported by fw. Stop the TX queues /* Only one at a time is supported by fw. Stop the TX queues
* until the ack status gets back. * until the ack status gets back.
*
* TODO: Add watchdog in case FW does not answer
*/ */
ieee80211_stop_queues(wcn->hw); ieee80211_stop_queues(wcn->hw);
/* TX watchdog if no TX irq or ack indication received */
mod_timer(&wcn->tx_ack_timer, jiffies + HZ / 10);
/* Request ack indication from the firmware */
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
bd.tx_comp = 1;
} }
/* Data frames served first*/ /* Data frames served first*/
...@@ -426,7 +432,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, ...@@ -426,7 +432,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
bd.tx_bd_sign = 0xbdbdbdbd; bd.tx_bd_sign = 0xbdbdbdbd;
ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low); ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
if (ret && bd.tx_comp) { if (ret && (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
/* If the skb has not been transmitted, /* If the skb has not been transmitted,
* don't keep a reference to it. * don't keep a reference to it.
*/ */
......
...@@ -245,6 +245,7 @@ struct wcn36xx { ...@@ -245,6 +245,7 @@ struct wcn36xx {
struct wcn36xx_dxe_mem_pool data_mem_pool; struct wcn36xx_dxe_mem_pool data_mem_pool;
struct sk_buff *tx_ack_skb; struct sk_buff *tx_ack_skb;
struct timer_list tx_ack_timer;
/* RF module */ /* RF module */
unsigned rf_id; unsigned rf_id;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment