aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00
diff options
context:
space:
mode:
authorIvo van Doorn <ivdoorn@gmail.com>2010-08-23 13:54:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-25 14:34:54 -0400
commit5eb7efe8a4807d98a277280e1317e5094eedfb6b (patch)
tree124a1a599ff718cfd84bb441202fe251f65bda19 /drivers/net/wireless/rt2x00
parentee1e755f84dfd5d482076c642fac830aafdc482b (diff)
rt2x00: Move direct access to queue->entries to rt2x00queue.c
All access to queue->entries through the Q_INDEX/Q_INDEX_DONE variables must be done using spinlock protection. It is best to manage this completely from rt2x00queue.c. For safely looping through all entries in the queue, the function rt2x00queue_for_each_entry is added which will walk from from a index range in a safe manner. This also fixes rt2x00usb which walked the entries list from 0 to length to kill each entry (killing entries must be done from Q_INDEX_DONE to Q_INDEX to enforce TX status reporting to occur in the correct order. Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Acked-by: Gertjan van Wingerde <gwingerde@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00')
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c45
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h16
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c75
4 files changed, 83 insertions, 57 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index af1c6914d7b4..a5e58705b49b 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -629,7 +629,7 @@ static void rt2800pci_write_tx_desc(struct queue_entry *entry,
629static void rt2800pci_kick_tx_queue(struct data_queue *queue) 629static void rt2800pci_kick_tx_queue(struct data_queue *queue)
630{ 630{
631 struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 631 struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
632 unsigned int idx = queue->index[Q_INDEX]; 632 struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
633 unsigned int qidx = 0; 633 unsigned int qidx = 0;
634 634
635 if (queue->qid == QID_MGMT) 635 if (queue->qid == QID_MGMT)
@@ -637,7 +637,7 @@ static void rt2800pci_kick_tx_queue(struct data_queue *queue)
637 else 637 else
638 qidx = queue->qid; 638 qidx = queue->qid;
639 639
640 rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), idx); 640 rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), entry->entry_idx);
641} 641}
642 642
643static void rt2800pci_kill_tx_queue(struct data_queue *queue) 643static void rt2800pci_kill_tx_queue(struct data_queue *queue)
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 189eaf72967e..7fb9b6179af1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -625,6 +625,51 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
625 return 0; 625 return 0;
626} 626}
627 627
628void rt2x00queue_for_each_entry(struct data_queue *queue,
629 enum queue_index start,
630 enum queue_index end,
631 void (*fn)(struct queue_entry *entry))
632{
633 unsigned long irqflags;
634 unsigned int index_start;
635 unsigned int index_end;
636 unsigned int i;
637
638 if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) {
639 ERROR(queue->rt2x00dev,
640 "Entry requested from invalid index range (%d - %d)\n",
641 start, end);
642 return;
643 }
644
645 /*
646 * Only protect the range we are going to loop over,
647 * if during our loop a extra entry is set to pending
648 * it should not be kicked during this run, since it
649 * is part of another TX operation.
650 */
651 spin_lock_irqsave(&queue->lock, irqflags);
652 index_start = queue->index[start];
653 index_end = queue->index[end];
654 spin_unlock_irqrestore(&queue->lock, irqflags);
655
656 /*
657 * Start from the TX done pointer, this guarentees that we will
658 * send out all frames in the correct order.
659 */
660 if (index_start < index_end) {
661 for (i = index_start; i < index_end; i++)
662 fn(&queue->entries[i]);
663 } else {
664 for (i = index_start; i < queue->limit; i++)
665 fn(&queue->entries[i]);
666
667 for (i = 0; i < index_end; i++)
668 fn(&queue->entries[i]);
669 }
670}
671EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry);
672
628struct data_queue *rt2x00queue_get_queue(struct rt2x00_dev *rt2x00dev, 673struct data_queue *rt2x00queue_get_queue(struct rt2x00_dev *rt2x00dev,
629 const enum data_queue_qid queue) 674 const enum data_queue_qid queue)
630{ 675{
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 2d3bf843735f..46a3be1d82b9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -571,6 +571,22 @@ struct data_queue_desc {
571 queue_loop(__entry, (__dev)->tx, queue_end(__dev)) 571 queue_loop(__entry, (__dev)->tx, queue_end(__dev))
572 572
573/** 573/**
574 * rt2x00queue_for_each_entry - Loop through all entries in the queue
575 * @queue: Pointer to @data_queue
576 * @start: &enum queue_index Pointer to start index
577 * @end: &enum queue_index Pointer to end index
578 * @fn: The function to call for each &struct queue_entry
579 *
580 * This will walk through all entries in the queue, in chronological
581 * order. This means it will start at the current @start pointer
582 * and will walk through the queue until it reaches the @end pointer.
583 */
584void rt2x00queue_for_each_entry(struct data_queue *queue,
585 enum queue_index start,
586 enum queue_index end,
587 void (*fn)(struct queue_entry *entry));
588
589/**
574 * rt2x00queue_empty - Check if the queue is empty. 590 * rt2x00queue_empty - Check if the queue is empty.
575 * @queue: Queue to check if empty. 591 * @queue: Queue to check if empty.
576 */ 592 */
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 1d2eb461329f..6cc7aa418d87 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -225,7 +225,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
225 ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work); 225 ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
226} 226}
227 227
228static inline void rt2x00usb_kick_tx_entry(struct queue_entry *entry) 228static void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
229{ 229{
230 struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 230 struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
231 struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); 231 struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
@@ -252,69 +252,34 @@ static inline void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
252 252
253void rt2x00usb_kick_tx_queue(struct data_queue *queue) 253void rt2x00usb_kick_tx_queue(struct data_queue *queue)
254{ 254{
255 unsigned long irqflags; 255 rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
256 unsigned int index; 256 rt2x00usb_kick_tx_entry);
257 unsigned int index_done;
258 unsigned int i;
259
260 /*
261 * Only protect the range we are going to loop over,
262 * if during our loop a extra entry is set to pending
263 * it should not be kicked during this run, since it
264 * is part of another TX operation.
265 */
266 spin_lock_irqsave(&queue->lock, irqflags);
267 index = queue->index[Q_INDEX];
268 index_done = queue->index[Q_INDEX_DONE];
269 spin_unlock_irqrestore(&queue->lock, irqflags);
270
271 /*
272 * Start from the TX done pointer, this guarentees that we will
273 * send out all frames in the correct order.
274 */
275 if (index_done < index) {
276 for (i = index_done; i < index; i++)
277 rt2x00usb_kick_tx_entry(&queue->entries[i]);
278 } else {
279 for (i = index_done; i < queue->limit; i++)
280 rt2x00usb_kick_tx_entry(&queue->entries[i]);
281
282 for (i = 0; i < index; i++)
283 rt2x00usb_kick_tx_entry(&queue->entries[i]);
284 }
285} 257}
286EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue); 258EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue);
287 259
288void rt2x00usb_kill_tx_queue(struct data_queue *queue) 260static void rt2x00usb_kill_tx_entry(struct queue_entry *entry)
289{ 261{
290 struct queue_entry_priv_usb *entry_priv; 262 struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
291 struct queue_entry_priv_usb_bcn *bcn_priv; 263 struct queue_entry_priv_usb *entry_priv = entry->priv_data;
292 unsigned int i; 264 struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
293 bool kill_guard;
294 265
295 /* 266 if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
296 * When killing the beacon queue, we must also kill 267 return;
297 * the beacon guard byte. 268
298 */ 269 usb_kill_urb(entry_priv->urb);
299 kill_guard =
300 (queue->qid == QID_BEACON) &&
301 (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &queue->rt2x00dev->flags));
302 270
303 /* 271 /*
304 * Cancel all entries. 272 * Kill guardian urb (if required by driver).
305 */ 273 */
306 for (i = 0; i < queue->limit; i++) { 274 if ((entry->queue->qid == QID_BEACON) &&
307 entry_priv = queue->entries[i].priv_data; 275 (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
308 usb_kill_urb(entry_priv->urb); 276 usb_kill_urb(bcn_priv->guardian_urb);
277}
309 278
310 /* 279void rt2x00usb_kill_tx_queue(struct data_queue *queue)
311 * Kill guardian urb (if required by driver). 280{
312 */ 281 rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
313 if (kill_guard) { 282 rt2x00usb_kill_tx_entry);
314 bcn_priv = queue->entries[i].priv_data;
315 usb_kill_urb(bcn_priv->guardian_urb);
316 }
317 }
318} 283}
319EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); 284EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
320 285