diff options
author | Michael Brown | 2015-02-09 14:37:34 +0100 |
---|---|---|
committer | Michael Brown | 2015-02-09 15:46:22 +0100 |
commit | a60f2ddfeb2b7e20f1902181c5d35c58f186c475 (patch) | |
tree | 8ab9fd75609b0193d4343d26753e8dacd87f9abb /src/drivers/bus | |
parent | [ncm] Reserve headroom in received packets (diff) | |
download | ipxe-a60f2ddfeb2b7e20f1902181c5d35c58f186c475.tar.gz ipxe-a60f2ddfeb2b7e20f1902181c5d35c58f186c475.tar.xz ipxe-a60f2ddfeb2b7e20f1902181c5d35c58f186c475.zip |
[usb] Try multiple USB device configurations
Iterate over a USB device's available configurations until we find one
for which we have working drivers.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/bus')
-rw-r--r-- | src/drivers/bus/usb.c | 203 |
1 files changed, 134 insertions, 69 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index 1e76ffc9..bbdc29cc 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -831,7 +831,7 @@ usb_probe_all ( struct usb_device *usb, func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor ); func->dev.desc.device = le16_to_cpu ( usb->device.product ); snprintf ( func->dev.name, sizeof ( func->dev.name ), - "%s-%d", usb->name, first ); + "%s-%d.%d", usb->name, config->config, first ); INIT_LIST_HEAD ( &func->dev.children ); func->dev.parent = bus->dev; @@ -905,6 +905,133 @@ static void usb_remove_all ( struct usb_device *usb ) { } } +/** + * Select USB device configuration + * + * @v usb USB device + * @v index Configuration index + * @ret rc Return status code + */ +static int usb_configure ( struct usb_device *usb, unsigned int index ) { + struct usb_configuration_descriptor partial; + struct usb_configuration_descriptor *config; + size_t len; + int rc; + + /* Read first part of configuration descriptor to get size */ + if ( ( rc = usb_get_config_descriptor ( usb, index, &partial, + sizeof ( partial ) ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_partial; + } + len = le16_to_cpu ( partial.len ); + if ( len < sizeof ( partial ) ) { + DBGC ( usb, "USB %s underlength configuraton descriptor %d\n", + usb->name, index ); + rc = -EINVAL; + goto err_partial_len; + } + + /* Allocate buffer for whole configuration descriptor */ + config = malloc ( len ); + if ( ! config ) { + rc = -ENOMEM; + goto err_alloc_config; + } + + /* Read whole configuration descriptor */ + if ( ( rc = usb_get_config_descriptor ( usb, index, config, + len ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_config_descriptor; + } + if ( config->len != partial.len ) { + DBGC ( usb, "USB %s bad configuration descriptor %d length\n", + usb->name, index ); + rc = -EINVAL; + goto err_config_len; + } + + /* Set configuration */ + if ( ( rc = usb_set_configuration ( usb, config->config ) ) != 0){ + DBGC ( usb, "USB %s could not set configuration %d: %s\n", + usb->name, config->config, strerror ( rc ) ); + goto err_set_configuration; + } + + /* Probe USB device drivers */ + usb_probe_all ( usb, config ); + + /* Free configuration descriptor */ + free ( config ); + + return 0; + + usb_remove_all ( usb ); + usb_set_configuration ( usb, 0 ); + err_set_configuration: + err_config_len: + err_get_config_descriptor: + free ( config ); + err_alloc_config: + err_partial_len: + err_get_partial: + return rc; +} + +/** + * Clear USB device configuration + * + * @v usb USB device + */ +static void usb_deconfigure ( struct usb_device *usb ) { + unsigned int i; + + /* Remove device drivers */ + usb_remove_all ( usb ); + + /* Sanity checks */ + for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++){ + if ( i != USB_ENDPOINT_IDX ( USB_EP0_ADDRESS ) ) + assert ( usb->ep[i] == NULL ); + } + + /* Clear device configuration */ + usb_set_configuration ( usb, 0 ); +} + +/** + * Find and select a supported USB device configuration + * + * @v usb USB device + * @ret rc Return status code + */ +static int usb_configure_any ( struct usb_device *usb ) { + unsigned int index; + int rc = -ENOENT; + + /* Attempt all configuration indexes */ + for ( index = 0 ; index < usb->device.configurations ; index++ ) { + + /* Attempt this configuration index */ + if ( ( rc = usb_configure ( usb, index ) ) != 0 ) + continue; + + /* If we have no drivers, then try the next configuration */ + if ( list_empty ( &usb->functions ) ) { + rc = -ENOTSUP; + usb_deconfigure ( usb ); + continue; + } + + return 0; + } + + return rc; +} + /****************************************************************************** * * USB device @@ -948,11 +1075,8 @@ static int register_usb ( struct usb_device *usb ) { struct usb_port *port = usb->port; struct usb_hub *hub = port->hub; struct usb_bus *bus = hub->bus; - struct usb_configuration_descriptor partial; - struct usb_configuration_descriptor *config; unsigned int protocol; size_t mtu; - size_t len; int rc; /* Add to port */ @@ -1040,65 +1164,14 @@ static int register_usb ( struct usb_device *usb ) { usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), usb_speed_name ( port->speed ), usb->control.mtu ); - /* Read first part of configuration descriptor to get size */ - if ( ( rc = usb_get_config_descriptor ( usb, 0, &partial, - sizeof ( partial ) ) ) != 0 ) { - DBGC ( usb, "USB %s could not get configuration descriptor: " - "%s\n", usb->name, strerror ( rc ) ); - goto err_get_partial; - } - len = le16_to_cpu ( partial.len ); - if ( len < sizeof ( partial ) ) { - DBGC ( usb, "USB %s underlength configuraton descriptor\n", - usb->name ); - rc = -EINVAL; - goto err_partial_len; - } - - /* Allocate buffer for whole configuration descriptor */ - config = malloc ( len ); - if ( ! config ) { - rc = -ENOMEM; - goto err_alloc_config; - } - - /* Read whole configuration descriptor */ - if ( ( rc = usb_get_config_descriptor ( usb, 0, config, len ) ) != 0 ) { - DBGC ( usb, "USB %s could not get configuration descriptor: " - "%s\n", usb->name, strerror ( rc ) ); - goto err_get_config_descriptor; - } - if ( config->len != partial.len ) { - DBGC ( usb, "USB %s bad configuration descriptor length\n", - usb->name ); - rc = -EINVAL; - goto err_config_len; - } - - /* Set configuration */ - if ( ( rc = usb_set_configuration ( usb, config->config ) ) != 0){ - DBGC ( usb, "USB %s could not set configuration %#02x: %s\n", - usb->name, config->config, strerror ( rc ) ); - goto err_set_configuration; - } - - /* Probe USB device drivers */ - usb_probe_all ( usb, config ); - - /* Free configuration descriptor */ - free ( config ); + /* Configure device */ + if ( ( rc = usb_configure_any ( usb ) ) != 0 ) + goto err_configure_any; return 0; - usb_remove_all ( usb ); - usb_set_configuration ( usb, 0 ); - err_set_configuration: - err_config_len: - err_get_config_descriptor: - free ( config ); - err_alloc_config: - err_partial_len: - err_get_partial: + usb_deconfigure ( usb ); + err_configure_any: err_get_device_descriptor: err_mtu: err_get_mtu: @@ -1126,20 +1199,12 @@ static void unregister_usb ( struct usb_device *usb ) { struct usb_hub *hub = port->hub; struct io_buffer *iobuf; struct io_buffer *tmp; - unsigned int i; - - /* Remove device drivers */ - usb_remove_all ( usb ); /* Sanity checks */ - for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++){ - if ( i != USB_ENDPOINT_IDX ( USB_EP0_ADDRESS ) ) - assert ( usb->ep[i] == NULL ); - } assert ( port->usb == usb ); /* Clear device configuration */ - usb_set_configuration ( usb, 0 ); + usb_deconfigure ( usb ); /* Close control endpoint */ usb_endpoint_close ( &usb->control ); |