Subject: kern/24716: Multitech usb ISDN TA (MTA128ST-USB) gives 'device error' when plugged in
To: None <>
From: None <>
List: netbsd-bugs
Date: 03/09/2004 13:20:19
>Number:         24716
>Category:       kern
>Synopsis:       Multitech usb ISDN TA (MTA128ST-USB) gives 'device error' when plugged in
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Mar 09 13:21:00 UTC 2004
>Originator:     peter burnett
>Release:        1.6.1
Barron McCann
On plugging in the TA, the usb code reads the first 8 bytes of the device descriptor which informs it that the maxpacket for the default control pipe is 64. 
So the code sets maxpacket in the endpoint to 64 and tries to read the full device descriptor (18 bytes). This gives an underrun because the TA is using a packet size of 8. In fact the TA continues using a packet size of 8 until it gets the SET ADDRESS command and then it starts using its stated value of 64.

I tried several workarounds.
1. I left the maxpacket at 8 in the endpoint. This got further but failed with OVERRUNS after the SET ADDRESS because the device was now using maxpacket=64
2. I delayed setting maxpacket in the endpoint until after the SET ADDRESS. This works fine with the TA but presumably may not work with other USB devices.
3. I moved the code which does the SET ADDRESS to be before the device descriptor is read. This works fine with the TA and should work ok with other usb devices I think.  

Plug an MTA128ST-USb into any system I think.
In usb_subr.c in proc usbd_new_device I moved the section of code copied below:
	/* Set the address */
	err = usbd_set_address(dev, addr);
	DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr));
	if (err) {
		DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr));
		usbd_remove_device(dev, up);
		return (err);
	/* Allow device time to set new address */
	usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE);

	dev->address = addr;	/* New device address now */

to further up above this bit:
	up->device = dev;
	dd = &dev->ddesc;
	/* Try a few times in case the device is slow (i.e. outside specs.) */
	for (i = 0; i < 3; i++) {
		/* Get the first 8 bytes of the device descriptor. */
		err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd);

I had a look at the Linux device driver and noticed that it does the SET ADDRESS before reading the device descriptor so I am hopeful that this is the best solution.