rt2800usb: read TX_STA_FIFO asynchronously

Trying to fix the "TX status report missed" warnings
by reading the TX_STA_FIFO entries as quickly as possible.
The TX_STA_FIFO is too small in hardware, thus reading
it only from the workqueue is too slow and entries get lost.

Start an asynchronous read of the TX_STA_FIFO directly from
the TX URB completion callback (atomic context, thus it cannot
use the blocking rt2800_register_read()). If the async
read returns a valid FIFO entry, it is pushed into a larger
FIFO inside struct rt2x00_dev, until rt2800_txdone() picks
it up.

A .tx_dma_done callback is added to struct rt2x00lib_ops
to trigger the async read from the URB completion callback.

Signed-off-by: Johannes Stezenbach <js@sig21.net>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 0e2c006..d79c8fd 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -730,34 +730,20 @@
 	struct data_queue *queue;
 	struct queue_entry *entry;
 	u32 reg;
-	u8 pid;
-	int i;
+	u8 qid;
 
-	/*
-	 * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
-	 * at most X times and also stop processing once the TX_STA_FIFO_VALID
-	 * flag is not set anymore.
-	 *
-	 * The legacy drivers use X=TX_RING_SIZE but state in a comment
-	 * that the TX_STA_FIFO stack has a size of 16. We stick to our
-	 * tx ring size for now.
-	 */
-	for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
-		rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
-		if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
-			break;
+	while (kfifo_get(&rt2x00dev->txstatus_fifo, &reg)) {
 
-		/*
-		 * Skip this entry when it contains an invalid
-		 * queue identication number.
+		/* TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus
+		 * qid is guaranteed to be one of the TX QIDs
 		 */
-		pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
-		if (pid >= QID_RX)
+		qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
+		queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
+		if (unlikely(!queue)) {
+			WARNING(rt2x00dev, "Got TX status for an unavailable "
+					   "queue %u, dropping\n", qid);
 			continue;
-
-		queue = rt2x00queue_get_tx_queue(rt2x00dev, pid);
-		if (unlikely(!queue))
-			continue;
+		}
 
 		/*
 		 * Inside each queue, we process each entry in a chronological