USB: whci-hcd: always do an update after processing a halted qTD
[pandora-kernel.git] / drivers / usb / host / whci / asl.c
index c205078..562eba1 100644 (file)
@@ -115,6 +115,10 @@ static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
                if (status & QTD_STS_HALTED) {
                        /* Ug, an error. */
                        process_halted_qtd(whc, qset, td);
+                       /* A halted qTD always triggers an update
+                          because the qset was either removed or
+                          reactivated. */
+                       update |= WHC_UPDATE_UPDATED;
                        goto done;
                }
 
@@ -227,11 +231,21 @@ void scan_async_work(struct work_struct *work)
        /*
         * Now that the ASL is updated, complete the removal of any
         * removed qsets.
+        *
+        * If the qset was to be reset, do so and reinsert it into the
+        * ASL if it has pending transfers.
         */
        spin_lock_irq(&whc->lock);
 
        list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
                qset_remove_complete(whc, qset);
+               if (qset->reset) {
+                       qset_reset(whc, qset);
+                       if (!list_empty(&qset->stds)) {
+                               asl_qset_insert_begin(whc, qset);
+                               queue_work(whc->workqueue, &whc->async_work);
+                       }
+               }
        }
 
        spin_unlock_irq(&whc->lock);
@@ -267,7 +281,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
        else
                err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
        if (!err) {
-               if (!qset->in_sw_list)
+               if (!qset->in_sw_list && !qset->remove)
                        asl_qset_insert_begin(whc, qset);
        } else
                usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
@@ -295,6 +309,7 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
        struct whc_urb *wurb = urb->hcpriv;
        struct whc_qset *qset = wurb->qset;
        struct whc_std *std, *t;
+       bool has_qtd = false;
        int ret;
        unsigned long flags;
 
@@ -305,17 +320,21 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
                goto out;
 
        list_for_each_entry_safe(std, t, &qset->stds, list_node) {
-               if (std->urb == urb)
+               if (std->urb == urb) {
+                       if (std->qtd)
+                               has_qtd = true;
                        qset_free_std(whc, std);
-               else
+               else
                        std->qtd = NULL; /* so this std is re-added when the qset is */
        }
 
-       asl_qset_remove(whc, qset);
-       wurb->status = status;
-       wurb->is_async = true;
-       queue_work(whc->workqueue, &wurb->dequeue_work);
-
+       if (has_qtd) {
+               asl_qset_remove(whc, qset);
+               wurb->status = status;
+               wurb->is_async = true;
+               queue_work(whc->workqueue, &wurb->dequeue_work);
+       } else
+               qset_remove_urb(whc, qset, urb, status);
 out:
        spin_unlock_irqrestore(&whc->lock, flags);