]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Always clear recorded disconnections after performing hotplug actions
authorMichael Brown <mcb30@ipxe.org>
Wed, 13 May 2015 13:45:21 +0000 (14:45 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 13 May 2015 14:01:07 +0000 (15:01 +0100)
The recorded disconnections (in port->disconnected) will currently be
left uncleared if usb_attached() returns an error (e.g. because there
are no drivers for a particular USB device).  This is incorrect
behaviour: the disconnection has been handled and the record should be
cleared until the next physical disconnection is detected (via the CSC
bit).

The problem is masked for EHCI, UHCI, and USB hubs, since these will
report a changed port (via usb_port_changed()) only when the
underlying hardware reports a change.  xHCI will call
usb_port_changed() in response to any port status event, at which
point the stale value of port->disconnected will be erroneously acted
upon.  This can lead to an endless loop of repeatedly enumerating the
same device when a driverless device is attached to an xHCI root hub
port.

Fix by unconditionally clearing port->disconnected in usb_hotplugged().

Reported-by: Robin Smidsrød <robin@smidsrod.no>
Tested-by: Robin Smidsrød <robin@smidsrod.no>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/bus/usb.c

index 7574aaa1da28648dd7c0996ab63a1dd317d1f7fc..2019e3341dfbf97ae0aae9e1a3f7e70debdbe63e 100644 (file)
@@ -1591,7 +1591,7 @@ static int usb_hotplugged ( struct usb_port *port ) {
        if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) {
                DBGC ( hub, "USB hub %s port %d could not get speed: %s\n",
                       hub->name, port->address, strerror ( rc ) );
-               return rc;
+               goto err_speed;
        }
 
        /* Detach device, if applicable */
@@ -1599,15 +1599,15 @@ static int usb_hotplugged ( struct usb_port *port ) {
                usb_detached ( port );
 
        /* Attach device, if applicable */
-       if ( port->speed && ! port->attached ) {
-               if ( ( rc = usb_attached ( port ) ) != 0 )
-                       return rc;
-       }
+       if ( port->speed && ( ! port->attached ) &&
+            ( ( rc = usb_attached ( port ) ) != 0 ) )
+               goto err_attached;
 
+ err_attached:
+ err_speed:
        /* Clear any recorded disconnections */
        port->disconnected = 0;
-
-       return 0;
+       return rc;
 }
 
 /******************************************************************************