Re: [Bugme-new] [Bug 13304] New: ehci_hcd module causing problems in using usb head phone

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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)

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux