From xiphmont@gmail.com Thu Sep 28 00:04:07 2006 Message-ID: <806dafc20609280004y7969fdf4n9194eb0076275cda@mail.gmail.com> Date: Thu, 28 Sep 2006 03:04:03 -0400 From: "Christopher \"Monty\" Montgomery" To: linux-usb-devel@lists.sourceforge.net Subject: [PATCH 3/15] USB: ehci-hcd: group interrupt endpoint code into one place Cc: greg@kroah.com, david-b@pacbell.net, xiphmont@gmail.com Content-Disposition: inline patch 3: moves scattered interrupt endpoint (periodic QH) code to one place. Also eliminates some vestigal patterning after the async QH code. There should be no functional difference after this patch. Signed-off-by: Christopher "Monty" Montgomery Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 4 drivers/usb/host/ehci-q.c | 10 + drivers/usb/host/ehci-sched.c | 233 ++++++++++++++++++++++++------------------ 3 files changed, 145 insertions(+), 102 deletions(-) --- gregkh-2.6.orig/drivers/usb/host/ehci-hcd.c +++ gregkh-2.6/drivers/usb/host/ehci-hcd.c @@ -761,7 +761,7 @@ static int ehci_urb_dequeue (struct usb_ break; switch (qh->qh_state) { case QH_STATE_LINKED: - intr_deschedule (ehci, qh); + periodic_qh_deschedule (ehci, qh); /* FALL THROUGH */ case QH_STATE_IDLE: qh_completions (ehci, qh); @@ -777,7 +777,7 @@ static int ehci_urb_dequeue (struct usb_ && HC_IS_RUNNING (hcd->state)) { int status; - status = qh_schedule (ehci, qh); + status = periodic_qh_schedule (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); if (status != 0) { --- gregkh-2.6.orig/drivers/usb/host/ehci-q.c +++ gregkh-2.6/drivers/usb/host/ehci-q.c @@ -269,8 +269,8 @@ __acquires(ehci->lock) static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh); -static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh); -static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); +static void periodic_qh_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh); +static int periodic_qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); /* * Process and free completed qtds for a qh, returning URBs to drivers. @@ -430,8 +430,10 @@ halt: */ if ((__constant_cpu_to_le32 (QH_SMASK) & qh->hw_info2) != 0) { - intr_deschedule (ehci, qh); - (void) qh_schedule (ehci, qh); + + periodic_qh_deschedule (ehci, qh); + periodic_qh_schedule (ehci, qh); + } else unlink_async (ehci, qh); break; --- gregkh-2.6.orig/drivers/usb/host/ehci-sched.c +++ gregkh-2.6/drivers/usb/host/ehci-sched.c @@ -67,28 +67,33 @@ periodic_next_shadow (union ehci_shadow } } -/* caller must hold ehci->lock */ -static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) -{ - union ehci_shadow *prev_p = &ehci->pshadow [frame]; - __le32 *hw_p = &ehci->periodic [frame]; - union ehci_shadow here = *prev_p; - - /* find predecessor of "ptr"; hw and shadow lists are in sync */ - while (here.ptr && here.ptr != ptr) { - prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p)); - hw_p = here.hw_next; - here = *prev_p; +/* find position of a specific entry in the periodic schedule (ie, + * returns pointers such that we can update the predecessor's + * linkage); here->ptr == NULL indicates the find failed. + * + * @ehci: pointer to ehci host controller device structure. + * @frame: frame number of the shadow/hardware schedule to search + * @ptr: entry for which to search + * @hw_p: return pointer to hw_next field of preceeding entry + */ +static union ehci_shadow * +periodic_find_entry(struct ehci_hcd *ehci, + unsigned frame, + void *ptr, + __le32 **hw_p) +{ + union ehci_shadow *here = &ehci->pshadow [frame % ehci->periodic_size]; + __le32 type; + + *hw_p = &ehci->periodic [frame % ehci->periodic_size]; + type = Q_NEXT_TYPE (**hw_p); + + while (here->ptr && here->ptr != ptr) { + *hw_p = here->hw_next; + here = periodic_next_shadow (here, type); + type = Q_NEXT_TYPE (**hw_p); } - /* an interrupt entry (at list end) could have been shared */ - if (!here.ptr) - return; - - /* update shadow and hardware lists ... the old "next" pointers - * from ptr may still be in use, the caller updates them. - */ - *prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); - *hw_p = *here.hw_next; + return here; } /* how many of the uframe's 125 usecs are allocated? */ @@ -535,14 +540,106 @@ static int disable_periodic (struct ehci } /*-------------------------------------------------------------------------*/ +/* Periodic interrupt (QH) endpoint machinery */ -/* periodic schedule slots have iso tds (normal or split) first, then a - * sparse tree for active interrupt transfers. +/* periodic_qh_unlink_frame - unlink the passed in QH from the + * specified frame * - * this just links in a qh; caller guarantees uframe masks are set right. - * no FSTN support (yet; ehci 0.96+) + * @ehci: pointer to ehci host controller device structure + * @frame: frame number in shadow/hardware schedule + * @qh: QH to unlink */ -static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) +static void periodic_qh_unlink_frame (struct ehci_hcd *ehci, + unsigned frame, + struct ehci_qh *qh) +{ + __le32 *hw_p; + union ehci_shadow *here = periodic_find_entry(ehci, frame, qh, &hw_p); + + if (here->ptr){ + + *hw_p = *here->hw_next; + *here = here->qh->qh_next; + wmb (); + + /*... the old "next" pointers from ptr (and if a qh, the + fstns) may still be in use, the caller updates them. */ + + } + +} + +/* periodic_qh_deschedule - toplevel entry point for removing a given + * interrupt endpoint (rather, its QH) from the shadow/hardware + * schedule. + * + * @ehci: pointer to ehci host controller device structure + * @qh: QH to remove + */ +static void periodic_qh_deschedule(struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + unsigned i; + unsigned wait; + + // FIXME: + // IF this isn't high speed + // and this qh is active in the current uframe + // (and overlay token SplitXstate is false?) + // THEN + // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */); + + if(sched_verbose) + ehci_info(ehci, "Removing QH %p from schedule:\n", qh); + + /* remove from hardware schedule */ + for (i=0; i < ehci->periodic_size; i++) + periodic_qh_unlink_frame (ehci, i, qh); + + /* update per-qh bandwidth for usbfs */ + ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg (&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = NULL; + qh_put (qh); + + /* maybe turn off periodic schedule */ + deref_periodic (ehci); + + /* simple/paranoid: always delay, expecting the HC needs to read + * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and + * expect khubd to clean up after any CSPLITs we won't issue. + * active high speed queues may need bigger delays... + */ + if (list_empty (&qh->qtd_list) + || (__constant_cpu_to_le32 (QH_CMASK) + & qh->hw_info2) != 0) + wait = 2; + else + wait = 55; /* worst case: 3 * 1024 */ + + udelay (wait); + qh->qh_state = QH_STATE_IDLE; + qh->hw_next = EHCI_LIST_END; + + wmb (); +} + +/* periodic_qh_link - link the passed in QH into all relevant frames of the + * hardware schedule; assumes QH is already budgeted. + * + * @ehci: pointer to ehci host controller device structure + * @qh: QH to link + */ +static int periodic_qh_link (struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; unsigned period = qh->period; @@ -604,69 +701,6 @@ static int qh_link_periodic (struct ehci return enable_periodic (ehci); } -static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - unsigned i; - unsigned period; - - // FIXME: - // IF this isn't high speed - // and this qh is active in the current uframe - // (and overlay token SplitXstate is false?) - // THEN - // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */); - - /* high bandwidth, or otherwise part of every microframe */ - if ((period = qh->period) == 0) - period = 1; - - for (i = qh->start; i < ehci->periodic_size; i += period) - periodic_unlink (ehci, i, qh); - - /* update per-qh bandwidth for usbfs */ - ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); - - dev_dbg (&qh->dev->dev, - "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, - le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); - - /* qh->qh_next still "live" to HC */ - qh->qh_state = QH_STATE_UNLINK; - qh->qh_next.ptr = NULL; - qh_put (qh); - - /* maybe turn off periodic schedule */ - deref_periodic (ehci); -} - -static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) -{ - unsigned wait; - - qh_unlink_periodic (ehci, qh); - - /* simple/paranoid: always delay, expecting the HC needs to read - * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and - * expect khubd to clean up after any CSPLITs we won't issue. - * active high speed queues may need bigger delays... - */ - if (list_empty (&qh->qtd_list) - || (__constant_cpu_to_le32 (QH_CMASK) - & qh->hw_info2) != 0) - wait = 2; - else - wait = 55; /* worst case: 3 * 1024 */ - - udelay (wait); - qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END; - wmb (); -} - /*-------------------------------------------------------------------------*/ static int check_period ( @@ -780,10 +814,7 @@ done: return retval; } -/* "first fit" scheduling policy used the first time through, - * or when the previous schedule slot can't be re-used. - */ -static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +static int periodic_qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) { int status; unsigned uframe; @@ -841,11 +872,21 @@ static int qh_schedule (struct ehci_hcd ehci_dbg (ehci, "reused qh %p schedule\n", qh); /* stuff into the periodic schedule */ - status = qh_link_periodic (ehci, qh); + status = periodic_qh_link (ehci, qh); done: return status; } +/* intr_submit - toplevel entry point for submitting an interrupt + * endpoint request. If needed, performs initial creation, budgeting + * and scheduling of a QH for this endpoint. + * + * @ehci: + * @ep: + * @urb: + * @qtd_list: + * @mem_flags + */ static int intr_submit ( struct ehci_hcd *ehci, struct usb_host_endpoint *ep, @@ -878,7 +919,7 @@ static int intr_submit ( goto done; } if (qh->qh_state == QH_STATE_IDLE) { - if ((status = qh_schedule (ehci, qh)) != 0) + if ((status = periodic_qh_schedule (ehci, qh)) != 0) goto done; } @@ -2193,7 +2234,7 @@ restart: q = q.qh->qh_next; modified = qh_completions (ehci, temp.qh); if (unlikely (list_empty (&temp.qh->qtd_list))) - intr_deschedule (ehci, temp.qh); + periodic_qh_deschedule (ehci, temp.qh); qh_put (temp.qh); break; case Q_TYPE_FSTN: