Commit 7a50bdfb authored by Eyal Shapira's avatar Eyal Shapira Committed by Luciano Coelho

wlcore: fix broken TX due to wrong queuing of recovery

commit 14bba17b "wl12xx: Propagate errors from wl1271_raw_write32"
breaks down TX in certain scenarios. wl1271_irq_locked() propagates
errors from wl1271_tx_work_locked however it may return -EBUSY
when the FW queues are full which is a legitimate case and not a
a real error. In this case a recovery is triggered by wl1271_irq
and this keeps repeating itself so TX is completely broken.
Fix it by avoiding propagating return values as errors even if they
aren't. Only bus (SDIO or SPI) ops failures would be progagated
as only these should trigger recovery.
Signed-off-by: default avatarEyal Shapira <eyal@wizery.com>
Signed-off-by: default avatarLuciano Coelho <coelho@ti.com>
parent 8b425e62
...@@ -352,8 +352,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, ...@@ -352,8 +352,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool is_dummy; bool is_dummy;
bool is_gem = false; bool is_gem = false;
if (!skb) if (!skb) {
wl1271_error("discarding null skb");
return -EINVAL; return -EINVAL;
}
info = IEEE80211_SKB_CB(skb); info = IEEE80211_SKB_CB(skb);
...@@ -662,6 +664,16 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids) ...@@ -662,6 +664,16 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
} }
} }
/*
* Returns failure values only in case of failed bus ops within this function.
* wl1271_prepare_tx_frame retvals won't be returned in order to avoid
* triggering recovery by higher layers when not necessary.
* In case a FW command fails within wl1271_prepare_tx_frame fails a recovery
* will be queued in wl1271_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame
* can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING
* within prepare_tx_frame code but there's nothing we should do about those
* as well.
*/
int wlcore_tx_work_locked(struct wl1271 *wl) int wlcore_tx_work_locked(struct wl1271 *wl)
{ {
struct wl12xx_vif *wlvif; struct wl12xx_vif *wlvif;
...@@ -671,9 +683,10 @@ int wlcore_tx_work_locked(struct wl1271 *wl) ...@@ -671,9 +683,10 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
bool sent_packets = false; bool sent_packets = false;
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
int ret = 0; int ret = 0;
int bus_ret = 0;
if (unlikely(wl->state == WL1271_STATE_OFF)) if (unlikely(wl->state == WL1271_STATE_OFF))
return -EIO; return 0;
while ((skb = wl1271_skb_dequeue(wl))) { while ((skb = wl1271_skb_dequeue(wl))) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
...@@ -694,9 +707,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl) ...@@ -694,9 +707,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
last_len); last_len);
ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA,
wl->aggr_buf, buf_offset, true); wl->aggr_buf, buf_offset, true);
if (ret < 0) if (bus_ret < 0)
goto out; goto out;
sent_packets = true; sent_packets = true;
...@@ -734,9 +747,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl) ...@@ -734,9 +747,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
out_ack: out_ack:
if (buf_offset) { if (buf_offset) {
buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len);
ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
buf_offset, true); buf_offset, true);
if (ret < 0) if (bus_ret < 0)
goto out; goto out;
sent_packets = true; sent_packets = true;
...@@ -747,9 +760,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl) ...@@ -747,9 +760,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
* required for older hardware revisions * required for older hardware revisions
*/ */
if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) {
ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS, bus_ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS,
wl->tx_packets_count); wl->tx_packets_count);
if (ret < 0) if (bus_ret < 0)
goto out; goto out;
} }
...@@ -758,7 +771,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl) ...@@ -758,7 +771,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
wl12xx_rearm_rx_streaming(wl, active_hlids); wl12xx_rearm_rx_streaming(wl, active_hlids);
out: out:
return ret; return bus_ret;
} }
void wl1271_tx_work(struct work_struct *work) void wl1271_tx_work(struct work_struct *work)
......
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