diff options
author | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2008-05-19 14:05:44 +0000 |
---|---|---|
committer | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2008-05-19 14:05:44 +0000 |
commit | bc731f83b4b63b2d161048775b218d6d93597932 (patch) | |
tree | 934a94e8586f3d29b733c5f4bffcef44ed02271e /sys/dev | |
parent | 87d9a20d613126cffddb43984b9067d7e459a2c0 (diff) |
try a few times before giving up while getting the device descriptor. if
it fails, reset the port and try again. this seems to fix some devices that
would punt with "device problem, disabling port" otherwise.
from augustss@netbsd with some change as per discussion with theo.
set the address before getting the descriptor. from peterbu at bemac dot com
via netbsd kern/24716.
tested by many.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/usb_subr.c | 49 |
1 files changed, 30 insertions, 19 deletions
diff --git a/sys/dev/usb/usb_subr.c b/sys/dev/usb/usb_subr.c index cda20113d4a..928907f945f 100644 --- a/sys/dev/usb/usb_subr.c +++ b/sys/dev/usb/usb_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: usb_subr.c,v 1.64 2007/11/04 05:52:36 deraadt Exp $ */ +/* $OpenBSD: usb_subr.c,v 1.65 2008/05/19 14:05:43 fgsch Exp $ */ /* $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.18 1999/11/17 22:33:47 n_hibma Exp $ */ @@ -979,6 +979,7 @@ usbd_new_device(struct device *parent, usbd_bus_handle bus, int depth, usbd_device_handle dev, adev; struct usbd_device *hub; usb_device_descriptor_t *dd; + usb_port_status_t ps; usbd_status err; int addr; int i; @@ -1049,15 +1050,40 @@ usbd_new_device(struct device *parent, usbd_bus_handle bus, int depth, return (err); } + /* Set the address. Do this early; some devices need that. */ + /* 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; + if ((i % 4) == 0) + usbd_reset_port(up->parent, port, &ps); + else + usbd_delay_ms(dev, 200); + } + if (err) { + DPRINTFN(-1,("usbd_new_device: set address %d failed\n", addr)); + err = USBD_SET_ADDR_FAILED; + 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 */ + bus->devices[addr] = dev; + dd = &dev->ddesc; /* Try a few times in case the device is slow (i.e. outside specs.) */ - for (i = 0; i < 5; i++) { + for (i = 0; i < 15; i++) { /* Get the first 8 bytes of the device descriptor. */ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); if (!err) break; - /* progressively increase the delay */ - usbd_delay_ms(dev, 200 * (i + 1)); + if ((i % 4) == 0) + usbd_reset_port(up->parent, port, &ps); + else + usbd_delay_ms(dev, 200); } if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc " @@ -1107,21 +1133,6 @@ usbd_new_device(struct device *parent, usbd_bus_handle bus, int depth, return (err); } - /* Set the address */ - DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); - err = usbd_set_address(dev, addr); - if (err) { - DPRINTFN(-1,("usbd_new_device: set address %d failed\n", addr)); - err = USBD_SET_ADDR_FAILED; - 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 */ - bus->devices[addr] = dev; - /* Assume 100mA bus powered for now. Changed when configured. */ dev->power = USB_MIN_POWER; dev->self_powered = 0; |