]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Clear device endpoint halt before resetting host endpoint
authorMichael Brown <mcb30@ipxe.org>
Thu, 2 Jul 2020 01:51:58 +0000 (02:51 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 2 Jul 2020 02:06:50 +0000 (03:06 +0100)
Resetting the host endpoint may immediately restart any pending
transfers for that endpoint.  If the device endpoint halt has not yet
been cleared, then this will probably result in a second failed
transfer.

This second failure may be detected within usb_endpoint_reset() while
waiting for usb_clear_feature() to complete.  The endpoint will
subsequently be removed from the list of halted endpoints, causing the
second failure to be effectively ignored and leaving the host endpoint
in a permanently halted state.

Fix by deferring the host endpoint reset until after the device
endpoint is ready to accept new transfers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/bus/usb.c

index d8db3849acefd384520d2c0dca549d96140292ef..d18751c667cadc477bb4947099a474a00ef1805c 100644 (file)
@@ -405,13 +405,6 @@ static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
        /* Sanity check */
        assert ( ! list_empty ( &ep->halted ) );
 
-       /* Reset endpoint */
-       if ( ( rc = ep->host->reset ( ep ) ) != 0 ) {
-               DBGC ( usb, "USB %s %s could not reset: %s\n",
-                      usb->name, usb_endpoint_name ( ep ), strerror ( rc ) );
-               return rc;
-       }
-
        /* Clear transaction translator, if applicable */
        if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 )
                return rc;
@@ -427,6 +420,13 @@ static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
                return rc;
        }
 
+       /* Reset endpoint */
+       if ( ( rc = ep->host->reset ( ep ) ) != 0 ) {
+               DBGC ( usb, "USB %s %s could not reset: %s\n",
+                      usb->name, usb_endpoint_name ( ep ), strerror ( rc ) );
+               return rc;
+       }
+
        /* Remove from list of halted endpoints */
        list_del ( &ep->halted );
        INIT_LIST_HEAD ( &ep->halted );