]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/net/wireless/rt2x00/rt2x00queue.c
rt2x00: Add "flush" queue command
[mirror_ubuntu-bionic-kernel.git] / drivers / net / wireless / rt2x00 / rt2x00queue.c
index 558965fb41b347583514dcdb4006a930811b1986..313a8faa5fa43708d2f5606b0364c8b92d3c0090 100644 (file)
@@ -780,6 +780,12 @@ void rt2x00queue_unpause_queue(struct data_queue *queue)
                 */
                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 @@ void rt2x00queue_stop_queue(struct data_queue *queue)
 }
 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 @@ void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev)
 }
 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;