diff options
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800usb.c | 41 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00usb.c | 5 |
4 files changed, 51 insertions, 2 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 69004b968122..20059727ef80 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c | |||
@@ -98,6 +98,22 @@ static void rt2800usb_stop_queue(struct data_queue *queue) | |||
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | /* | ||
102 | * test if there is an entry in any TX queue for which DMA is done | ||
103 | * but the TX status has not been returned yet | ||
104 | */ | ||
105 | static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) | ||
106 | { | ||
107 | struct data_queue *queue; | ||
108 | |||
109 | tx_queue_for_each(rt2x00dev, queue) { | ||
110 | if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) != | ||
111 | rt2x00queue_get_entry(queue, Q_INDEX_DONE)) | ||
112 | return true; | ||
113 | } | ||
114 | return false; | ||
115 | } | ||
116 | |||
101 | static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, | 117 | static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, |
102 | int urb_status, u32 tx_status) | 118 | int urb_status, u32 tx_status) |
103 | { | 119 | { |
@@ -115,8 +131,11 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, | |||
115 | } else | 131 | } else |
116 | rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, | 132 | rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, |
117 | rt2800usb_tx_sta_fifo_read_completed); | 133 | rt2800usb_tx_sta_fifo_read_completed); |
118 | } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) | 134 | } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) { |
119 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | 135 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); |
136 | } else if (rt2800usb_txstatus_pending(rt2x00dev)) { | ||
137 | mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20)); | ||
138 | } | ||
120 | } | 139 | } |
121 | 140 | ||
122 | static void rt2800usb_tx_dma_done(struct queue_entry *entry) | 141 | static void rt2800usb_tx_dma_done(struct queue_entry *entry) |
@@ -127,6 +146,14 @@ static void rt2800usb_tx_dma_done(struct queue_entry *entry) | |||
127 | rt2800usb_tx_sta_fifo_read_completed); | 146 | rt2800usb_tx_sta_fifo_read_completed); |
128 | } | 147 | } |
129 | 148 | ||
149 | static void rt2800usb_tx_sta_fifo_timeout(unsigned long data) | ||
150 | { | ||
151 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
152 | |||
153 | rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, | ||
154 | rt2800usb_tx_sta_fifo_read_completed); | ||
155 | } | ||
156 | |||
130 | /* | 157 | /* |
131 | * Firmware functions | 158 | * Firmware functions |
132 | */ | 159 | */ |
@@ -459,6 +486,14 @@ static void rt2800usb_work_txdone(struct work_struct *work) | |||
459 | break; | 486 | break; |
460 | } | 487 | } |
461 | } | 488 | } |
489 | |||
490 | /* | ||
491 | * The hw may delay sending the packet after DMA complete | ||
492 | * if the medium is busy, thus the TX_STA_FIFO entry is | ||
493 | * also delayed -> use a timer to retrieve it. | ||
494 | */ | ||
495 | if (rt2800usb_txstatus_pending(rt2x00dev)) | ||
496 | mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(20)); | ||
462 | } | 497 | } |
463 | 498 | ||
464 | /* | 499 | /* |
@@ -599,6 +634,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
599 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); | 634 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); |
600 | __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); | 635 | __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); |
601 | 636 | ||
637 | setup_timer(&rt2x00dev->txstatus_timer, | ||
638 | rt2800usb_tx_sta_fifo_timeout, | ||
639 | (unsigned long) rt2x00dev); | ||
640 | |||
602 | /* | 641 | /* |
603 | * Set the rssi offset. | 642 | * Set the rssi offset. |
604 | */ | 643 | */ |
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index e3b9b5146bf5..8f37121bb83f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/etherdevice.h> | 37 | #include <linux/etherdevice.h> |
38 | #include <linux/input-polldev.h> | 38 | #include <linux/input-polldev.h> |
39 | #include <linux/kfifo.h> | 39 | #include <linux/kfifo.h> |
40 | #include <linux/timer.h> | ||
40 | 41 | ||
41 | #include <net/mac80211.h> | 42 | #include <net/mac80211.h> |
42 | 43 | ||
@@ -924,6 +925,11 @@ struct rt2x00_dev { | |||
924 | DECLARE_KFIFO_PTR(txstatus_fifo, u32); | 925 | DECLARE_KFIFO_PTR(txstatus_fifo, u32); |
925 | 926 | ||
926 | /* | 927 | /* |
928 | * Timer to ensure tx status reports are read (rt2800usb). | ||
929 | */ | ||
930 | struct timer_list txstatus_timer; | ||
931 | |||
932 | /* | ||
927 | * Tasklet for processing tx status reports (rt2800pci). | 933 | * Tasklet for processing tx status reports (rt2800pci). |
928 | */ | 934 | */ |
929 | struct tasklet_struct txstatus_tasklet; | 935 | struct tasklet_struct txstatus_tasklet; |
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 2e490e0998da..7776d9f1f297 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c | |||
@@ -1071,6 +1071,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) | |||
1071 | /* | 1071 | /* |
1072 | * Stop all work. | 1072 | * Stop all work. |
1073 | */ | 1073 | */ |
1074 | del_timer_sync(&rt2x00dev->txstatus_timer); | ||
1074 | cancel_work_sync(&rt2x00dev->intf_work); | 1075 | cancel_work_sync(&rt2x00dev->intf_work); |
1075 | if (rt2x00_is_usb(rt2x00dev)) { | 1076 | if (rt2x00_is_usb(rt2x00dev)) { |
1076 | cancel_work_sync(&rt2x00dev->rxdone_work); | 1077 | cancel_work_sync(&rt2x00dev->rxdone_work); |
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 14736e217947..34b8a887831b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c | |||
@@ -280,7 +280,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) | |||
280 | * Schedule the delayed work for reading the TX status | 280 | * Schedule the delayed work for reading the TX status |
281 | * from the device. | 281 | * from the device. |
282 | */ | 282 | */ |
283 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | 283 | if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || |
284 | !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) | ||
285 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | ||
284 | } | 286 | } |
285 | 287 | ||
286 | static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data) | 288 | static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void* data) |
@@ -816,6 +818,7 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, | |||
816 | 818 | ||
817 | INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); | 819 | INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); |
818 | INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); | 820 | INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); |
821 | init_timer(&rt2x00dev->txstatus_timer); | ||
819 | 822 | ||
820 | retval = rt2x00usb_alloc_reg(rt2x00dev); | 823 | retval = rt2x00usb_alloc_reg(rt2x00dev); |
821 | if (retval) | 824 | if (retval) |