On Thu, 25 Jun 2009, Alan Stern wrote: > > Please tell me if any further analysis is required to investigate the > > problem and if it's a hardware problem how to investigate that. > > Like I said in my two previous messages, I need to see the contents of > the "registers" and "periodic" files for the sb_05 controller after the > end of the test. > > Here's something else you can try. After doing the "rmmod ohci-hcd" > do "dmesg -c >/dev/null", and then proceed as usual with the "echo -n > ...". After you stop the headphone recording (and before you stop the > usbmon process or collect the dmesg log), unplug the hub and > immediately plug it back in. Do the keyboard and mouse work then? One other thing. It's possible that the problem is in the GL852 hub; I have heard reports about problems with Genesys Logic's hubs before. The attached patch (for 2.6.27) might help. Alan Stern
Index: 2.6.27.19/drivers/usb/core/hub.h =================================================================== --- 2.6.27.19.orig/drivers/usb/core/hub.h +++ 2.6.27.19/drivers/usb/core/hub.h @@ -185,16 +185,18 @@ struct usb_tt { /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ spinlock_t lock; struct list_head clear_list; /* of usb_tt_clear */ - struct work_struct kevent; + struct work_struct clear_work; }; struct usb_tt_clear { struct list_head clear_list; unsigned tt; u16 devinfo; + struct usb_hcd *hcd; + struct usb_host_endpoint *ep; }; -extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe); +extern int usb_hub_clear_tt_buffer(struct urb *urb); extern void usb_ep0_reinit(struct usb_device *); #endif /* __LINUX_HUB_H */ Index: 2.6.27.19/drivers/usb/core/hcd.h =================================================================== --- 2.6.27.19.orig/drivers/usb/core/hcd.h +++ 2.6.27.19/drivers/usb/core/hcd.h @@ -217,6 +217,10 @@ struct hc_driver { void (*relinquish_port)(struct usb_hcd *, int); /* has a port been handed over to a companion? */ int (*port_handed_over)(struct usb_hcd *, int); + /* CLEAR_TT_BUFFER completion callback */ + void (*clear_tt_buffer_complete)(struct usb_hcd *, + struct usb_host_endpoint *); + }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); Index: 2.6.27.19/drivers/usb/core/hub.c =================================================================== --- 2.6.27.19.orig/drivers/usb/core/hub.c +++ 2.6.27.19/drivers/usb/core/hub.c @@ -436,10 +436,10 @@ hub_clear_tt_buffer (struct usb_device * * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. */ -static void hub_tt_kevent (struct work_struct *work) +static void hub_tt_work(struct work_struct *work) { struct usb_hub *hub = - container_of(work, struct usb_hub, tt.kevent); + container_of(work, struct usb_hub, tt.clear_work); unsigned long flags; int limit = 100; @@ -448,6 +448,7 @@ static void hub_tt_kevent (struct work_s struct list_head *temp; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; + const struct hc_driver *drv; int status; temp = hub->tt.clear_list.next; @@ -457,21 +458,25 @@ static void hub_tt_kevent (struct work_s /* drop lock so HCD can concurrently report other TT errors */ spin_unlock_irqrestore (&hub->tt.lock, flags); status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); - spin_lock_irqsave (&hub->tt.lock, flags); - if (status) dev_err (&hdev->dev, "clear tt %d (%04x) error %d\n", clear->tt, clear->devinfo, status); + + /* Tell the HCD, even if the operation failed */ + drv = clear->hcd->driver; + if (drv->clear_tt_buffer_complete) + (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep); + kfree(clear); + spin_lock_irqsave(&hub->tt.lock, flags); } spin_unlock_irqrestore (&hub->tt.lock, flags); } /** - * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub - * @udev: the device whose split transaction failed - * @pipe: identifies the endpoint of the failed transaction + * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub + * @urb: an URB associated with the failed or incomplete split transaction * * High speed HCDs use this to tell the hub driver that some split control or * bulk transaction failed in a way that requires clearing internal state of @@ -481,8 +486,10 @@ static void hub_tt_kevent (struct work_s * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. */ -void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) +int usb_hub_clear_tt_buffer(struct urb *urb) { + struct usb_device *udev = urb->dev; + int pipe = urb->pipe; struct usb_tt *tt = udev->tt; unsigned long flags; struct usb_tt_clear *clear; @@ -494,7 +501,7 @@ void usb_hub_tt_clear_buffer (struct usb if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ - return; + return -ENOMEM; } /* info that CLEAR_TT_BUFFER needs */ @@ -506,14 +513,19 @@ void usb_hub_tt_clear_buffer (struct usb : (USB_ENDPOINT_XFER_BULK << 11); if (usb_pipein (pipe)) clear->devinfo |= 1 << 15; - + + /* info for completion callback */ + clear->hcd = bus_to_hcd(udev->bus); + clear->ep = urb->ep; + /* tell keventd to clear state for this TT */ spin_lock_irqsave (&tt->lock, flags); list_add_tail (&clear->clear_list, &tt->clear_list); - schedule_work (&tt->kevent); + schedule_work(&tt->clear_work); spin_unlock_irqrestore (&tt->lock, flags); + return 0; } -EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer); static void hub_power_on(struct usb_hub *hub) { @@ -732,7 +744,7 @@ static void hub_quiesce(struct usb_hub * if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); if (hub->tt.hub) - cancel_work_sync(&hub->tt.kevent); + cancel_work_sync(&hub->tt.clear_work); } /* caller has locked the hub device */ @@ -849,7 +861,7 @@ static int hub_configure(struct usb_hub spin_lock_init (&hub->tt.lock); INIT_LIST_HEAD (&hub->tt.clear_list); - INIT_WORK (&hub->tt.kevent, hub_tt_kevent); + INIT_WORK(&hub->tt.clear_work, hub_tt_work); switch (hdev->descriptor.bDeviceProtocol) { case 0: break; Index: 2.6.27.19/drivers/usb/host/ehci-q.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-q.c +++ 2.6.27.19/drivers/usb/host/ehci-q.c @@ -139,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struc /*-------------------------------------------------------------------------*/ +static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_qh *qh = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + qh->clearing_tt = 0; + if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) + && HC_IS_RUNNING(hcd->state)) + qh_link_async(ehci, qh); + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, + struct urb *urb, u32 token) +{ + + /* If an async split transaction gets an error or is unlinked, + * the TT buffer may be left in an indeterminate state. We + * have to clear the TT buffer. + * + * Note: this routine is never called for Isochronous transfers. + */ + if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); +#endif /* DEBUG */ + if (!ehci_is_TDI(ehci) + || urb->dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub) { + if (usb_hub_clear_tt_buffer(urb) == 0) + qh->clearing_tt = 1; + } else { + + /* REVISIT ARC-derived cores don't clear the root + * hub TT buffer in this way... + */ + } + } +} + static int qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, @@ -195,28 +244,6 @@ static int qtd_copy_status ( usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", token, status); - - /* if async CSPLIT failed, try cleaning out the TT buffer */ - if (status != -EPIPE - && urb->dev->tt - && !usb_pipeint(urb->pipe) - && ((token & QTD_STS_MMF) != 0 - || QTD_CERR(token) == 0) - && (!ehci_is_TDI(ehci) - || urb->dev->tt->hub != - ehci_to_hcd(ehci)->self.root_hub)) { -#ifdef DEBUG - struct usb_device *tt = urb->dev->tt->hub; - dev_dbg (&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - 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); - } } return status; @@ -379,9 +406,16 @@ qh_completions (struct ehci_hcd *ehci, s /* qh unlinked; token in overlay may be most current */ if (state == QH_STATE_IDLE && cpu_to_hc32(ehci, qtd->qtd_dma) - == qh->hw_current) + == qh->hw_current) { token = hc32_to_cpu(ehci, qh->hw_token); + /* An unlink may leave an incomplete + * async transaction in the TT buffer. + * We have to clear it. + */ + ehci_clear_tt_buffer(ehci, qh, urb, token); + } + /* force halt for unlinked or blocked qh, so we'll * patch the qh later and so that completions can't * activate it while we "know" it's stopped. @@ -407,6 +441,13 @@ halt: && (qtd->hw_alt_next & EHCI_LIST_END(ehci))) last_status = -EINPROGRESS; + + /* As part of low/full-speed endpoint-halt processing + * we must clear the TT buffer (11.17.5). + */ + if (unlikely(last_status != -EINPROGRESS && + last_status != -EREMOTEIO)) + ehci_clear_tt_buffer(ehci, qh, urb, token); } /* if we're removing something not at the queue head, @@ -833,6 +874,10 @@ static void qh_link_async (struct ehci_h __hc32 dma = QH_NEXT(ehci, qh->qh_dma); struct ehci_qh *head; + /* Don't link a QH if there's a Clear-TT-Buffer pending */ + if (unlikely(qh->clearing_tt)) + return; + /* (re)start the async schedule? */ head = ehci->async; timer_action_done (ehci, TIMER_ASYNC_OFF); Index: 2.6.27.19/drivers/usb/host/ehci.h =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci.h +++ 2.6.27.19/drivers/usb/host/ehci.h @@ -502,7 +502,9 @@ struct ehci_qh { unsigned short period; /* polling interval */ unsigned short start; /* where polling starts */ #define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ + unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ Index: 2.6.27.19/drivers/usb/host/ehci-hcd.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-hcd.c +++ 2.6.27.19/drivers/usb/host/ehci-hcd.c @@ -963,6 +963,8 @@ idle_timeout: schedule_timeout_uninterruptible(1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ + if (qh->clearing_tt) + goto idle_timeout; if (list_empty (&qh->qtd_list)) { qh_put (qh); break; Index: 2.6.27.19/drivers/usb/host/ehci-au1xxx.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-au1xxx.c +++ 2.6.27.19/drivers/usb/host/ehci-au1xxx.c @@ -112,6 +112,8 @@ static const struct hc_driver ehci_au1xx .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) Index: 2.6.27.19/drivers/usb/host/ehci-fsl.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-fsl.c +++ 2.6.27.19/drivers/usb/host/ehci-fsl.c @@ -324,6 +324,8 @@ static const struct hc_driver ehci_fsl_h .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_fsl_drv_probe(struct platform_device *pdev) Index: 2.6.27.19/drivers/usb/host/ehci-ixp4xx.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-ixp4xx.c +++ 2.6.27.19/drivers/usb/host/ehci-ixp4xx.c @@ -60,6 +60,8 @@ static const struct hc_driver ixp4xx_ehc #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ixp4xx_ehci_probe(struct platform_device *pdev) Index: 2.6.27.19/drivers/usb/host/ehci-orion.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-orion.c +++ 2.6.27.19/drivers/usb/host/ehci-orion.c @@ -163,6 +163,8 @@ static const struct hc_driver ehci_orion .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static void __init Index: 2.6.27.19/drivers/usb/host/ehci-pci.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-pci.c +++ 2.6.27.19/drivers/usb/host/ehci-pci.c @@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_h .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; /*-------------------------------------------------------------------------*/ Index: 2.6.27.19/drivers/usb/host/ehci-ppc-of.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-ppc-of.c +++ 2.6.27.19/drivers/usb/host/ehci-ppc-of.c @@ -78,6 +78,8 @@ static const struct hc_driver ehci_ppc_o #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; Index: 2.6.27.19/drivers/usb/host/ehci-ps3.c =================================================================== --- 2.6.27.19.orig/drivers/usb/host/ehci-ps3.c +++ 2.6.27.19/drivers/usb/host/ehci-ps3.c @@ -74,6 +74,8 @@ static const struct hc_driver ps3_ehci_h #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ps3_ehci_probe(struct ps3_system_bus_device *dev)