rt2x00: Add "flush" queue command

Add a new command to the queue handlers: "flush",
this moves the flush() callback from mac80211
into rt2x00queue and adds support for flushing
the RX queue as well.

Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Acked-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 558965f..313a8fa 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -780,6 +780,12 @@
 		 */
 		ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
 		break;
+	case QID_RX:
+		/*
+		 * For RX we need to kick the queue now in order to
+		 * receive frames.
+		 */
+		queue->rt2x00dev->ops->lib->kick_queue(queue);
 	default:
 		break;
 	}
@@ -823,6 +829,74 @@
 }
 EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);
 
+void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
+{
+	unsigned int i;
+	bool started;
+	bool tx_queue =
+		(queue->qid == QID_AC_BE) ||
+		(queue->qid == QID_AC_BK) ||
+		(queue->qid == QID_AC_VI) ||
+		(queue->qid == QID_AC_VO);
+
+	mutex_lock(&queue->status_lock);
+
+	/*
+	 * If the queue has been started, we must stop it temporarily
+	 * to prevent any new frames to be queued on the device. If
+	 * we are not dropping the pending frames, the queue must
+	 * only be stopped in the software and not the hardware,
+	 * otherwise the queue will never become empty on its own.
+	 */
+	started = test_bit(QUEUE_STARTED, &queue->flags);
+	if (started) {
+		/*
+		 * Pause the queue
+		 */
+		rt2x00queue_pause_queue(queue);
+
+		/*
+		 * If we are not supposed to drop any pending
+		 * frames, this means we must force a start (=kick)
+		 * to the queue to make sure the hardware will
+		 * start transmitting.
+		 */
+		if (!drop && tx_queue)
+			queue->rt2x00dev->ops->lib->kick_queue(queue);
+	}
+
+	/*
+	 * Check if driver supports flushing, we can only guarentee
+	 * full support for flushing if the driver is able
+	 * to cancel all pending frames (drop = true).
+	 */
+	if (drop && queue->rt2x00dev->ops->lib->flush_queue)
+		queue->rt2x00dev->ops->lib->flush_queue(queue);
+
+	/*
+	 * When we don't want to drop any frames, or when
+	 * the driver doesn't fully flush the queue correcly,
+	 * we must wait for the queue to become empty.
+	 */
+	for (i = 0; !rt2x00queue_empty(queue) && i < 100; i++)
+		msleep(10);
+
+	/*
+	 * The queue flush has failed...
+	 */
+	if (unlikely(!rt2x00queue_empty(queue)))
+		WARNING(queue->rt2x00dev, "Queue %d failed to flush", queue->qid);
+
+	/*
+	 * Restore the queue to the previous status
+	 */
+	if (started)
+		rt2x00queue_unpause_queue(queue);
+
+	mutex_unlock(&queue->status_lock);
+}
+EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue);
+
 void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev)
 {
 	struct data_queue *queue;
@@ -857,6 +931,17 @@
 }
 EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues);
 
+void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop)
+{
+	struct data_queue *queue;
+
+	tx_queue_for_each(rt2x00dev, queue)
+		rt2x00queue_flush_queue(queue, drop);
+
+	rt2x00queue_flush_queue(rt2x00dev->rx, drop);
+}
+EXPORT_SYMBOL_GPL(rt2x00queue_flush_queues);
+
 static void rt2x00queue_reset(struct data_queue *queue)
 {
 	unsigned long irqflags;