]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[xhci] Support USB1 devices attached via transaction translators
authorMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 20:24:20 +0000 (20:24 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 23 Mar 2015 20:24:20 +0000 (20:24 +0000)
xHCI provides a somewhat convoluted mechanism for specifying details
of a transaction translator.  Hubs must be marked as such in the
device slot context.  The only opportunity to do so is as part of a
Configure Endpoint command, which can be executed only when opening
the hub's interrupt endpoint.

We add a mechanism for host controllers to intercept the opening of
hub devices, providing xHCI with an opportunity to update the internal
device slot structure for the corresponding USB device to indicate
that the device is a hub.  We then include the hub-specific details in
the input context whenever any Configure Endpoint command is issued.

When a device is opened, we record the device slot and port for its
transaction translator (if any), and supply these as part of the
Address Device command.

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

index 1b61037970ba43b694e1e662222368393b7c09f0..f53f746105d0e9c4c747dc400fcbd2187fe33c67 100644 (file)
@@ -1692,6 +1692,7 @@ struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, struct usb_device *usb,
                hub->protocol = usb->port->protocol;
        hub->ports = ports;
        hub->driver = driver;
+       hub->host = &bus->op->hub;
 
        /* Initialise port list */
        for ( i = 1 ; i <= hub->ports ; i++ ) {
@@ -1721,11 +1722,18 @@ int register_usb_hub ( struct usb_hub *hub ) {
        /* Add to hub list */
        list_add_tail ( &hub->list, &bus->hubs );
 
-       /* Open hub */
+       /* Open hub (host controller) */
+       if ( ( rc = hub->host->open ( hub ) ) != 0 ) {
+               DBGC ( hub, "USB hub %s could not open: %s\n",
+                      hub->name, strerror ( rc ) );
+               goto err_host_open;
+       }
+
+       /* Open hub (driver) */
        if ( ( rc = hub->driver->open ( hub ) ) != 0 ) {
                DBGC ( hub, "USB hub %s could not open: %s\n",
                       hub->name, strerror ( rc ) );
-               goto err_open;
+               goto err_driver_open;
        }
 
        /* Delay to allow ports to stabilise */
@@ -1747,7 +1755,9 @@ int register_usb_hub ( struct usb_hub *hub ) {
        return 0;
 
        hub->driver->close ( hub );
- err_open:
+ err_driver_open:
+       hub->host->close ( hub );
+ err_host_open:
        list_del ( &hub->list );
        return rc;
 }
@@ -1768,9 +1778,12 @@ void unregister_usb_hub ( struct usb_hub *hub ) {
                        usb_detached ( port );
        }
 
-       /* Close hub */
+       /* Close hub (driver) */
        hub->driver->close ( hub );
 
+       /* Close hub (host controller) */
+       hub->host->close ( hub );
+
        /* Cancel any pending port status changes */
        for ( i = 1 ; i <= hub->ports ; i++ ) {
                port = usb_port ( hub, i );
@@ -1839,7 +1852,7 @@ struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports,
        bus->host = &bus->op->bus;
 
        /* Allocate root hub */
-       bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->hub );
+       bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->root );
        if ( ! bus->hub )
                goto err_alloc_hub;
 
index f912de3590b1e93ba5ac2194013861e5ab8570c2..4bff3fef16be558e1e7df5bacc67e2dc40270143 100644 (file)
@@ -1330,6 +1330,35 @@ static int ehci_device_address ( struct usb_device *usb ) {
        return rc;
 }
 
+/******************************************************************************
+ *
+ * Hub operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open hub
+ *
+ * @v hub              USB hub
+ * @ret rc             Return status code
+ */
+static int ehci_hub_open ( struct usb_hub *hub __unused ) {
+
+       /* Nothing to do */
+       return 0;
+}
+
+/**
+ * Close hub
+ *
+ * @v hub              USB hub
+ */
+static void ehci_hub_close ( struct usb_hub *hub __unused ) {
+
+       /* Nothing to do */
+}
+
 /******************************************************************************
  *
  * Root hub operations
@@ -1343,7 +1372,7 @@ static int ehci_device_address ( struct usb_device *usb ) {
  * @v hub              USB hub
  * @ret rc             Return status code
  */
-static int ehci_hub_open ( struct usb_hub *hub ) {
+static int ehci_root_open ( struct usb_hub *hub ) {
        struct usb_bus *bus = hub->bus;
        struct ehci_device *ehci = usb_bus_get_hostdata ( bus );
        uint32_t portsc;
@@ -1374,7 +1403,7 @@ static int ehci_hub_open ( struct usb_hub *hub ) {
  *
  * @v hub              USB hub
  */
-static void ehci_hub_close ( struct usb_hub *hub ) {
+static void ehci_root_close ( struct usb_hub *hub ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
 
        /* Route all ports back to companion controllers */
@@ -1391,7 +1420,7 @@ static void ehci_hub_close ( struct usb_hub *hub ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int ehci_hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
+static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
        unsigned int line;
@@ -1449,7 +1478,7 @@ static int ehci_hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int ehci_hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
+static int ehci_root_disable ( struct usb_hub *hub, struct usb_port *port ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
 
@@ -1468,7 +1497,7 @@ static int ehci_hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int ehci_hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
+static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
        unsigned int speed;
@@ -1512,8 +1541,8 @@ static int ehci_hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
  * @v ep               USB endpoint
  * @ret rc             Return status code
  */
-static int ehci_hub_clear_tt ( struct usb_hub *hub, struct usb_port *port,
-                              struct usb_endpoint *ep ) {
+static int ehci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port,
+                               struct usb_endpoint *ep ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
 
        /* Should never be called; this is a root hub */
@@ -1529,7 +1558,7 @@ static int ehci_hub_clear_tt ( struct usb_hub *hub, struct usb_port *port,
  * @v hub              USB hub
  * @v port             USB port
  */
-static void ehci_hub_poll ( struct usb_hub *hub, struct usb_port *port ) {
+static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
        uint32_t change;
@@ -1692,7 +1721,7 @@ static void ehci_bus_poll ( struct usb_bus *bus ) {
 
                /* Iterate over all ports looking for status changes */
                for ( i = 1 ; i <= ehci->ports ; i++ )
-                       ehci_hub_poll ( hub, usb_port ( hub, i ) );
+                       ehci_root_poll ( hub, usb_port ( hub, i ) );
        }
 
        /* Report fatal errors */
@@ -1730,10 +1759,14 @@ static struct usb_host_operations ehci_operations = {
        .hub = {
                .open = ehci_hub_open,
                .close = ehci_hub_close,
-               .enable = ehci_hub_enable,
-               .disable = ehci_hub_disable,
-               .speed = ehci_hub_speed,
-               .clear_tt = ehci_hub_clear_tt,
+       },
+       .root = {
+               .open = ehci_root_open,
+               .close = ehci_root_close,
+               .enable = ehci_root_enable,
+               .disable = ehci_root_disable,
+               .speed = ehci_root_speed,
+               .clear_tt = ehci_root_clear_tt,
        },
 };
 
index 69d621d9ca3d52c12bc5732aa18b641c2760bae5..b6ce7444131d1fbae3aab59fbea3055a0322658b 100644 (file)
@@ -1990,6 +1990,8 @@ static void xhci_address_device_input ( struct xhci_device *xhci,
        slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( 1, 0, slot->psiv,
                                                        slot->route ) );
        slot_ctx->port = slot->port;
+       slot_ctx->tt_id = slot->tt_id;
+       slot_ctx->tt_port = slot->tt_port;
 
        /* Populate control endpoint context */
        ep_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_EP0 ) );
@@ -2039,7 +2041,7 @@ static inline int xhci_address_device ( struct xhci_device *xhci,
  * @v input            Input context
  */
 static void xhci_configure_endpoint_input ( struct xhci_device *xhci,
-                                           struct xhci_slot *slot __unused,
+                                           struct xhci_slot *slot,
                                            struct xhci_endpoint *endpoint,
                                            void *input ) {
        struct xhci_control_context *control_ctx;
@@ -2054,7 +2056,9 @@ static void xhci_configure_endpoint_input ( struct xhci_device *xhci,
        /* Populate slot context */
        slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT ));
        slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ),
-                                                       0, 0, 0 ) );
+                                                       ( slot->ports ? 1 : 0 ),
+                                                       slot->psiv, 0 ) );
+       slot_ctx->ports = slot->ports;
 
        /* Populate endpoint context */
        ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) );
@@ -2587,7 +2591,9 @@ static int xhci_endpoint_stream ( struct usb_endpoint *ep,
  */
 static int xhci_device_open ( struct usb_device *usb ) {
        struct xhci_device *xhci = usb_bus_get_hostdata ( usb->port->hub->bus );
+       struct usb_port *tt = usb_transaction_translator ( usb );
        struct xhci_slot *slot;
+       struct xhci_slot *tt_slot;
        size_t len;
        int type;
        int id;
@@ -2621,6 +2627,11 @@ static int xhci_device_open ( struct usb_device *usb ) {
        slot->xhci = xhci;
        slot->usb = usb;
        slot->id = id;
+       if ( tt ) {
+               tt_slot = usb_get_hostdata ( tt->hub->usb );
+               slot->tt_id = tt_slot->id;
+               slot->tt_port = tt->address;
+       }
 
        /* Allocate a device context */
        len = xhci_device_context_offset ( xhci, XHCI_CTX_END );
@@ -2817,6 +2828,51 @@ static void xhci_bus_poll ( struct usb_bus *bus ) {
        xhci_event_poll ( xhci );
 }
 
+/******************************************************************************
+ *
+ * Hub operations
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open hub
+ *
+ * @v hub              USB hub
+ * @ret rc             Return status code
+ */
+static int xhci_hub_open ( struct usb_hub *hub ) {
+       struct xhci_slot *slot;
+
+       /* Do nothing if this is the root hub */
+       if ( ! hub->usb )
+               return 0;
+
+       /* Get device slot */
+       slot = usb_get_hostdata ( hub->usb );
+
+       /* Update device slot hub parameters.  We don't inform the
+        * hardware of this information until the hub's interrupt
+        * endpoint is opened, since the only mechanism for so doing
+        * provided by the xHCI specification is a Configure Endpoint
+        * command, and we can't issue that command until we have a
+        * non-EP0 endpoint to configure.
+        */
+       slot->ports = hub->ports;
+
+       return 0;
+}
+
+/**
+ * Close hub
+ *
+ * @v hub              USB hub
+ */
+static void xhci_hub_close ( struct usb_hub *hub __unused ) {
+
+       /* Nothing to do */
+}
+
 /******************************************************************************
  *
  * Root hub operations
@@ -2830,7 +2886,7 @@ static void xhci_bus_poll ( struct usb_bus *bus ) {
  * @v hub              USB hub
  * @ret rc             Return status code
  */
-static int xhci_hub_open ( struct usb_hub *hub ) {
+static int xhci_root_open ( struct usb_hub *hub ) {
        struct usb_bus *bus = hub->bus;
        struct xhci_device *xhci = usb_bus_get_hostdata ( bus );
        struct usb_port *port;
@@ -2880,7 +2936,7 @@ static int xhci_hub_open ( struct usb_hub *hub ) {
  *
  * @v hub              USB hub
  */
-static void xhci_hub_close ( struct usb_hub *hub ) {
+static void xhci_root_close ( struct usb_hub *hub ) {
 
        /* Clear hub driver private data */
        usb_hub_set_drvdata ( hub, NULL );
@@ -2893,7 +2949,7 @@ static void xhci_hub_close ( struct usb_hub *hub ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int xhci_hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
+static int xhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) {
        struct xhci_device *xhci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
        unsigned int i;
@@ -2930,7 +2986,7 @@ static int xhci_hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int xhci_hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
+static int xhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) {
        struct xhci_device *xhci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
 
@@ -2950,7 +3006,7 @@ static int xhci_hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
  * @v port             USB port
  * @ret rc             Return status code
  */
-static int xhci_hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
+static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
        struct xhci_device *xhci = usb_hub_get_drvdata ( hub );
        uint32_t portsc;
        unsigned int psiv;
@@ -3000,8 +3056,8 @@ static int xhci_hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
  * @v ep               USB endpoint
  * @ret rc             Return status code
  */
-static int xhci_hub_clear_tt ( struct usb_hub *hub, struct usb_port *port,
-                              struct usb_endpoint *ep ) {
+static int xhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port,
+                               struct usb_endpoint *ep ) {
        struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
 
        /* Should never be called; this is a root hub */
@@ -3041,10 +3097,14 @@ static struct usb_host_operations xhci_operations = {
        .hub = {
                .open = xhci_hub_open,
                .close = xhci_hub_close,
-               .enable = xhci_hub_enable,
-               .disable = xhci_hub_disable,
-               .speed = xhci_hub_speed,
-               .clear_tt = xhci_hub_clear_tt,
+       },
+       .root = {
+               .open = xhci_root_open,
+               .close = xhci_root_close,
+               .enable = xhci_root_enable,
+               .disable = xhci_root_disable,
+               .speed = xhci_root_speed,
+               .clear_tt = xhci_root_clear_tt,
        },
 };
 
index 5abb5caaf59a057f18703e8ff53380905773fffa..c5da48de2d824b7cb2a893b011a00ea7d69753ef 100644 (file)
@@ -1110,6 +1110,12 @@ struct xhci_slot {
        unsigned int port;
        /** Protocol speed ID */
        unsigned int psiv;
+       /** Number of ports (if this device is a hub) */
+       unsigned int ports;
+       /** Transaction translator slot ID */
+       unsigned int tt_id;
+       /** Transaction translator port */
+       unsigned int tt_port;
        /** Endpoints, indexed by context ID */
        struct xhci_endpoint *endpoint[XHCI_CTX_END];
 };
index 6f67e06198ab7cbcb0b1d814025ffe8e7837c496..b3803cd18ed8db534147f7ddab56b1bd19a02645 100644 (file)
@@ -777,6 +777,8 @@ struct usb_hub {
        /** List of hubs */
        struct list_head list;
 
+       /** Host controller operations */
+       struct usb_hub_host_operations *host;
        /** Driver operations */
        struct usb_hub_driver_operations *driver;
        /** Driver private data */
@@ -789,7 +791,22 @@ struct usb_hub {
        struct usb_port port[0];
 };
 
-/** USB hub operations */
+/** USB hub host controller operations */
+struct usb_hub_host_operations {
+       /** Open hub
+        *
+        * @v hub               USB hub
+        * @ret rc              Return status code
+        */
+       int ( * open ) ( struct usb_hub *hub );
+       /** Close hub
+        *
+        * @v hub               USB hub
+        */
+       void ( * close ) ( struct usb_hub *hub );
+};
+
+/** USB hub driver operations */
 struct usb_hub_driver_operations {
        /** Open hub
         *
@@ -940,8 +957,10 @@ struct usb_host_operations {
        struct usb_device_host_operations device;
        /** Bus operations */
        struct usb_bus_host_operations bus;
+       /** Hub operations */
+       struct usb_hub_host_operations hub;
        /** Root hub operations */
-       struct usb_hub_driver_operations hub;
+       struct usb_hub_driver_operations root;
 };
 
 /**