]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/host/isp116x-hcd.c
isp116x-hcd: prepare for urb->status
[linux-2.6-omap-h63xx.git] / drivers / usb / host / isp116x-hcd.c
index a72e041df8e7256b800c08226493ea46df82b32b..5c851a36de722e36d67fb65128219213d95d0235 100644 (file)
@@ -228,7 +228,6 @@ static void preproc_atl_queue(struct isp116x *isp116x)
                                   struct urb, urb_list);
                ptd = &ep->ptd;
                len = ep->length;
-               spin_lock(&urb->lock);
                ep->data = (unsigned char *)urb->transfer_buffer
                    + urb->actual_length;
 
@@ -264,7 +263,6 @@ static void preproc_atl_queue(struct isp116x *isp116x)
                    | PTD_EP(ep->epnum);
                ptd->len = PTD_LEN(len) | PTD_DIR(dir);
                ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
-               spin_unlock(&urb->lock);
                if (!ep->active) {
                        ptd->mps |= PTD_LAST_MSK;
                        isp116x->atl_last_dir = dir;
@@ -274,6 +272,61 @@ static void preproc_atl_queue(struct isp116x *isp116x)
        }
 }
 
+/*
+  Take done or failed requests out of schedule. Give back
+  processed urbs.
+*/
+static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
+                          struct urb *urb)
+__releases(isp116x->lock) __acquires(isp116x->lock)
+{
+       unsigned i;
+
+       urb->hcpriv = NULL;
+       ep->error_count = 0;
+
+       if (usb_pipecontrol(urb->pipe))
+               ep->nextpid = USB_PID_SETUP;
+
+       urb_dbg(urb, "Finish");
+
+       spin_unlock(&isp116x->lock);
+       usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb);
+       spin_lock(&isp116x->lock);
+
+       /* take idle endpoints out of the schedule */
+       if (!list_empty(&ep->hep->urb_list))
+               return;
+
+       /* async deschedule */
+       if (!list_empty(&ep->schedule)) {
+               list_del_init(&ep->schedule);
+               return;
+       }
+
+       /* periodic deschedule */
+       DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+       for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+               struct isp116x_ep *temp;
+               struct isp116x_ep **prev = &isp116x->periodic[i];
+
+               while (*prev && ((temp = *prev) != ep))
+                       prev = &temp->next;
+               if (*prev)
+                       *prev = ep->next;
+               isp116x->load[i] -= ep->load;
+       }
+       ep->branch = PERIODIC_SIZE;
+       isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
+           ep->load / ep->period;
+
+       /* switch irq type? */
+       if (!--isp116x->periodic_count) {
+               isp116x->irqenb &= ~HCuPINT_SOF;
+               isp116x->irqenb |= HCuPINT_ATL;
+       }
+}
+
 /*
   Analyze transfer results, handle partial transfers and errors
 */
@@ -284,6 +337,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
        struct usb_device *udev;
        struct ptd *ptd;
        int short_not_ok;
+       int status;
        u8 cc;
 
        for (ep = isp116x->atl_active; ep; ep = ep->active) {
@@ -294,7 +348,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                ptd = &ep->ptd;
                cc = PTD_GET_CC(ptd);
                short_not_ok = 1;
-               spin_lock(&urb->lock);
+               status = -EINPROGRESS;
 
                /* Data underrun is special. For allowed underrun
                   we clear the error and continue as normal. For
@@ -302,47 +356,36 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                   immediately while for control transfer,
                   we do a STATUS stage. */
                if (cc == TD_DATAUNDERRUN) {
-                       if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) {
-                               DBG("Allowed data underrun\n");
+                       if (!(urb->transfer_flags & URB_SHORT_NOT_OK) ||
+                                       usb_pipecontrol(urb->pipe)) {
+                               DBG("Allowed or control data underrun\n");
                                cc = TD_CC_NOERROR;
                                short_not_ok = 0;
                        } else {
                                ep->error_count = 1;
-                               if (usb_pipecontrol(urb->pipe))
-                                       ep->nextpid = USB_PID_ACK;
-                               else
-                                       usb_settoggle(udev, ep->epnum,
-                                                     ep->nextpid ==
-                                                     USB_PID_OUT,
-                                                     PTD_GET_TOGGLE(ptd));
+                               usb_settoggle(udev, ep->epnum,
+                                             ep->nextpid == USB_PID_OUT,
+                                             PTD_GET_TOGGLE(ptd));
                                urb->actual_length += PTD_GET_COUNT(ptd);
-                               urb->status = cc_to_error[TD_DATAUNDERRUN];
-                               spin_unlock(&urb->lock);
-                               continue;
+                               status = cc_to_error[TD_DATAUNDERRUN];
+                               goto done;
                        }
                }
-               /* Keep underrun error through the STATUS stage */
-               if (urb->status == cc_to_error[TD_DATAUNDERRUN])
-                       cc = TD_DATAUNDERRUN;
 
                if (cc != TD_CC_NOERROR && cc != TD_NOTACCESSED
                    && (++ep->error_count >= 3 || cc == TD_CC_STALL
                        || cc == TD_DATAOVERRUN)) {
-                       if (urb->status == -EINPROGRESS)
-                               urb->status = cc_to_error[cc];
+                       status = cc_to_error[cc];
                        if (ep->nextpid == USB_PID_ACK)
                                ep->nextpid = 0;
-                       spin_unlock(&urb->lock);
-                       continue;
+                       goto done;
                }
                /* According to usb spec, zero-length Int transfer signals
                   finishing of the urb. Hey, does this apply only
                   for IN endpoints? */
                if (usb_pipeint(urb->pipe) && !PTD_GET_LEN(ptd)) {
-                       if (urb->status == -EINPROGRESS)
-                               urb->status = 0;
-                       spin_unlock(&urb->lock);
-                       continue;
+                       status = 0;
+                       goto done;
                }
 
                /* Relax after previously failed, but later succeeded
@@ -381,8 +424,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                        /* All data for this URB is transferred, let's finish */
                        if (usb_pipecontrol(urb->pipe))
                                ep->nextpid = USB_PID_ACK;
-                       else if (urb->status == -EINPROGRESS)
-                               urb->status = 0;
+                       else
+                               status = 0;
                        break;
                case USB_PID_SETUP:
                        if (PTD_GET_ACTIVE(ptd)
@@ -402,69 +445,27 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                        if (PTD_GET_ACTIVE(ptd)
                            || (cc != TD_CC_NOERROR && cc < 0x0E))
                                break;
-                       if (urb->status == -EINPROGRESS)
-                               urb->status = 0;
+                       if ((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+                                       urb->actual_length <
+                                               urb->transfer_buffer_length)
+                               status = -EREMOTEIO;
+                       else
+                               status = 0;
                        ep->nextpid = 0;
                        break;
                default:
                        BUG();
                }
-               spin_unlock(&urb->lock);
-       }
-}
-
-/*
-  Take done or failed requests out of schedule. Give back
-  processed urbs.
-*/
-static void finish_request(struct isp116x *isp116x, struct isp116x_ep *ep,
-                          struct urb *urb, struct pt_regs *regs)
-__releases(isp116x->lock) __acquires(isp116x->lock)
-{
-       unsigned i;
-
-       urb->hcpriv = NULL;
-       ep->error_count = 0;
-
-       if (usb_pipecontrol(urb->pipe))
-               ep->nextpid = USB_PID_SETUP;
-
-       urb_dbg(urb, "Finish");
-
-       spin_unlock(&isp116x->lock);
-       usb_hcd_giveback_urb(isp116x_to_hcd(isp116x), urb, regs);
-       spin_lock(&isp116x->lock);
-
-       /* take idle endpoints out of the schedule */
-       if (!list_empty(&ep->hep->urb_list))
-               return;
-
-       /* async deschedule */
-       if (!list_empty(&ep->schedule)) {
-               list_del_init(&ep->schedule);
-               return;
-       }
-
-       /* periodic deschedule */
-       DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
-       for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
-               struct isp116x_ep *temp;
-               struct isp116x_ep **prev = &isp116x->periodic[i];
 
-               while (*prev && ((temp = *prev) != ep))
-                       prev = &temp->next;
-               if (*prev)
-                       *prev = ep->next;
-               isp116x->load[i] -= ep->load;
-       }
-       ep->branch = PERIODIC_SIZE;
-       isp116x_to_hcd(isp116x)->self.bandwidth_allocated -=
-           ep->load / ep->period;
-
-       /* switch irq type? */
-       if (!--isp116x->periodic_count) {
-               isp116x->irqenb &= ~HCuPINT_SOF;
-               isp116x->irqenb |= HCuPINT_ATL;
+ done:
+               if (status != -EINPROGRESS) {
+                       spin_lock(&urb->lock);
+                       if (urb->status == -EINPROGRESS)
+                               urb->status = status;
+                       spin_unlock(&urb->lock);
+               }
+               if (urb->status != -EINPROGRESS)
+                       finish_request(isp116x, ep, urb);
        }
 }
 
@@ -568,11 +569,8 @@ static void start_atl_transfers(struct isp116x *isp116x)
 /*
   Finish the processed transfers
 */
-static void finish_atl_transfers(struct isp116x *isp116x, struct pt_regs *regs)
+static void finish_atl_transfers(struct isp116x *isp116x)
 {
-       struct isp116x_ep *ep;
-       struct urb *urb;
-
        if (!isp116x->atl_active)
                return;
        /* Fifo not ready? */
@@ -582,20 +580,10 @@ static void finish_atl_transfers(struct isp116x *isp116x, struct pt_regs *regs)
        atomic_inc(&isp116x->atl_finishing);
        unpack_fifo(isp116x);
        postproc_atl_queue(isp116x);
-       for (ep = isp116x->atl_active; ep; ep = ep->active) {
-               urb =
-                   container_of(ep->hep->urb_list.next, struct urb, urb_list);
-               /* USB_PID_ACK check here avoids finishing of
-                  control transfers, for which TD_DATAUNDERRUN
-                  occured, while URB_SHORT_NOT_OK was set */
-               if (urb && urb->status != -EINPROGRESS
-                   && ep->nextpid != USB_PID_ACK)
-                       finish_request(isp116x, ep, urb, regs);
-       }
        atomic_dec(&isp116x->atl_finishing);
 }
 
-static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
+static irqreturn_t isp116x_irq(struct usb_hcd *hcd)
 {
        struct isp116x *isp116x = hcd_to_isp116x(hcd);
        u16 irqstat;
@@ -608,7 +596,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 
        if (irqstat & (HCuPINT_ATL | HCuPINT_SOF)) {
                ret = IRQ_HANDLED;
-               finish_atl_transfers(isp116x, regs);
+               finish_atl_transfers(isp116x);
        }
 
        if (irqstat & HCuPINT_OPR) {
@@ -821,15 +809,12 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
        }
 
        /* in case of unlink-during-submit */
-       spin_lock(&urb->lock);
        if (urb->status != -EINPROGRESS) {
-               spin_unlock(&urb->lock);
-               finish_request(isp116x, ep, urb, NULL);
+               finish_request(isp116x, ep, urb);
                ret = 0;
                goto fail;
        }
        urb->hcpriv = hep;
-       spin_unlock(&urb->lock);
        start_atl_transfers(isp116x);
 
       fail:
@@ -870,7 +855,7 @@ static int isp116x_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
                        }
 
        if (urb)
-               finish_request(isp116x, ep, urb, NULL);
+               finish_request(isp116x, ep, urb);
 
        spin_unlock_irqrestore(&isp116x->lock, flags);
        return 0;
@@ -1577,7 +1562,7 @@ static int isp116x_remove(struct platform_device *pdev)
 
 #define resource_len(r) (((r)->end - (r)->start) + 1)
 
-static int __init isp116x_probe(struct platform_device *pdev)
+static int __devinit isp116x_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd;
        struct isp116x *isp116x;