Patch is against kernels 2.6.18 through at least 2.6.18-git7
patch 1: This patch slightly refactors isoch stream cleanup such that
stream state is more persistent; it is instantiated at first transfer
and not released until endpoint shutdown. This is to isoch transfers
something persistent to associate with bandwidth budget reservations
later.
Signed-off-by: Christopher "Monty" Montgomery <[EMAIL PROTECTED]>
---
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci.h
b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h 2006-08-07 00:18:54.000000000 -0400
+++ b/drivers/usb/host/ehci.h 2006-09-26 22:06:01.000000000 -0400
@@ -604,6 +604,14 @@ struct ehci_fstn {
/*-------------------------------------------------------------------------*/
+#define EHCI_EP_QH(x) (((x)->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \
+ != USB_ENDPOINT_XFER_ISOC ? (x)->hcpriv : NULL)
+#define EHCI_EP_STREAM(x) (((x)->desc.bmAttributes & \
+ USB_ENDPOINT_XFERTYPE_MASK) == \
+ USB_ENDPOINT_XFER_ISOC ? (x)->hcpriv : NULL)
+
+/*-------------------------------------------------------------------------*/
+
#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
/*
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-hcd.c
b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c 2006-08-09 22:12:43.000000000 -0400
+++ b/drivers/usb/host/ehci-hcd.c 2006-09-26 22:06:01.000000000 -0400
@@ -815,62 +815,79 @@ ehci_endpoint_disable (struct usb_hcd *h
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
unsigned long flags;
struct ehci_qh *qh, *tmp;
+ struct ehci_iso_stream *iso;
/* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */
-rescan:
spin_lock_irqsave (&ehci->lock, flags);
- qh = ep->hcpriv;
- if (!qh)
- goto done;
+ iso = EHCI_EP_STREAM(ep);
- /* endpoints can be iso streams. for now, we don't
- * accelerate iso completions ... so spin a while.
- */
- if (qh->hw_info1 == 0) {
- ehci_vdbg (ehci, "iso delay\n");
- goto idle_timeout;
- }
+ if (iso){
+ /* for now, we don't accelerate iso completions ... so spin
+ a while. */
+
+ while(iso->refcount>1){
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irqsave (&ehci->lock, flags);
+ }
- if (!HC_IS_RUNNING (hcd->state))
- qh->qh_state = QH_STATE_IDLE;
- switch (qh->qh_state) {
- case QH_STATE_LINKED:
- for (tmp = ehci->async->qh_next.qh;
- tmp && tmp != qh;
- tmp = tmp->qh_next.qh)
- continue;
- /* periodic qh self-unlinks on empty */
- if (!tmp)
- goto nogood;
- unlink_async (ehci, qh);
- /* FALL THROUGH */
- case QH_STATE_UNLINK: /* wait for hw to finish? */
-idle_timeout:
+ /* we want to be sure completions deref the stream to 1,
+ then we finally pull the plug here */
+ iso_stream_put(ehci,iso);
+ ep->hcpriv = NULL;
spin_unlock_irqrestore (&ehci->lock, flags);
- schedule_timeout_uninterruptible(1);
- goto rescan;
- case QH_STATE_IDLE: /* fully unlinked */
- if (list_empty (&qh->qtd_list)) {
- qh_put (qh);
+ return;
+ }
+
+ while ( (qh = EHCI_EP_QH(ep)) ){
+
+ if (!HC_IS_RUNNING (hcd->state))
+ qh->qh_state = QH_STATE_IDLE;
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ for (tmp = ehci->async->qh_next.qh;
+ tmp && tmp != qh;
+ tmp = tmp->qh_next.qh)
+ continue;
+
+ /* periodic qh self-unlinks on empty */
+ if (!tmp) goto error;
+ unlink_async (ehci, qh);
+ /* FALL THROUGH */
+
+ case QH_STATE_UNLINK: /* wait for hw to finish? */
+
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irqsave (&ehci->lock, flags);
break;
+
+ case QH_STATE_IDLE: /* fully unlinked */
+
+ if (list_empty (&qh->qtd_list)) {
+ qh_put (qh);
+ ep->hcpriv = NULL;
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ goto error;
}
- /* else FALL THROUGH */
- default:
-nogood:
- /* caller was supposed to have unlinked any requests;
- * that's not our job. just leak this memory.
- */
- ehci_err (ehci, "qh %p (#%02x) state %d%s\n",
- qh, ep->desc.bEndpointAddress, qh->qh_state,
- list_empty (&qh->qtd_list) ? "" : "(has tds)");
- break;
}
- ep->hcpriv = NULL;
-done:
+
spin_unlock_irqrestore (&ehci->lock, flags);
return;
+
+error:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. just leak this memory.
+ */
+ ehci_err (ehci, "qh %p (#%02x) state %d%s\n",
+ qh, ep->desc.bEndpointAddress, qh->qh_state,
+ list_empty (&qh->qtd_list) ? "" : "(has tds)");
+ ep->hcpriv = NULL;
}
static int ehci_get_frame (struct usb_hcd *hcd)
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-sched.c
b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c 2006-08-09 22:12:43.000000000 -0400
+++ b/drivers/usb/host/ehci-sched.c 2006-09-26 22:06:01.000000000 -0400
@@ -959,10 +959,14 @@ iso_stream_put(struct ehci_hcd *ehci, st
{
stream->refcount--;
- /* free whenever just a dev->ep reference remains.
- * not like a QH -- no persistent state (toggle, halt)
- */
- if (stream->refcount == 1) {
+ /* don't free on last descriptor; free when endpoint disable
+ finally releases last refcount. Although it is technically
+ broken for an endpoint driver to submit its streaming
+ descriptors such that a new one appears after the old one
+ ends, it is only punishing the users to insist on breaking
+ these drivers when it's not necessary to do so. This saves
+ substantial overhead in that case.*/
+ if (stream->refcount == 0) {
int is_in;
// BUG_ON (!list_empty(&stream->td_list));
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel