On Wed, 13 Feb 2002, Greg KH wrote: > > Yes, I do totally agree with David: there is absolutely _no_ debounce > > interval (connect-detect-to-port-reset) implemented in 2.4.18-pre9 hub.c > > Good catch. I'll send out a patch in a bit to fix this.
Me too - see below > > struct usb_port_status - which becomes the transfer_buffer for the > > usb_control_msg) call. Mapping stack locations for PCI-DMA however is > > Bleah, yes this should be fixed. Patches _gladly_ accepted for this > problem. Hi, the attached patch below addresses both problems: * usb_hub_port_debounce() provides the required debounce delay including the "restart delay when spurious disconnect detected" behaviour described in 7.1.7.1. However, I've used a minimum delay of 200ms (instead TATTDB > 100ms) as this turned out to be needed to solve the "not accepting new address" problem relyably for my usb-irda dongle. I've kept the additional 400ms delay for low speed device as I wasn't able to test whether this issue is still there. Did some testing with both the usb-irda and the EZ-USB FX dev. board, where the latter one turned out to be a pretty good trigger for connect/ disconnect races on hub-port, when one starts repeatedly pressing the reset/disconnect button - Looks good for me now! * usb_hub_port_status() was introduced to wrap the calls for usb_get_port_status and uses a kmalloced temporary buffer instead of exposing stack locations to PCI-DMA. Had it included during the debounce tests and it work as expected for me. Patch created and tested with 2.4.18-pre9. Martin ---------------------- --- linux-2.4.18-pre9/drivers/usb/hub.c Wed Nov 28 15:16:48 2001 +++ v2.4.18-pre9-md/drivers/usb/hub.c Thu Feb 14 01:49:34 2002 @@ -496,6 +496,29 @@ err("cannot disconnect hub %d", dev->devnum); } +static int usb_hub_port_status(struct usb_device *hub, int port, + unsigned short *status, unsigned short *change) +{ + struct usb_port_status *portsts; + int ret = -1; + + portsts = kmalloc(sizeof(portsts), GFP_KERNEL); + if (portsts) { + ret = usb_get_port_status(hub, port + 1, portsts); + if (ret < 0) + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + else { + *status = le16_to_cpu(portsts->wPortStatus); + *change = le16_to_cpu(portsts->wPortChange); + dbg("port %d, portstatus %x, change %x, %s", port + 1, + *status, *change, portspeed(*status)); + ret = 0; + } + kfree(portsts); + } + return ret; +} + #define HUB_RESET_TRIES 5 #define HUB_PROBE_TRIES 2 #define HUB_SHORT_RESET_TIME 10 @@ -507,7 +530,6 @@ struct usb_device *dev, unsigned int delay) { int delay_time, ret; - struct usb_port_status portsts; unsigned short portchange, portstatus; for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { @@ -515,17 +537,11 @@ wait_ms(delay); /* read and decode port status */ - ret = usb_get_port_status(hub, port + 1, &portsts); + ret = usb_hub_port_status(hub, port, &portstatus, &portchange); if (ret < 0) { - err("get_port_status(%d) failed (err = %d)", port + 1, ret); return -1; } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - dbg("port %d, portstatus %x, change %x, %s", port + 1, - portstatus, portchange, portspeed (portstatus)); - /* bomb out completely if something weird happened */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) return -1; @@ -592,17 +608,49 @@ port + 1, hub->devnum, ret); } +#define HUB_DEBOUNCE_TIMEOUT 200 /* 7.1.7.1: min 100ms - restart if disconnect +*/ +#define HUB_DEBOUNCE_STEP 10 /* interval when to check for spurious +disconnect */ + +/* return: -1 on error, 0 on success, 1 on disconnect. */ +static int usb_hub_port_debounce(struct usb_device *hub, int port) +{ + int delay_time, ret, bogus_discon; + unsigned short portchange, portstatus; + + bogus_discon = 0; + for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; /* empty */ ) { + + /* wait for debounce and to give the device stable buspower */ + + wait_ms(HUB_DEBOUNCE_STEP); + + ret = usb_hub_port_status(hub, port, &portstatus, &portchange); + if (ret < 0) + return -1; + + if (!(portstatus & USB_PORT_STAT_CONNECTION)) { + delay_time = 0; + if (++bogus_discon > 3) { /* 3 discon in a row are taken +serious */ + info("port %d: disconnect detected during +connect-debounce", port+1); + return 1; + } + } + else { + delay_time += HUB_DEBOUNCE_STEP; + bogus_discon = 0; + } + } + return 0; +} + static void usb_hub_port_connect_change(struct usb_device *hub, int port, - struct usb_port_status *portsts) + unsigned char portstatus, unsigned char portchange) { struct usb_device *dev; - unsigned short portstatus, portchange; unsigned int delay = HUB_SHORT_RESET_TIME; int i; char *portstr, *tempstr; - portstatus = le16_to_cpu(portsts->wPortStatus); - portchange = le16_to_cpu(portsts->wPortChange); dbg("port %d, portstatus %x, change %x, %s", port + 1, portstatus, portchange, portspeed (portstatus)); @@ -621,6 +669,12 @@ return; } + if (usb_hub_port_debounce(hub, port)) { + err("connect-debounce failed, port %d disabled", port+1); + usb_hub_port_disable(hub, port); + return; + } + /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (portstatus & USB_PORT_STAT_LOW_SPEED) { @@ -751,22 +805,18 @@ } for (i = 0; i < hub->descriptor->bNbrPorts; i++) { - struct usb_port_status portsts; unsigned short portstatus, portchange; - ret = usb_get_port_status(dev, i + 1, &portsts); + ret = usb_hub_port_status(dev, i, &portstatus, &portchange); if (ret < 0) { err("get_port_status failed (err = %d)", ret); continue; } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - if (portchange & USB_PORT_STAT_C_CONNECTION) { dbg("port %d connection change", i + 1); - usb_hub_port_connect_change(dev, i, &portsts); + usb_hub_port_connect_change(dev, i, portstatus, +portchange); } else if (portchange & USB_PORT_STAT_C_ENABLE) { dbg("port %d enable change, status %x", i + 1, portstatus); usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); @@ -780,7 +830,7 @@ (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { err("already running port %i disabled by hub (EMI?), re-enabling...", i + 1); - usb_hub_port_connect_change(dev, i, &portsts); + usb_hub_port_connect_change(dev, i, +portstatus, portchange); } } _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel