|
@@ -1487,8 +1487,12 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
const struct b43_dma_ops *ops;
|
|
|
struct b43_dmaring *ring;
|
|
|
struct b43_dmadesc_meta *meta;
|
|
|
+ static const struct b43_txstatus fake; /* filled with 0 */
|
|
|
+ const struct b43_txstatus *txstat;
|
|
|
int slot, firstused;
|
|
|
bool frame_succeed;
|
|
|
+ int skip;
|
|
|
+ static u8 err_out1, err_out2;
|
|
|
|
|
|
ring = parse_cookie(dev, status->cookie, &slot);
|
|
|
if (unlikely(!ring))
|
|
@@ -1501,13 +1505,36 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
firstused = ring->current_slot - ring->used_slots + 1;
|
|
|
if (firstused < 0)
|
|
|
firstused = ring->nr_slots + firstused;
|
|
|
+
|
|
|
+ skip = 0;
|
|
|
if (unlikely(slot != firstused)) {
|
|
|
/* This possibly is a firmware bug and will result in
|
|
|
- * malfunction, memory leaks and/or stall of DMA functionality. */
|
|
|
- b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. "
|
|
|
- "Expected %d, but got %d\n",
|
|
|
- ring->index, firstused, slot);
|
|
|
- return;
|
|
|
+ * malfunction, memory leaks and/or stall of DMA functionality.
|
|
|
+ */
|
|
|
+ if (slot == next_slot(ring, next_slot(ring, firstused))) {
|
|
|
+ /* If a single header/data pair was missed, skip over
|
|
|
+ * the first two slots in an attempt to recover.
|
|
|
+ */
|
|
|
+ slot = firstused;
|
|
|
+ skip = 2;
|
|
|
+ if (!err_out1) {
|
|
|
+ /* Report the error once. */
|
|
|
+ b43dbg(dev->wl,
|
|
|
+ "Skip on DMA ring %d slot %d.\n",
|
|
|
+ ring->index, slot);
|
|
|
+ err_out1 = 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* More than a single header/data pair were missed.
|
|
|
+ * Report this error once.
|
|
|
+ */
|
|
|
+ if (!err_out2)
|
|
|
+ b43dbg(dev->wl,
|
|
|
+ "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n",
|
|
|
+ ring->index, firstused, slot);
|
|
|
+ err_out2 = 1;
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
ops = ring->ops;
|
|
@@ -1522,11 +1549,13 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
slot, firstused, ring->index);
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
if (meta->skb) {
|
|
|
struct b43_private_tx_info *priv_info =
|
|
|
- b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb));
|
|
|
+ b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb));
|
|
|
|
|
|
- unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);
|
|
|
+ unmap_descbuffer(ring, meta->dmaaddr,
|
|
|
+ meta->skb->len, 1);
|
|
|
kfree(priv_info->bouncebuffer);
|
|
|
priv_info->bouncebuffer = NULL;
|
|
|
} else {
|
|
@@ -1538,8 +1567,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
struct ieee80211_tx_info *info;
|
|
|
|
|
|
if (unlikely(!meta->skb)) {
|
|
|
- /* This is a scatter-gather fragment of a frame, so
|
|
|
- * the skb pointer must not be NULL. */
|
|
|
+ /* This is a scatter-gather fragment of a frame,
|
|
|
+ * so the skb pointer must not be NULL.
|
|
|
+ */
|
|
|
b43dbg(dev->wl, "TX status unexpected NULL skb "
|
|
|
"at slot %d (first=%d) on ring %d\n",
|
|
|
slot, firstused, ring->index);
|
|
@@ -1550,9 +1580,18 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
|
|
|
/*
|
|
|
* Call back to inform the ieee80211 subsystem about
|
|
|
- * the status of the transmission.
|
|
|
+ * the status of the transmission. When skipping over
|
|
|
+ * a missed TX status report, use a status structure
|
|
|
+ * filled with zeros to indicate that the frame was not
|
|
|
+ * sent (frame_count 0) and not acknowledged
|
|
|
*/
|
|
|
- frame_succeed = b43_fill_txstatus_report(dev, info, status);
|
|
|
+ if (unlikely(skip))
|
|
|
+ txstat = &fake;
|
|
|
+ else
|
|
|
+ txstat = status;
|
|
|
+
|
|
|
+ frame_succeed = b43_fill_txstatus_report(dev, info,
|
|
|
+ txstat);
|
|
|
#ifdef CONFIG_B43_DEBUG
|
|
|
if (frame_succeed)
|
|
|
ring->nr_succeed_tx_packets++;
|
|
@@ -1580,12 +1619,14 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
|
|
|
/* Everything unmapped and free'd. So it's not used anymore. */
|
|
|
ring->used_slots--;
|
|
|
|
|
|
- if (meta->is_last_fragment) {
|
|
|
+ if (meta->is_last_fragment && !skip) {
|
|
|
/* This is the last scatter-gather
|
|
|
* fragment of the frame. We are done. */
|
|
|
break;
|
|
|
}
|
|
|
slot = next_slot(ring, slot);
|
|
|
+ if (skip > 0)
|
|
|
+ --skip;
|
|
|
}
|
|
|
if (ring->stopped) {
|
|
|
B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME);
|