USB: ehci tolerates some buggy devices
[pandora-kernel.git] / drivers / usb / host / ehci-q.c
index b10f39c..c0e752c 100644 (file)
@@ -198,7 +198,8 @@ static int qtd_copy_status (
 
                /* if async CSPLIT failed, try cleaning out the TT buffer */
                if (status != -EPIPE
-                               && urb->dev->tt && !usb_pipeint (urb->pipe)
+                               && urb->dev->tt
+                               && !usb_pipeint(urb->pipe)
                                && ((token & QTD_STS_MMF) != 0
                                        || QTD_CERR(token) == 0)
                                && (!ehci_is_TDI(ehci)
@@ -211,6 +212,9 @@ static int qtd_copy_status (
                                urb->dev->ttport, urb->dev->devnum,
                                usb_pipeendpoint (urb->pipe), token);
 #endif /* DEBUG */
+                       /* REVISIT ARC-derived cores don't clear the root
+                        * hub TT buffer in this way...
+                        */
                        usb_hub_tt_clear_buffer (urb->dev, urb->pipe);
                }
        }
@@ -315,10 +319,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        if (likely (last->urb != urb)) {
                                ehci_urb_done(ehci, last->urb, last_status);
                                count++;
+                               last_status = -EINPROGRESS;
                        }
                        ehci_qtd_free (ehci, last);
                        last = NULL;
-                       last_status = -EINPROGRESS;
                }
 
                /* ignore urbs submitted during completions we reported */
@@ -638,6 +642,7 @@ qh_make (
        u32                     info1 = 0, info2 = 0;
        int                     is_input, type;
        int                     maxp = 0;
+       struct usb_tt           *tt = urb->dev->tt;
 
        if (!qh)
                return qh;
@@ -652,6 +657,14 @@ qh_make (
        type = usb_pipetype (urb->pipe);
        maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
 
+       /* 1024 byte maxpacket is a hardware ceiling.  High bandwidth
+        * acts like up to 3KB, but is built from smaller packets.
+        */
+       if (max_packet(maxp) > 1024) {
+               ehci_dbg(ehci, "bogus qh maxpacket %d\n", max_packet(maxp));
+               goto done;
+       }
+
        /* Compute interrupt scheduling parameters just once, and save.
         * - allowing for high bandwidth, how many nsec/uframe are used?
         * - split transactions need a second CSPLIT uframe; same question
@@ -661,8 +674,9 @@ qh_make (
         * For control/bulk requests, the HC or TT handles these.
         */
        if (type == PIPE_INTERRUPT) {
-               qh->usecs = NS_TO_US (usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0,
-                               hb_mult (maxp) * max_packet (maxp)));
+               qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+                               is_input, 0,
+                               hb_mult(maxp) * max_packet(maxp)));
                qh->start = NO_FRAME;
 
                if (urb->dev->speed == USB_SPEED_HIGH) {
@@ -680,7 +694,6 @@ qh_make (
                                goto done;
                        }
                } else {
-                       struct usb_tt   *tt = urb->dev->tt;
                        int             think_time;
 
                        /* gap is f(FS/LS transfer times) */
@@ -736,10 +749,8 @@ qh_make (
                /* set the address of the TT; for TDI's integrated
                 * root hub tt, leave it zeroed.
                 */
-               if (!ehci_is_TDI(ehci)
-                               || urb->dev->tt->hub !=
-                                       ehci_to_hcd(ehci)->self.root_hub)
-                       info2 |= urb->dev->tt->hub->devnum << 16;
+               if (tt && tt->hub != ehci_to_hcd(ehci)->self.root_hub)
+                       info2 |= tt->hub->devnum << 16;
 
                /* NOTE:  if (PIPE_INTERRUPT) { scheduler sets c-mask } */
 
@@ -754,7 +765,13 @@ qh_make (
                        info2 |= (EHCI_TUNE_MULT_HS << 30);
                } else if (type == PIPE_BULK) {
                        info1 |= (EHCI_TUNE_RL_HS << 28);
-                       info1 |= 512 << 16;     /* usb2 fixed maxpacket */
+                       /* The USB spec says that high speed bulk endpoints
+                        * always use 512 byte maxpacket.  But some device
+                        * vendors decided to ignore that, and MSFT is happy
+                        * to help them do so.  So now people expect to use
+                        * such nonconformant devices with Linux too; sigh.
+                        */
+                       info1 |= max_packet(maxp) << 16;
                        info2 |= (EHCI_TUNE_MULT_HS << 30);
                } else {                /* PIPE_INTERRUPT */
                        info1 |= max_packet (maxp) << 16;
@@ -973,7 +990,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
        struct ehci_qh          *qh = ehci->reclaim;
        struct ehci_qh          *next;
 
-       timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+       iaa_watchdog_done(ehci);
 
        // qh->hw_next = cpu_to_hc32(qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
@@ -983,7 +1000,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
        /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
        next = qh->reclaim;
        ehci->reclaim = next;
-       ehci->reclaim_ready = 0;
        qh->reclaim = NULL;
 
        qh_completions (ehci, qh);
@@ -1059,11 +1075,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
                return;
        }
 
-       ehci->reclaim_ready = 0;
        cmd |= CMD_IAAD;
        ehci_writel(ehci, cmd, &ehci->regs->command);
        (void)ehci_readl(ehci, &ehci->regs->command);
-       timer_action (ehci, TIMER_IAA_WATCHDOG);
+       iaa_watchdog_start(ehci);
 }
 
 /*-------------------------------------------------------------------------*/