X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Frt2x00%2Frt2x00usb.c;h=b3317df7a7d4afdd88169c8ad16f2ad99984bc9e;hb=f9cef506815386df4bd7e463b59e0a0984ce0355;hp=aec6440d364a7168f264b49997e95725bffdf3e0;hpb=652a9dd2a0c07251e328519cc23f1316ab13ed51;p=mv-sheeva.git diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index aec6440d364..b3317df7a7d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -208,8 +208,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) struct queue_entry *entry = (struct queue_entry *)urb->context; struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || - !__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; /* @@ -221,13 +220,15 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) * Check if the frame was correctly uploaded */ if (urb->status) - __set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); /* * Schedule the delayed work for reading the TX status * from the device. */ - ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work); + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work); } static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) @@ -252,7 +253,10 @@ static void rt2x00usb_kick_tx_entry(struct queue_entry *entry) entry->skb->data, length, rt2x00usb_interrupt_txdone, entry); - usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } } void rt2x00usb_kick_tx_queue(struct data_queue *queue) @@ -290,8 +294,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) { - struct queue_entry *entry; - struct queue_entry_priv_usb *entry_priv; + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; unsigned short threshold = queue->threshold; WARNING(queue->rt2x00dev, "TX queue %d DMA timed out," @@ -305,28 +308,33 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) * queue from being enabled during the txdone handler. */ queue->threshold = queue->limit; - ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid); + ieee80211_stop_queue(rt2x00dev->hw, queue->qid); /* - * Reset all currently uploaded TX frames. + * Kill all entries in the queue, afterwards we need to + * wait a bit for all URBs to be cancelled. */ - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - entry_priv = entry->priv_data; - usb_kill_urb(entry_priv->urb); + rt2x00usb_kill_tx_queue(queue); - /* - * We need a short delay here to wait for - * the URB to be canceled - */ - do { - udelay(100); - } while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)); + /* + * 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); - /* - * Invoke the TX done handler - */ - rt2x00usb_work_txdone_entry(entry); + /* + * 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); } /* @@ -334,7 +342,7 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) * queue again. */ queue->threshold = threshold; - ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); + ieee80211_wake_queue(rt2x00dev->hw, queue->qid); } static void rt2x00usb_watchdog_tx_status(struct data_queue *queue) @@ -350,10 +358,12 @@ void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; tx_queue_for_each(rt2x00dev, queue) { - if (rt2x00queue_dma_timeout(queue)) - rt2x00usb_watchdog_tx_dma(queue); - if (rt2x00queue_timeout(queue)) - rt2x00usb_watchdog_tx_status(queue); + if (!rt2x00queue_empty(queue)) { + if (rt2x00queue_dma_timeout(queue)) + rt2x00usb_watchdog_tx_dma(queue); + if (rt2x00queue_timeout(queue)) + rt2x00usb_watchdog_tx_status(queue); + } } } EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); @@ -385,7 +395,7 @@ static void rt2x00usb_work_rxdone(struct work_struct *work) /* * Send the frame to rt2x00lib for further processing. */ - rt2x00lib_rxdone(rt2x00dev, entry); + rt2x00lib_rxdone(entry); } } @@ -394,8 +404,7 @@ 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_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || - !__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; /* @@ -409,13 +418,15 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb) * a problem. */ if (urb->actual_length < entry->queue->desc_size || urb->status) - __set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + 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); + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work); } /* @@ -453,7 +464,10 @@ void rt2x00usb_clear_entry(struct queue_entry *entry) rt2x00usb_interrupt_rxdone, entry); set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) { + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } } } EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); @@ -528,9 +542,9 @@ static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) return 0; } -static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue) +static int rt2x00usb_alloc_entries(struct data_queue *queue) { + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv; struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; @@ -547,7 +561,7 @@ static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev, * no guardian byte was required for the beacon, * then we are done. */ - if (rt2x00dev->bcn != queue || + if (queue->qid != QID_BEACON || !test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)) return 0; @@ -561,9 +575,9 @@ static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev, return 0; } -static void rt2x00usb_free_urb(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue) +static void rt2x00usb_free_entries(struct data_queue *queue) { + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; struct queue_entry_priv_usb *entry_priv; struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; @@ -582,7 +596,7 @@ static void rt2x00usb_free_urb(struct rt2x00_dev *rt2x00dev, * no guardian byte was required for the beacon, * then we are done. */ - if (rt2x00dev->bcn != queue || + if (queue->qid != QID_BEACON || !test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)) return; @@ -609,7 +623,7 @@ int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) * Allocate DMA */ queue_for_each(rt2x00dev, queue) { - status = rt2x00usb_alloc_urb(rt2x00dev, queue); + status = rt2x00usb_alloc_entries(queue); if (status) goto exit; } @@ -628,7 +642,7 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; queue_for_each(rt2x00dev, queue) - rt2x00usb_free_urb(rt2x00dev, queue); + rt2x00usb_free_entries(queue); } EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize);