david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[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>
This commit is contained in:
Michael Brown 2015-02-09 13:37:34 +00:00
parent 2e72d100af
commit a60f2ddfeb
2 changed files with 137 additions and 72 deletions

View File

@ -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 );

View File

@ -1034,13 +1034,13 @@ usb_get_config_descriptor ( struct usb_device *usb, unsigned int index,
* Set USB configuration
*
* @v usb USB device
* @v config Configuration value
* @v index Configuration index
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) int
usb_set_configuration ( struct usb_device *usb, unsigned int config ) {
usb_set_configuration ( struct usb_device *usb, unsigned int index ) {
return usb_control ( usb, USB_SET_CONFIGURATION, config, 0, NULL, 0 );
return usb_control ( usb, USB_SET_CONFIGURATION, index, 0, NULL, 0 );
}
/**