@@ -98,6 +98,22 @@ static void rt2800usb_stop_queue(struct data_queue *queue)
}
}
+/*
+ * test if there is an entry in any TX queue for which DMA is done
+ * but the TX status has not been returned yet
+ */
+static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue;
+
+ tx_queue_for_each(rt2x00dev, queue) {
+ if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) !=
+ rt2x00queue_get_entry(queue, Q_INDEX_DONE))
+ return true;
+ }
+ return false;
+}
+
static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
int urb_status, u32 tx_status)
{
@@ -115,8 +131,11 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
} else
rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
rt2800usb_tx_sta_fifo_read_completed);
- } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo))
+ } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) {
queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
+ } else if (rt2800usb_txstatus_pending(rt2x00dev)) {
+ mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20));
+ }
}
static void rt2800usb_tx_dma_done(struct queue_entry *entry)
@@ -127,6 +146,14 @@ static void rt2800usb_tx_dma_done(struct queue_entry *entry)
rt2800usb_tx_sta_fifo_read_completed);
}
+static void rt2800usb_tx_sta_fifo_timeout(unsigned long data)
+{
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+
+ rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
+ rt2800usb_tx_sta_fifo_read_completed);
+}
+
/*
* Firmware functions
*/
@@ -459,6 +486,14 @@ static void rt2800usb_work_txdone(struct work_struct *work)
break;
}
}
+
+ /*
+ * The hw may delay sending the packet after DMA complete
+ * if the medium is busy, thus the TX_STA_FIFO entry is
+ * also delayed -> use a timer to retrieve it.
+ */
+ if (rt2800usb_txstatus_pending(rt2x00dev))
+ mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20));
}
/*
@@ -599,6 +634,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags);
__set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags);
+ setup_timer(&rt2x00dev->txstatus_timer,
+ rt2800usb_tx_sta_fifo_timeout,
+ (unsigned long) rt2x00dev);
+
/*
* Set the rssi offset.
*/
@@ -37,6 +37,7 @@
#include <linux/etherdevice.h>
#include <linux/input-polldev.h>
#include <linux/kfifo.h>
+#include <linux/timer.h>
#include <net/mac80211.h>
@@ -924,6 +925,11 @@ struct rt2x00_dev {
DECLARE_KFIFO_PTR(txstatus_fifo, u32);
/*
+ * Timer to ensure tx status reports are read (rt2800usb).
+ */
+ struct timer_list txstatus_timer;
+
+ /*
* Tasklet for processing tx status reports (rt2800pci).
*/
struct tasklet_struct txstatus_tasklet;
@@ -1071,6 +1071,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
/*
* Stop all work.
*/
+ del_timer_sync(&rt2x00dev->txstatus_timer);
cancel_work_sync(&rt2x00dev->intf_work);
if (rt2x00_is_usb(rt2x00dev)) {
cancel_work_sync(&rt2x00dev->rxdone_work);
@@ -280,7 +280,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
* Schedule the delayed work for reading the TX status
* from the device.
*/
- queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
+ if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) ||
+ !kfifo_is_empty(&rt2x00dev->txstatus_fifo))
+ queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
}
static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data)
@@ -816,6 +818,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone);
INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
+ init_timer(&rt2x00dev->txstatus_timer);
retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval)