From 7225ce1ea93d24c0914eea0410dcfc426281f996 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Thu, 4 Nov 2010 20:39:23 +0100 Subject: rt2x00: Rename rt2x00queue_timeout Rename rt2x00queue_timeout to rt2x00queue_status_timeout to better describe what is actually timing out (note that we already have a rt2x00queue_dma_timeout). Signed-off-by: Ivo van Doorn Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index b3317df..88995d5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -361,7 +361,7 @@ void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) if (!rt2x00queue_empty(queue)) { if (rt2x00queue_dma_timeout(queue)) rt2x00usb_watchdog_tx_dma(queue); - if (rt2x00queue_timeout(queue)) + if (rt2x00queue_status_timeout(queue)) rt2x00usb_watchdog_tx_status(queue); } } -- cgit v1.1 From aaf886bd215396f295bc0489e8ae09d1c03d9aa0 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Thu, 4 Nov 2010 20:39:48 +0100 Subject: rt2x00: Remove failsave from rt2x00usb_watchdog_tx_dma When the TX status handler failed to clear the queue in rt2x00usb_watchdog_tx_dma() we shouldn't use a failsave to use the rt2x00usb txdone handler. If a driver has overriden the txdone handler it must make sure the txdone handler is capable of cleaning up the queue itself. Signed-off-by: Ivo van Doorn Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 88995d5..6dd9619 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -323,21 +323,6 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) rt2x00dev->txdone_work.func(&rt2x00dev->txdone_work); /* - * Security measure: if the driver did override the - * txdone_work function, and the hardware did arrive - * in a state which causes it to malfunction, it is - * possible that the driver couldn't handle the txdone - * event correctly. So after giving the driver the - * chance to cleanup, we now force a cleanup of any - * leftovers. - */ - if (!rt2x00queue_empty(queue)) { - WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," - " status handling failed, invoke hard reset", queue->qid); - rt2x00usb_work_txdone(&rt2x00dev->txdone_work); - } - - /* * The queue has been reset, and mac80211 is allowed to use the * queue again. */ -- cgit v1.1 From 070192dd2975c0e97bbdeac7623b755235c6db7d Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Thu, 4 Nov 2010 20:41:05 +0100 Subject: rt2x00: Fix crash on USB unplug By not scheduling the TX/RX completion worker threads when Radio is disabled, or hardware has been unplugged, the queues cannot be completely cleaned. This causes crashes when the hardware has been unplugged while the radio is still enabled. Signed-off-by: Ivo van Doorn Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 6dd9619..9ac1459 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -226,9 +226,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) * Schedule the delayed work for reading the TX status * from the device. */ - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work); + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work); } static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) @@ -409,9 +407,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb) * Schedule the delayed work for reading the RX status * from the device. */ - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work); + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work); } /* -- cgit v1.1 From d7bb5f845f437662296adbfeaab8fbfce1c32289 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Mon, 13 Dec 2010 12:32:49 +0100 Subject: rt2x00: fix hang when unplugging USB device in use When an rt2x00 USB device is unplugged while in use, it reliably hangs the whole system. After some time the watchdog prints: BUG: soft lockup - CPU#0 stuck for 64s! [kworker/u:0:5] ... [] (usb_submit_urb+0x0/0x2ac) from [] (rt2x00usb_kick_rx_entry+0xb4/0xe8 [rt2x00usb]) [] (rt2x00usb_kick_rx_entry+0x0/0xe8 [rt2x00usb]) from [] (rt2x00usb_clear_entry+x28/0x2c [rt2x00usb]) [] (rt2x00usb_clear_entry+0x0/0x2c [rt2x00usb]) from [] (rt2x00lib_rxdone+0x2e0/0x2f8 [rt2x00lib]) [] (rt2x00lib_rxdone+0x0/0x2f8 [rt2x00lib]) from [] (rt2x00usb_work_rxdone+0x54/0x74 [rt2x00usb]) [] (rt2x00usb_work_rxdone+0x0/0x74 [rt2x00usb]) from [] (process_one_work+0x20c/0x35c) Clear the DEVICE_STATE_PRESENT flag when usb_submit_urb() returns -ENODEV to fix this. Signed-off-by: Johannes Stezenbach Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 9ac1459..3a6c83e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -235,6 +235,7 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; u32 length; + int status; if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags)) return; @@ -251,7 +252,10 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) entry->skb->data, length, rt2x00usb_interrupt_txdone, entry); - if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } @@ -435,6 +439,7 @@ void rt2x00usb_clear_entry(struct queue_entry *entry) to_usb_device_intf(entry->queue->rt2x00dev->dev); struct queue_entry_priv_usb *entry_priv = entry->priv_data; int pipe; + int status; entry->flags = 0; @@ -445,7 +450,12 @@ void rt2x00usb_clear_entry(struct queue_entry *entry) rt2x00usb_interrupt_rxdone, entry); set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, + &entry->queue->rt2x00dev->flags); set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); rt2x00lib_dmadone(entry); } -- cgit v1.1 From 094a1d92fdb18c4455758b1c33e99d647c837ee9 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Mon, 13 Dec 2010 12:34:00 +0100 Subject: rt2x00: trivial: add missing \n on warnings Signed-off-by: Johannes Stezenbach Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 3a6c83e..608200e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -300,7 +300,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) unsigned short threshold = queue->threshold; WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," - " invoke forced forced reset", queue->qid); + " invoke forced forced reset\n", queue->qid); /* * Temporarily disable the TX queue, this will force mac80211 @@ -335,7 +335,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) static void rt2x00usb_watchdog_tx_status(struct data_queue *queue) { WARNING(queue->rt2x00dev, "TX queue %d status timed out," - " invoke forced tx handler", queue->qid); + " invoke forced tx handler\n", queue->qid); ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work); } -- cgit v1.1 From 5450b7e2f0b47e52175b31399d8186a74ef3c46d Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:34:22 +0100 Subject: rt2x00: Introduce 3 queue commands in drivers (start, kick, stop). As part of the queue refactoring, we now introduce 3 queue commands: start, kick, stop. - Start: will enable a queue, for TX this will not mean anything, while for beacons and RX this will update the registers to enable the queue. - Kick: This will kick all pending frames to the hardware. This is needed for the TX queue to push all frames to the HW after the queue has been started - Stop: This will stop the queue in the hardware, and cancel any pending work (So this doesn't mean the queue is empty after a stop!). Move all code from the drivers into the appropriate functions, and link those calls to the old rt2x00lib callback functions (we will fix this later when we refactor the queue control inside rt2x00lib). Signed-off-by: Ivo van Doorn Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 608200e..12958a4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -268,7 +268,7 @@ void rt2x00usb_kick_tx_queue(struct data_queue *queue) } EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue); -static void rt2x00usb_kill_tx_entry(struct queue_entry *entry) +static void rt2x00usb_kill_entry(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv = entry->priv_data; @@ -287,12 +287,12 @@ static void rt2x00usb_kill_tx_entry(struct queue_entry *entry) usb_kill_urb(bcn_priv->guardian_urb); } -void rt2x00usb_kill_tx_queue(struct data_queue *queue) +void rt2x00usb_stop_queue(struct data_queue *queue) { rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, - rt2x00usb_kill_tx_entry); + rt2x00usb_kill_entry); } -EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); +EXPORT_SYMBOL_GPL(rt2x00usb_stop_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { @@ -316,7 +316,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) * Kill all entries in the queue, afterwards we need to * wait a bit for all URBs to be cancelled. */ - rt2x00usb_kill_tx_queue(queue); + rt2x00usb_stop_queue(queue); /* * In case that a driver has overriden the txdone_work @@ -423,7 +423,7 @@ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) REGISTER_TIMEOUT); /* - * The USB version of kill_tx_queue also works + * The USB version of also works * on the RX queue. */ rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev->rx); -- cgit v1.1 From dbba306f2ae574450a7a5133d6637fe6f5fafc72 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:34:54 +0100 Subject: rt2x00: Reorganize queue callback functions As part of the queue refactoring, change the queue callback function names to have 3 different actions: start, kick & stop. We can now also remove the STATE_RADIO_RX_ON/STATE_RADIO_RX_OFF device_state flags, and replace the usage with using the start_queue/stop_queue callback functions. This streamlines the RX queue handling to the similar approach as all other queues. Signed-off-by: Ivo van Doorn Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 12958a4..d4361dc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -261,12 +261,22 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) } } -void rt2x00usb_kick_tx_queue(struct data_queue *queue) +void rt2x00usb_kick_queue(struct data_queue *queue) { - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, - rt2x00usb_kick_tx_entry); + switch (queue->qid) { + case QID_AC_BE: + case QID_AC_BK: + case QID_AC_VI: + case QID_AC_VO: + if (!rt2x00queue_empty(queue)) + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, + rt2x00usb_kick_tx_entry); + break; + default: + break; + } } -EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue); +EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); static void rt2x00usb_kill_entry(struct queue_entry *entry) { @@ -422,11 +432,7 @@ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, REGISTER_TIMEOUT); - /* - * The USB version of also works - * on the RX queue. - */ - rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev->rx); + rt2x00dev->ops->lib->stop_queue(rt2x00dev->rx); } EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); -- cgit v1.1 From 0b7fde54f94979edc67bbf86b5adba702ebfefe8 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:35:17 +0100 Subject: rt2x00: Protect queue control with mutex Add wrapper functions in rt2x00queue.c to start & stop queues. This control must be protected using a mutex. Queues can also be paused which will halt the flow of packets between the driver and mac80211. This doesn't require a mutex protection. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 190 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 98 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index d4361dc..fca29ae 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -261,6 +261,89 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) } } +/* + * RX data handlers. + */ +static void rt2x00usb_work_rxdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, rxdone_work); + struct queue_entry *entry; + struct skb_frame_desc *skbdesc; + u8 rxd[32]; + + while (!rt2x00queue_empty(rt2x00dev->rx)) { + entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + break; + + /* + * Fill in desc fields of the skb descriptor + */ + skbdesc = get_skb_frame_desc(entry->skb); + skbdesc->desc = rxd; + skbdesc->desc_len = entry->queue->desc_size; + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry); + } +} + +static void rt2x00usb_interrupt_rxdone(struct urb *urb) +{ + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return; + + /* + * Report the frame as DMA done + */ + rt2x00lib_dmadone(entry); + + /* + * Check if the received data is simply too small + * to be actually valid, or if the urb is signaling + * a problem. + */ + if (urb->actual_length < entry->queue->desc_size || urb->status) + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + + /* + * Schedule the delayed work for reading the RX status + * from the device. + */ + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work); +} + +static void rt2x00usb_kick_rx_entry(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + int status; + + if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return; + + usb_fill_bulk_urb(entry_priv->urb, usb_dev, + usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), + entry->skb->data, entry->skb->len, + rt2x00usb_interrupt_rxdone, entry); + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } +} + void rt2x00usb_kick_queue(struct data_queue *queue) { switch (queue->qid) { @@ -272,6 +355,11 @@ void rt2x00usb_kick_queue(struct data_queue *queue) rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, rt2x00usb_kick_tx_entry); break; + case QID_RX: + if (!rt2x00queue_full(queue)) + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, + rt2x00usb_kick_rx_entry); + break; default: break; } @@ -307,7 +395,6 @@ EXPORT_SYMBOL_GPL(rt2x00usb_stop_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - unsigned short threshold = queue->threshold; WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," " invoke forced forced reset\n", queue->qid); @@ -315,18 +402,8 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) /* * Temporarily disable the TX queue, this will force mac80211 * to use the other queues until this queue has been restored. - * - * Set the queue threshold to the queue limit. This prevents the - * queue from being enabled during the txdone handler. */ - queue->threshold = queue->limit; - ieee80211_stop_queue(rt2x00dev->hw, queue->qid); - - /* - * Kill all entries in the queue, afterwards we need to - * wait a bit for all URBs to be cancelled. - */ - rt2x00usb_stop_queue(queue); + rt2x00queue_stop_queue(queue); /* * In case that a driver has overriden the txdone_work @@ -338,8 +415,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) * The queue has been reset, and mac80211 is allowed to use the * queue again. */ - queue->threshold = threshold; - ieee80211_wake_queue(rt2x00dev->hw, queue->qid); + rt2x00queue_start_queue(queue); } static void rt2x00usb_watchdog_tx_status(struct data_queue *queue) @@ -366,73 +442,12 @@ void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); /* - * RX data handlers. - */ -static void rt2x00usb_work_rxdone(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, rxdone_work); - struct queue_entry *entry; - struct skb_frame_desc *skbdesc; - u8 rxd[32]; - - while (!rt2x00queue_empty(rt2x00dev->rx)) { - entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); - - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - break; - - /* - * Fill in desc fields of the skb descriptor - */ - skbdesc = get_skb_frame_desc(entry->skb); - skbdesc->desc = rxd; - skbdesc->desc_len = entry->queue->desc_size; - - /* - * Send the frame to rt2x00lib for further processing. - */ - rt2x00lib_rxdone(entry); - } -} - -static void rt2x00usb_interrupt_rxdone(struct urb *urb) -{ - struct queue_entry *entry = (struct queue_entry *)urb->context; - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - - if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - return; - - /* - * Report the frame as DMA done - */ - rt2x00lib_dmadone(entry); - - /* - * Check if the received data is simply too small - * to be actually valid, or if the urb is signaling - * a problem. - */ - if (urb->actual_length < entry->queue->desc_size || urb->status) - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - - /* - * Schedule the delayed work for reading the RX status - * from the device. - */ - ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work); -} - -/* * Radio handlers */ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) { rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, REGISTER_TIMEOUT); - - rt2x00dev->ops->lib->stop_queue(rt2x00dev->rx); } EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); @@ -441,31 +456,10 @@ EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); */ void rt2x00usb_clear_entry(struct queue_entry *entry) { - struct usb_device *usb_dev = - to_usb_device_intf(entry->queue->rt2x00dev->dev); - struct queue_entry_priv_usb *entry_priv = entry->priv_data; - int pipe; - int status; - entry->flags = 0; - if (entry->queue->qid == QID_RX) { - pipe = usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint); - usb_fill_bulk_urb(entry_priv->urb, usb_dev, pipe, - entry->skb->data, entry->skb->len, - rt2x00usb_interrupt_rxdone, entry); - - set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - - status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); - if (status) { - if (status == -ENODEV) - clear_bit(DEVICE_STATE_PRESENT, - &entry->queue->rt2x00dev->flags); - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - rt2x00lib_dmadone(entry); - } - } + if (entry->queue->qid == QID_RX) + rt2x00usb_kick_rx_entry(entry); } EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); -- cgit v1.1 From 5be65609fec2e331c7d804471be3d59089a30d98 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:35:40 +0100 Subject: 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 Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 70 ++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 23 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index fca29ae..cd80eec 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -366,7 +366,7 @@ void rt2x00usb_kick_queue(struct data_queue *queue) } EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); -static void rt2x00usb_kill_entry(struct queue_entry *entry) +static void rt2x00usb_flush_entry(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv = entry->priv_data; @@ -385,37 +385,61 @@ static void rt2x00usb_kill_entry(struct queue_entry *entry) usb_kill_urb(bcn_priv->guardian_urb); } -void rt2x00usb_stop_queue(struct data_queue *queue) +void rt2x00usb_flush_queue(struct data_queue *queue) { + struct work_struct *completion; + unsigned int i; + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, - rt2x00usb_kill_entry); + rt2x00usb_flush_entry); + + /* + * Obtain the queue completion handler + */ + switch (queue->qid) { + case QID_AC_BE: + case QID_AC_BK: + case QID_AC_VI: + case QID_AC_VO: + completion = &queue->rt2x00dev->txdone_work; + break; + case QID_RX: + completion = &queue->rt2x00dev->rxdone_work; + break; + default: + return; + } + + for (i = 0; i < 20; i++) { + /* + * Check if the driver is already done, otherwise we + * have to sleep a little while to give the driver/hw + * the oppurtunity to complete interrupt process itself. + */ + if (rt2x00queue_empty(queue)) + break; + + /* + * Schedule the completion handler manually, when this + * worker function runs, it should cleanup the queue. + */ + ieee80211_queue_work(queue->rt2x00dev->hw, completion); + + /* + * Wait for a little while to give the driver + * the oppurtunity to recover itself. + */ + msleep(10); + } } -EXPORT_SYMBOL_GPL(rt2x00usb_stop_queue); +EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," " invoke forced forced reset\n", queue->qid); - /* - * Temporarily disable the TX queue, this will force mac80211 - * to use the other queues until this queue has been restored. - */ - rt2x00queue_stop_queue(queue); - - /* - * In case that a driver has overriden the txdone_work - * function, we invoke the TX done through there. - */ - rt2x00dev->txdone_work.func(&rt2x00dev->txdone_work); - - /* - * The queue has been reset, and mac80211 is allowed to use the - * queue again. - */ - rt2x00queue_start_queue(queue); + rt2x00queue_flush_queue(queue, true); } static void rt2x00usb_watchdog_tx_status(struct data_queue *queue) -- cgit v1.1 From 64e7d72384c2ecef5a892b2243623af265dd83cc Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:36:00 +0100 Subject: rt2x00: Cleanup RX index counting Add the rt2x00_dmastart function to rt2x00lib which marks the queue_entry as "owned by device", and increased the Q_INDEX number. This cleanups up the index handling by rt2x00lib which at until so far used hackish approaches to keep the RX queue index numbering sane. The rt2x00pci.c changes are from Helmut Schaa Signed-off-by: Ivo van Doorn Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index cd80eec..cd29ebc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -330,6 +330,8 @@ static void rt2x00usb_kick_rx_entry(struct queue_entry *entry) if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; + rt2x00lib_dmastart(entry); + usb_fill_bulk_urb(entry_priv->urb, usb_dev, usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), entry->skb->data, entry->skb->len, -- cgit v1.1 From dba5dc1ae9764902f46d5225c9ff40e4f7b614c7 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:36:18 +0100 Subject: rt2x00: Introduce extra queue entry sanity flag Add a queue entry flag ENTRY_DATA_STATUS_PENDING, which can be used to indicate a queue entry has returned from the hardware and is waiting for status processing. Using this flag we can add some extra sanity checks to prevent queue corruption. Signed-off-by: Ivo van Doorn Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index cd29ebc..8a16b51 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -195,7 +195,8 @@ static void rt2x00usb_work_txdone(struct work_struct *work) while (!rt2x00queue_empty(queue)) { entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) break; rt2x00usb_work_txdone_entry(entry); @@ -237,7 +238,8 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) u32 length; int status; - if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags)) + if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) return; /* @@ -275,7 +277,8 @@ static void rt2x00usb_work_rxdone(struct work_struct *work) while (!rt2x00queue_empty(rt2x00dev->rx)) { entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) break; /* @@ -327,7 +330,8 @@ static void rt2x00usb_kick_rx_entry(struct queue_entry *entry) struct queue_entry_priv_usb *entry_priv = entry->priv_data; int status; - if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) return; rt2x00lib_dmastart(entry); -- cgit v1.1 From f615e9a38a8e6239d35891a05f2ac1159088780a Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 13 Dec 2010 12:36:38 +0100 Subject: rt2x00: Fix WMM Queue naming The Queue names were incorrectly copied from the legacy drivers, as a result the queue names were inversed to what was expected. This renames the queues using this mapping: QID_AC_BK -> QID_AC_VO (priority 0) QID_AC_BE -> QID_AC_VI (priority 1) QID_AC_VI -> QID_AC_BE (priority 2) QID_AC_VO -> QID_AC_BK (priority 3) Note that this was a naming problem only, which didn't affect the assignment of frames to their respective queues. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/rt2x00/rt2x00usb.c') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 8a16b51..1a9937d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -353,10 +353,10 @@ static void rt2x00usb_kick_rx_entry(struct queue_entry *entry) void rt2x00usb_kick_queue(struct data_queue *queue) { switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: - case QID_AC_VI: - case QID_AC_VO: if (!rt2x00queue_empty(queue)) rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, rt2x00usb_kick_tx_entry); @@ -403,10 +403,10 @@ void rt2x00usb_flush_queue(struct data_queue *queue) * Obtain the queue completion handler */ switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: case QID_AC_BE: case QID_AC_BK: - case QID_AC_VI: - case QID_AC_VO: completion = &queue->rt2x00dev->txdone_work; break; case QID_RX: -- cgit v1.1