diff options
-rw-r--r-- | src/drivers/bus/usb.c | 23 | ||||
-rw-r--r-- | src/drivers/usb/ehci.c | 59 | ||||
-rw-r--r-- | src/drivers/usb/xhci.c | 86 | ||||
-rw-r--r-- | src/drivers/usb/xhci.h | 6 | ||||
-rw-r--r-- | src/include/ipxe/usb.h | 23 |
5 files changed, 164 insertions, 33 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 1b610379..f53f7461 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -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; diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index f912de35..4bff3fef 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -1332,6 +1332,35 @@ static int ehci_device_address ( struct usb_device *usb ) { /****************************************************************************** * + * 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, }, }; diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 69d621d9..b6ce7444 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -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 ); @@ -2819,6 +2830,51 @@ static void xhci_bus_poll ( struct usb_bus *bus ) { /****************************************************************************** * + * 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, }, }; diff --git a/src/drivers/usb/xhci.h b/src/drivers/usb/xhci.h index 5abb5caa..c5da48de 100644 --- a/src/drivers/usb/xhci.h +++ b/src/drivers/usb/xhci.h @@ -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]; }; diff --git a/src/include/ipxe/usb.h b/src/include/ipxe/usb.h index 6f67e061..b3803cd1 100644 --- a/src/include/ipxe/usb.h +++ b/src/include/ipxe/usb.h @@ -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; }; /** |