diff options
author | Johannes Stezenbach <js@sig21.net> | 2011-04-18 09:29:12 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-04-19 15:39:27 -0400 |
commit | 0e0d39e5f3a3e59c8513b59d4feeeadcb93b707d (patch) | |
tree | 72b63cb8c5a994cc7e6253b241aeddd131d618f0 /drivers/net/wireless/rt2x00 | |
parent | 8da3efbb4a18be30ed03dd05af18d0b026b15173 (diff) |
rt2800usb: read TX_STA_FIFO asynchronously
Trying to fix the "TX status report missed" warnings
by reading the TX_STA_FIFO entries as quickly as possible.
The TX_STA_FIFO is too small in hardware, thus reading
it only from the workqueue is too slow and entries get lost.
Start an asynchronous read of the TX_STA_FIFO directly from
the TX URB completion callback (atomic context, thus it cannot
use the blocking rt2800_register_read()). If the async
read returns a valid FIFO entry, it is pushed into a larger
FIFO inside struct rt2x00_dev, until rt2800_txdone() picks
it up.
A .tx_dma_done callback is added to struct rt2x00lib_ops
to trigger the async read from the URB completion callback.
Signed-off-by: Johannes Stezenbach <js@sig21.net>
Signed-off-by: Ivo van Doorn <IvDoorn@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/rt2800lib.c | 34 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800usb.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00usb.c | 53 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00usb.h | 15 |
5 files changed, 110 insertions, 24 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 0e2c0061cfda..d79c8fd41138 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c | |||
@@ -730,34 +730,20 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev) | |||
730 | struct data_queue *queue; | 730 | struct data_queue *queue; |
731 | struct queue_entry *entry; | 731 | struct queue_entry *entry; |
732 | u32 reg; | 732 | u32 reg; |
733 | u8 pid; | 733 | u8 qid; |
734 | int i; | ||
735 | 734 | ||
736 | /* | 735 | while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { |
737 | * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO | ||
738 | * at most X times and also stop processing once the TX_STA_FIFO_VALID | ||
739 | * flag is not set anymore. | ||
740 | * | ||
741 | * The legacy drivers use X=TX_RING_SIZE but state in a comment | ||
742 | * that the TX_STA_FIFO stack has a size of 16. We stick to our | ||
743 | * tx ring size for now. | ||
744 | */ | ||
745 | for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { | ||
746 | rt2800_register_read(rt2x00dev, TX_STA_FIFO, ®); | ||
747 | if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID)) | ||
748 | break; | ||
749 | 736 | ||
750 | /* | 737 | /* TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus |
751 | * Skip this entry when it contains an invalid | 738 | * qid is guaranteed to be one of the TX QIDs |
752 | * queue identication number. | ||
753 | */ | 739 | */ |
754 | pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); | 740 | qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); |
755 | if (pid >= QID_RX) | 741 | queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); |
756 | continue; | 742 | if (unlikely(!queue)) { |
757 | 743 | WARNING(rt2x00dev, "Got TX status for an unavailable " | |
758 | queue = rt2x00queue_get_tx_queue(rt2x00dev, pid); | 744 | "queue %u, dropping\n", qid); |
759 | if (unlikely(!queue)) | ||
760 | continue; | 745 | continue; |
746 | } | ||
761 | 747 | ||
762 | /* | 748 | /* |
763 | * Inside each queue, we process each entry in a chronological | 749 | * Inside each queue, we process each entry in a chronological |
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index f3ce5e854be6..862430e600ad 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c | |||
@@ -98,6 +98,35 @@ static void rt2800usb_stop_queue(struct data_queue *queue) | |||
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, | ||
102 | int urb_status, u32 tx_status) | ||
103 | { | ||
104 | if (urb_status) { | ||
105 | WARNING(rt2x00dev, "rt2x00usb_register_read_async failed: %d\n", urb_status); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | /* try to read all TX_STA_FIFO entries before scheduling txdone_work */ | ||
110 | if (rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID)) { | ||
111 | if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status)) { | ||
112 | WARNING(rt2x00dev, "TX status FIFO overrun, " | ||
113 | "drop tx status report.\n"); | ||
114 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | ||
115 | } else | ||
116 | rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, | ||
117 | rt2800usb_tx_sta_fifo_read_completed); | ||
118 | } else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) | ||
119 | queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); | ||
120 | } | ||
121 | |||
122 | static void rt2800usb_tx_dma_done(struct queue_entry *entry) | ||
123 | { | ||
124 | struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; | ||
125 | |||
126 | rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, | ||
127 | rt2800usb_tx_sta_fifo_read_completed); | ||
128 | } | ||
129 | |||
101 | /* | 130 | /* |
102 | * Firmware functions | 131 | * Firmware functions |
103 | */ | 132 | */ |
@@ -565,6 +594,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
565 | __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); | 594 | __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); |
566 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); | 595 | __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); |
567 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); | 596 | __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); |
597 | __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); | ||
568 | 598 | ||
569 | /* | 599 | /* |
570 | * Set the rssi offset. | 600 | * Set the rssi offset. |
@@ -635,6 +665,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { | |||
635 | .kick_queue = rt2x00usb_kick_queue, | 665 | .kick_queue = rt2x00usb_kick_queue, |
636 | .stop_queue = rt2800usb_stop_queue, | 666 | .stop_queue = rt2800usb_stop_queue, |
637 | .flush_queue = rt2x00usb_flush_queue, | 667 | .flush_queue = rt2x00usb_flush_queue, |
668 | .tx_dma_done = rt2800usb_tx_dma_done, | ||
638 | .write_tx_desc = rt2800usb_write_tx_desc, | 669 | .write_tx_desc = rt2800usb_write_tx_desc, |
639 | .write_tx_data = rt2800usb_write_tx_data, | 670 | .write_tx_data = rt2800usb_write_tx_data, |
640 | .write_beacon = rt2800_write_beacon, | 671 | .write_beacon = rt2800_write_beacon, |
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 79c385accfac..e3b9b5146bf5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -571,6 +571,7 @@ struct rt2x00lib_ops { | |||
571 | void (*kick_queue) (struct data_queue *queue); | 571 | void (*kick_queue) (struct data_queue *queue); |
572 | void (*stop_queue) (struct data_queue *queue); | 572 | void (*stop_queue) (struct data_queue *queue); |
573 | void (*flush_queue) (struct data_queue *queue); | 573 | void (*flush_queue) (struct data_queue *queue); |
574 | void (*tx_dma_done) (struct queue_entry *entry); | ||
574 | 575 | ||
575 | /* | 576 | /* |
576 | * TX control handlers | 577 | * TX control handlers |
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 0bc8dccd0f0a..5fbab6f19706 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c | |||
@@ -165,6 +165,56 @@ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, | |||
165 | } | 165 | } |
166 | EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); | 166 | EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); |
167 | 167 | ||
168 | |||
169 | struct rt2x00_async_read_data { | ||
170 | __le32 reg; | ||
171 | struct usb_ctrlrequest cr; | ||
172 | struct rt2x00_dev *rt2x00dev; | ||
173 | void (*callback)(struct rt2x00_dev *,int,u32); | ||
174 | }; | ||
175 | |||
176 | static void rt2x00usb_register_read_async_cb(struct urb *urb) | ||
177 | { | ||
178 | struct rt2x00_async_read_data *rd = urb->context; | ||
179 | rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg)); | ||
180 | kfree(urb->context); | ||
181 | } | ||
182 | |||
183 | void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, | ||
184 | const unsigned int offset, | ||
185 | void (*callback)(struct rt2x00_dev*,int,u32)) | ||
186 | { | ||
187 | struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); | ||
188 | struct urb *urb; | ||
189 | struct rt2x00_async_read_data *rd; | ||
190 | |||
191 | rd = kmalloc(sizeof(*rd), GFP_ATOMIC); | ||
192 | if (!rd) | ||
193 | return; | ||
194 | |||
195 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
196 | if (!urb) { | ||
197 | kfree(rd); | ||
198 | return; | ||
199 | } | ||
200 | |||
201 | rd->rt2x00dev = rt2x00dev; | ||
202 | rd->callback = callback; | ||
203 | rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; | ||
204 | rd->cr.bRequest = USB_MULTI_READ; | ||
205 | rd->cr.wValue = 0; | ||
206 | rd->cr.wIndex = cpu_to_le16(offset); | ||
207 | rd->cr.wLength = cpu_to_le16(sizeof(u32)); | ||
208 | |||
209 | usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
210 | (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), | ||
211 | rt2x00usb_register_read_async_cb, rd); | ||
212 | if (usb_submit_urb(urb, GFP_ATOMIC) < 0) | ||
213 | kfree(rd); | ||
214 | usb_free_urb(urb); | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); | ||
217 | |||
168 | /* | 218 | /* |
169 | * TX data handlers. | 219 | * TX data handlers. |
170 | */ | 220 | */ |
@@ -212,6 +262,9 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) | |||
212 | if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) | 262 | if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) |
213 | return; | 263 | return; |
214 | 264 | ||
265 | if (rt2x00dev->ops->lib->tx_dma_done) | ||
266 | rt2x00dev->ops->lib->tx_dma_done(entry); | ||
267 | |||
215 | /* | 268 | /* |
216 | * Report the frame as DMA done | 269 | * Report the frame as DMA done |
217 | */ | 270 | */ |
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index 6aaf51fc7ad8..e3faca6d2a4f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h | |||
@@ -345,6 +345,21 @@ int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, | |||
345 | const struct rt2x00_field32 field, | 345 | const struct rt2x00_field32 field, |
346 | u32 *reg); | 346 | u32 *reg); |
347 | 347 | ||
348 | /** | ||
349 | * rt2x00usb_register_read_async - Asynchronously read 32bit register word | ||
350 | * @rt2x00dev: Device pointer, see &struct rt2x00_dev. | ||
351 | * @offset: Register offset | ||
352 | * @callback: Functon to call when read completes. | ||
353 | * | ||
354 | * Submit a control URB to read a 32bit register. This safe to | ||
355 | * be called from atomic context. The callback will be called | ||
356 | * when the URB completes. Otherwise the function is similar | ||
357 | * to rt2x00usb_register_read(). | ||
358 | */ | ||
359 | void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, | ||
360 | const unsigned int offset, | ||
361 | void (*callback)(struct rt2x00_dev*,int,u32)); | ||
362 | |||
348 | /* | 363 | /* |
349 | * Radio handlers | 364 | * Radio handlers |
350 | */ | 365 | */ |