]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/rt2x00/rt2x00usb.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / rt2x00 / rt2x00usb.c
index b3317df7a7d4afdd88169c8ad16f2ad99984bc9e..1a9937d5aff65e923498cc31208fd1e8b42e3168 100644 (file)
@@ -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);
@@ -226,9 +227,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)
@@ -237,8 +236,10 @@ 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))
+       if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) ||
+           test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
                return;
 
        /*
@@ -253,121 +254,15 @@ 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);
        }
 }
 
-void rt2x00usb_kick_tx_queue(struct data_queue *queue)
-{
-       rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
-                                  rt2x00usb_kick_tx_entry);
-}
-EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue);
-
-static void rt2x00usb_kill_tx_entry(struct queue_entry *entry)
-{
-       struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       struct queue_entry_priv_usb *entry_priv = entry->priv_data;
-       struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
-
-       if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
-               return;
-
-       usb_kill_urb(entry_priv->urb);
-
-       /*
-        * Kill guardian urb (if required by driver).
-        */
-       if ((entry->queue->qid == QID_BEACON) &&
-           (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
-               usb_kill_urb(bcn_priv->guardian_urb);
-}
-
-void rt2x00usb_kill_tx_queue(struct data_queue *queue)
-{
-       rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
-                                  rt2x00usb_kill_tx_entry);
-}
-EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_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", queue->qid);
-
-       /*
-        * 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_kill_tx_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);
-
-       /*
-        * 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.
-        */
-       queue->threshold = threshold;
-       ieee80211_wake_queue(rt2x00dev->hw, queue->qid);
-}
-
-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);
-
-       ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
-}
-
-void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
-{
-       struct data_queue *queue;
-
-       tx_queue_for_each(rt2x00dev, 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);
-
 /*
  * RX data handlers.
  */
@@ -382,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;
 
                /*
@@ -424,11 +320,157 @@ 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);
 }
 
+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) ||
+           test_bit(ENTRY_DATA_STATUS_PENDING, &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,
+                         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) {
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               if (!rt2x00queue_empty(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;
+       }
+}
+EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue);
+
+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;
+       struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
+
+       if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+               return;
+
+       usb_kill_urb(entry_priv->urb);
+
+       /*
+        * Kill guardian urb (if required by driver).
+        */
+       if ((entry->queue->qid == QID_BEACON) &&
+           (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
+               usb_kill_urb(bcn_priv->guardian_urb);
+}
+
+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_flush_entry);
+
+       /*
+        * Obtain the queue completion handler
+        */
+       switch (queue->qid) {
+       case QID_AC_VO:
+       case QID_AC_VI:
+       case QID_AC_BE:
+       case QID_AC_BK:
+               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_flush_queue);
+
+static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
+{
+       WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
+               " invoke forced forced reset\n", queue->qid);
+
+       rt2x00queue_flush_queue(queue, true);
+}
+
+static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
+{
+       WARNING(queue->rt2x00dev, "TX queue %d status timed out,"
+               " invoke forced tx handler\n", queue->qid);
+
+       ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
+}
+
+void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue;
+
+       tx_queue_for_each(rt2x00dev, queue) {
+               if (!rt2x00queue_empty(queue)) {
+                       if (rt2x00queue_dma_timeout(queue))
+                               rt2x00usb_watchdog_tx_dma(queue);
+                       if (rt2x00queue_status_timeout(queue))
+                               rt2x00usb_watchdog_tx_status(queue);
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
+
 /*
  * Radio handlers
  */
@@ -436,12 +478,6 @@ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev)
 {
        rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0,
                                    REGISTER_TIMEOUT);
-
-       /*
-        * The USB version of kill_tx_queue also works
-        * on the RX queue.
-        */
-       rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev->rx);
 }
 EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio);
 
@@ -450,25 +486,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;
-
        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);
-               if (usb_submit_urb(entry_priv->urb, GFP_ATOMIC)) {
-                       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);