diff options
author | Ivo van Doorn <ivdoorn@gmail.com> | 2010-12-13 12:35:40 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-12-13 15:23:35 -0500 |
commit | 5be65609fec2e331c7d804471be3d59089a30d98 (patch) | |
tree | 6c6967c7835d6ec3a8ce79c2c9b84bf95aefca31 /drivers/net/wireless/rt2x00/rt2x00queue.c | |
parent | 0b7fde54f94979edc67bbf86b5adba702ebfefe8 (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00queue.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00queue.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 558965fb41b3..313a8faa5fa4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -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; |