]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Clear transaction translator buffers when applicable
authorMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 16:20:36 +0000 (16:20 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 16:22:55 +0000 (16:22 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/bus/usb.c
src/include/ipxe/usb.h

index a0334243a544142fc4f38b85f57ec88e2baa3121..9ad6fade20231626ff4e4c5d8cf6fd9ae32e61f3 100644 (file)
@@ -321,6 +321,37 @@ int usb_endpoint_open ( struct usb_endpoint *ep ) {
        return rc;
 }
 
+/**
+ * Clear transaction translator (if applicable)
+ *
+ * @v ep               USB endpoint
+ * @ret rc             Return status code
+ */
+static int usb_endpoint_clear_tt ( struct usb_endpoint *ep ) {
+       struct usb_device *usb = ep->usb;
+       struct usb_port *tt;
+       int rc;
+
+       /* Do nothing if this is a periodic endpoint */
+       if ( ep->attributes & USB_ENDPOINT_ATTR_PERIODIC )
+               return 0;
+
+       /* Do nothing if this endpoint is not behind a transaction translator */
+       tt = usb_transaction_translator ( usb );
+       if ( ! tt )
+               return 0;
+
+       /* Clear transaction translator buffer */
+       if ( ( rc = tt->hub->driver->clear_tt ( tt->hub, tt, ep ) ) != 0 ) {
+               DBGC ( usb, "USB %s %s could not clear transaction translator: "
+                      "%s\n", usb->name, usb_endpoint_name ( ep->address ),
+                      strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
 /**
  * Close USB endpoint
  *
@@ -345,6 +376,9 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
        /* Discard any recycled buffers, if applicable */
        if ( ep->max )
                usb_flush ( ep );
+
+       /* Clear transaction translator, if applicable */
+       usb_endpoint_clear_tt ( ep );
 }
 
 /**
@@ -369,6 +403,10 @@ static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
                return 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 ) &&
@@ -1949,14 +1987,16 @@ void usb_free_address ( struct usb_bus *bus, unsigned int address ) {
  * @ret route          USB route string
  */
 unsigned int usb_route_string ( struct usb_device *usb ) {
+       struct usb_device *parent;
        unsigned int route;
 
        /* Navigate up to root hub, constructing route string as we go */
-       for ( route = 0 ; usb->port->hub->usb ; usb = usb->port->hub->usb ) {
+       for ( route = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) {
                route <<= 4;
                route |= ( ( usb->port->address > 0xf ) ?
                           0xf : usb->port->address );
        }
+
        return route;
 }
 
@@ -1967,10 +2007,11 @@ unsigned int usb_route_string ( struct usb_device *usb ) {
  * @ret depth          Hub depth
  */
 unsigned int usb_depth ( struct usb_device *usb ) {
+       struct usb_device *parent;
        unsigned int depth;
 
        /* Navigate up to root hub, constructing depth as we go */
-       for ( depth = 0 ; usb->port->hub->usb ; usb = usb->port->hub->usb )
+       for ( depth = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent )
                depth++;
 
        return depth;
@@ -1983,14 +2024,37 @@ unsigned int usb_depth ( struct usb_device *usb ) {
  * @ret port           Root hub port
  */
 struct usb_port * usb_root_hub_port ( struct usb_device *usb ) {
+       struct usb_device *parent;
 
        /* Navigate up to root hub */
-       while ( usb->port->hub->usb )
-               usb = usb->port->hub->usb;
+       while ( ( parent = usb->port->hub->usb ) )
+               usb = parent;
 
        return usb->port;
 }
 
+/**
+ * Get USB transaction translator
+ *
+ * @v usb              USB device
+ * @ret port           Transaction translator port, or NULL
+ */
+struct usb_port * usb_transaction_translator ( struct usb_device *usb ) {
+       struct usb_device *parent;
+
+       /* Navigate up to root hub.  If we find a low-speed or
+        * full-speed port with a higher-speed parent device, then
+        * that port is the transaction translator.
+        */
+       for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) {
+               if ( ( usb->port->speed <= USB_SPEED_FULL ) &&
+                    ( parent->port->speed > USB_SPEED_FULL ) )
+                       return usb->port;
+       }
+
+       return NULL;
+}
+
 /* Drag in objects via register_usb_bus() */
 REQUIRING_SYMBOL ( register_usb_bus );
 
index ee4004e9c69ffebd9e79f42746d2a51c13090595..6f67e06198ab7cbcb0b1d814025ffe8e7837c496 100644 (file)
@@ -249,6 +249,9 @@ struct usb_endpoint_descriptor {
 /** Endpoint attribute transfer type mask */
 #define USB_ENDPOINT_ATTR_TYPE_MASK 0x03
 
+/** Endpoint periodic type */
+#define USB_ENDPOINT_ATTR_PERIODIC 0x01
+
 /** Control endpoint transfer type */
 #define USB_ENDPOINT_ATTR_CONTROL 0x00
 
@@ -1189,6 +1192,7 @@ extern void usb_free_address ( struct usb_bus *bus, unsigned int address );
 extern unsigned int usb_route_string ( struct usb_device *usb );
 extern unsigned int usb_depth ( struct usb_device *usb );
 extern struct usb_port * usb_root_hub_port ( struct usb_device *usb );
+extern struct usb_port * usb_transaction_translator ( struct usb_device *usb );
 
 /** Minimum reset time
  *