Subject: kern/36459: uhub_explore failure with slow device
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <mlelstv@serpens.de>
List: netbsd-bugs
Date: 06/08/2007 17:40:00
>Number:         36459
>Category:       kern
>Synopsis:       uhub_explore failure with slow device
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jun 08 17:40:00 +0000 2007
>Originator:     Michael van Elst
>Release:        NetBSD 4.0_BETA2
>Organization:
-- 
                                Michael van Elst
Internet: mlelstv@serpens.de
                                "A potential Snark may lurk in every tree."
>Environment:
	
	
System: NetBSD henery 4.0_BETA2 NetBSD 4.0_BETA2 (HENERY) #1: Sun Jun 3 12:09:36 CEST 2007 mlelstv@henery:/home/netbsd4/obj/home/netbsd4/src/sys/arch/i386/compile/HENERY i386
Architecture: i386
Machine: i386
>Description:
When testing a Fusion 3G card (cardbus UMTS/GPRS/WLAN) I got errors
from uhub_explore:

usb_new_device: set address 2 failed

uhub_explore: usb_new_device failed, error=SET_ADDR_FAILED
uhub5: device problem, disabling port 1

Apparently the device is too slow. FreeBSD does a few retries
in this case.

>How-To-Repeat:
Try to use the Fusion 3G card with NetBSD4.

>Fix:
After merging the relevant code from FreeBSD the card works fine.
Here is the patch:

Index: usb_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.146
diff -u -r1.146 usb_subr.c
--- usb_subr.c	30 Mar 2007 16:52:12 -0000	1.146
+++ usb_subr.c	15 Apr 2007 10:56:13 -0000
@@ -1044,8 +1044,19 @@
 	}
 
 	/* Set the address.  Do this early; some devices need that. */
-	err = usbd_set_address(dev, addr);
+	/* Try a few times in case the device is slow (i.e. outside specs) */
 	DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
+	for (i = 0; i < 15; i++) {
+		err = usbd_set_address(dev, addr);
+		if (!err)
+			break;
+		usbd_delay_ms(dev, 200);
+		if ((i % 3) == 3) {
+			DPRINTFN(-1,("usbd_new_device: set address %d "
+			    "failed - trying a port reset\n", addr));
+			usbd_reset_port(up->parent, port, &ps);
+		}
+	}
 	if (err) {
 		DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr));
 		err = USBD_SET_ADDR_FAILED;

>Unformatted: