]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ehci] Support USB1 devices attached via transaction translators
authorMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 13:34:16 +0000 (13:34 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 16:23:08 +0000 (16:23 +0000)
Support low-speed and full-speed devices attached to a USB2 hub.  Such
devices use a transaction translator (TT) within the USB2 hub, which
asynchronously initiates transactions on the lower-speed bus and
returns the result via a split completion on the high-speed bus.

We make the simplifying assumption that there will never be more than
sixteen active interrupt endpoints behind a single transaction
translator; this assumption allows us to schedule all periodic start
splits in microframe 0 and all periodic split completions in
microframes 2 and 3.  (We do not handle isochronous endpoints.)

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

index 83635f457e0a0e2e039b35362b6d60061db0bf1a..f912de3590b1e93ba5ac2194013861e5ab8570c2 100644 (file)
@@ -545,7 +545,7 @@ static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring,
                assert ( xfer->len <= EHCI_LEN_MASK );
                assert ( EHCI_FL_TOGGLE == EHCI_LEN_TOGGLE );
                desc->len = cpu_to_le16 ( xfer->len | toggle );
-               desc->flags = xfer->flags;
+               desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX );
 
                /* Copy data to immediate data buffer (if requested) */
                data = xfer->data;
@@ -902,19 +902,16 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) {
                chr |= EHCI_CHR_TOGGLE;
 
        /* Determine endpoint speed */
-       switch ( usb->port->speed ) {
-       case USB_SPEED_HIGH :
+       if ( usb->port->speed == USB_SPEED_HIGH ) {
                chr |= EHCI_CHR_EPS_HIGH;
-               break;
-       case USB_SPEED_FULL :
-               chr |= EHCI_CHR_EPS_FULL;
-               break;
-       default:
-               assert ( usb->port->speed == USB_SPEED_LOW );
-               chr |= EHCI_CHR_EPS_LOW;
+       } else {
+               if ( usb->port->speed == USB_SPEED_FULL ) {
+                       chr |= EHCI_CHR_EPS_FULL;
+               } else {
+                       chr |= EHCI_CHR_EPS_LOW;
+               }
                if ( attr == USB_ENDPOINT_ATTR_CONTROL )
                        chr |= EHCI_CHR_CONTROL;
-               break;
        }
 
        return chr;
@@ -927,6 +924,8 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) {
  * @ret cap            Endpoint capabilities
  */
 static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) {
+       struct usb_device *usb = ep->usb;
+       struct usb_port *tt = usb_transaction_translator ( usb );
        unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
        uint32_t cap;
        unsigned int i;
@@ -943,6 +942,15 @@ static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) {
                }
        }
 
+       /* Set transaction translator hub address and port, if applicable */
+       if ( tt ) {
+               assert ( tt->hub->usb );
+               cap |= ( EHCI_CAP_TT_HUB ( tt->hub->usb->address ) |
+                        EHCI_CAP_TT_PORT ( tt->address ) );
+               if ( attr == USB_ENDPOINT_ATTR_INTERRUPT )
+                       cap |= EHCI_CAP_SPLIT_SCHED_DEFAULT;
+       }
+
        return cap;
 }
 
index e9437d40475b7376fcb3533915fdbe1e86772cd6..d575727d1c02693c6d1e2e4d4d2dff32be3a789e 100644 (file)
@@ -285,6 +285,12 @@ struct ehci_transfer_descriptor {
 /** SETUP token */
 #define EHCI_FL_PID_SETUP EHCI_FL_PID ( 2 )
 
+/** Error counter */
+#define EHCI_FL_CERR( count ) ( (count) << 2 )
+
+/** Error counter maximum value */
+#define EHCI_FL_CERR_MAX EHCI_FL_CERR ( 3 )
+
 /** Interrupt on completion */
 #define EHCI_FL_IOC 0x80
 
@@ -341,6 +347,34 @@ struct ehci_queue_head {
 /** Interrupt schedule mask */
 #define EHCI_CAP_INTR_SCHED( uframe ) ( 1 << ( (uframe) + 0 ) )
 
+/** Split completion schedule mask */
+#define EHCI_CAP_SPLIT_SCHED( uframe ) ( 1 << ( (uframe) + 8 ) )
+
+/** Default split completion schedule mask
+ *
+ * We schedule all split starts in microframe 0, on the assumption
+ * that we will never have to deal with more than sixteen actively
+ * interrupting devices via the same transaction translator.  We
+ * schedule split completions for all remaining microframes after
+ * microframe 1 (in which the low-speed or full-speed transaction is
+ * assumed to execute).  This is a very crude approximation designed
+ * to avoid the need for calculating exactly when low-speed and
+ * full-speed transactions will execute.  Since we only ever deal with
+ * interrupt endpoints (rather than isochronous endpoints), the volume
+ * of periodic traffic is extremely low, and this approximation should
+ * remain valid.
+ */
+#define EHCI_CAP_SPLIT_SCHED_DEFAULT                                   \
+       ( EHCI_CAP_SPLIT_SCHED ( 2 ) | EHCI_CAP_SPLIT_SCHED ( 3 ) |     \
+         EHCI_CAP_SPLIT_SCHED ( 4 ) | EHCI_CAP_SPLIT_SCHED ( 5 ) |     \
+         EHCI_CAP_SPLIT_SCHED ( 6 ) | EHCI_CAP_SPLIT_SCHED ( 7 ) )
+
+/** Transaction translator hub address */
+#define EHCI_CAP_TT_HUB( address ) ( (address) << 16 )
+
+/** Transaction translator port number */
+#define EHCI_CAP_TT_PORT( port ) ( (port) << 23 )
+
 /** High-bandwidth pipe multiplier */
 #define EHCI_CAP_MULT( mult ) ( (mult) << 30 )