aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2020-10-12 15:21:25 +0100
committerMichael Brown <mcb30@ipxe.org>2020-10-12 15:21:25 +0100
commitebf01660810c4ff923754323049d49d37321fd49 (patch)
treea86a91d751ccee4e0d5c01d8581abfcb98f6aae5
parentc70b3e04e86cefca335e36f883829d89583a6921 (diff)
downloadipxe-ebf01660810c4ff923754323049d49d37321fd49.zip
ipxe-ebf01660810c4ff923754323049d49d37321fd49.tar.gz
ipxe-ebf01660810c4ff923754323049d49d37321fd49.tar.bz2
[usb] Allow device halt to be cleared independently of host controller
Closing and reopening a USB endpoint will clear any halt status recorded by the host controller, but may leave the endpoint halted at the device. This will cause the first packet submitted to the reopened endpoint to be lost, before the automatic stall recovery mechanism detects the halt and resets the endpoint. This is relatively harmless for USB network or HID devices, since the wire protocols will recover gracefully from dropped packets. Some protocols (e.g. for USB mass storage devices) assume zero packet loss and so would be adversely affected. Fix by allowing any device endpoint halt status to be cleared on a freshly opened endpoint. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/drivers/bus/usb.c45
-rw-r--r--src/include/ipxe/usb.h1
2 files changed, 32 insertions, 14 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c
index 14eabb6..88df9c6 100644
--- a/src/drivers/bus/usb.c
+++ b/src/drivers/bus/usb.c
@@ -363,6 +363,35 @@ static int usb_endpoint_clear_tt ( struct usb_endpoint *ep ) {
}
/**
+ * Clear endpoint halt (if applicable)
+ *
+ * @v ep USB endpoint
+ * @ret rc Return status code
+ */
+int usb_endpoint_clear_halt ( struct usb_endpoint *ep ) {
+ struct usb_device *usb = ep->usb;
+ unsigned int type;
+ int rc;
+
+ /* Clear transaction translator, if applicable */
+ if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 )
+ return rc;
+
+ /* Clear endpoint halt (if applicable) */
+ type = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
+ if ( ( type != USB_ENDPOINT_ATTR_CONTROL ) &&
+ ( ( rc = usb_clear_feature ( usb, USB_RECIP_ENDPOINT,
+ USB_ENDPOINT_HALT,
+ ep->address ) ) != 0 ) ) {
+ DBGC ( usb, "USB %s %s could not clear endpoint halt: %s\n",
+ usb->name, usb_endpoint_name ( ep ), strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
* Close USB endpoint
*
* @v ep USB endpoint
@@ -399,27 +428,15 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
*/
static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
struct usb_device *usb = ep->usb;
- unsigned int type;
int rc;
/* Sanity check */
assert ( ! list_empty ( &ep->halted ) );
- /* Clear transaction translator, if applicable */
- if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 )
+ /* Clear device halt, if applicable */
+ if ( ( rc = usb_endpoint_clear_halt ( ep ) ) != 0 )
return rc;
- /* Clear endpoint halt, if applicable */
- type = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
- if ( ( type != USB_ENDPOINT_ATTR_CONTROL ) &&
- ( ( rc = usb_clear_feature ( usb, USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT,
- ep->address ) ) != 0 ) ) {
- DBGC ( usb, "USB %s %s could not clear endpoint halt: %s\n",
- usb->name, usb_endpoint_name ( ep ), strerror ( rc ) );
- return rc;
- }
-
/* Reset endpoint */
if ( ( rc = ep->host->reset ( ep ) ) != 0 ) {
DBGC ( usb, "USB %s %s could not reset: %s\n",
diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h
index 68289d2..a05ac61 100644
--- a/src/include/ipxe/usb.h
+++ b/src/include/ipxe/usb.h
@@ -580,6 +580,7 @@ usb_endpoint_described ( struct usb_endpoint *ep,
struct usb_interface_descriptor *interface,
unsigned int type, unsigned int index );
extern int usb_endpoint_open ( struct usb_endpoint *ep );
+extern int usb_endpoint_clear_halt ( struct usb_endpoint *ep );
extern void usb_endpoint_close ( struct usb_endpoint *ep );
extern int usb_message ( struct usb_endpoint *ep, unsigned int request,
unsigned int value, unsigned int index,