mirror of https://github.com/ipxe/ipxe.git
[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>pull/34/head
parent
2e72d100af
commit
a60f2ddfeb
|
@ -831,7 +831,7 @@ usb_probe_all ( struct usb_device *usb,
|
||||||
func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor );
|
func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor );
|
||||||
func->dev.desc.device = le16_to_cpu ( usb->device.product );
|
func->dev.desc.device = le16_to_cpu ( usb->device.product );
|
||||||
snprintf ( func->dev.name, sizeof ( func->dev.name ),
|
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 );
|
INIT_LIST_HEAD ( &func->dev.children );
|
||||||
func->dev.parent = bus->dev;
|
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
|
* USB device
|
||||||
|
@ -948,11 +1075,8 @@ static int register_usb ( struct usb_device *usb ) {
|
||||||
struct usb_port *port = usb->port;
|
struct usb_port *port = usb->port;
|
||||||
struct usb_hub *hub = port->hub;
|
struct usb_hub *hub = port->hub;
|
||||||
struct usb_bus *bus = hub->bus;
|
struct usb_bus *bus = hub->bus;
|
||||||
struct usb_configuration_descriptor partial;
|
|
||||||
struct usb_configuration_descriptor *config;
|
|
||||||
unsigned int protocol;
|
unsigned int protocol;
|
||||||
size_t mtu;
|
size_t mtu;
|
||||||
size_t len;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Add to port */
|
/* Add to port */
|
||||||
|
@ -1040,65 +1164,14 @@ static int register_usb ( struct usb_device *usb ) {
|
||||||
usb_bcd ( le16_to_cpu ( usb->device.protocol ) ),
|
usb_bcd ( le16_to_cpu ( usb->device.protocol ) ),
|
||||||
usb_speed_name ( port->speed ), usb->control.mtu );
|
usb_speed_name ( port->speed ), usb->control.mtu );
|
||||||
|
|
||||||
/* Read first part of configuration descriptor to get size */
|
/* Configure device */
|
||||||
if ( ( rc = usb_get_config_descriptor ( usb, 0, &partial,
|
if ( ( rc = usb_configure_any ( usb ) ) != 0 )
|
||||||
sizeof ( partial ) ) ) != 0 ) {
|
goto err_configure_any;
|
||||||
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 );
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
usb_remove_all ( usb );
|
usb_deconfigure ( usb );
|
||||||
usb_set_configuration ( usb, 0 );
|
err_configure_any:
|
||||||
err_set_configuration:
|
|
||||||
err_config_len:
|
|
||||||
err_get_config_descriptor:
|
|
||||||
free ( config );
|
|
||||||
err_alloc_config:
|
|
||||||
err_partial_len:
|
|
||||||
err_get_partial:
|
|
||||||
err_get_device_descriptor:
|
err_get_device_descriptor:
|
||||||
err_mtu:
|
err_mtu:
|
||||||
err_get_mtu:
|
err_get_mtu:
|
||||||
|
@ -1126,20 +1199,12 @@ static void unregister_usb ( struct usb_device *usb ) {
|
||||||
struct usb_hub *hub = port->hub;
|
struct usb_hub *hub = port->hub;
|
||||||
struct io_buffer *iobuf;
|
struct io_buffer *iobuf;
|
||||||
struct io_buffer *tmp;
|
struct io_buffer *tmp;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
/* Remove device drivers */
|
|
||||||
usb_remove_all ( usb );
|
|
||||||
|
|
||||||
/* Sanity checks */
|
/* 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 );
|
assert ( port->usb == usb );
|
||||||
|
|
||||||
/* Clear device configuration */
|
/* Clear device configuration */
|
||||||
usb_set_configuration ( usb, 0 );
|
usb_deconfigure ( usb );
|
||||||
|
|
||||||
/* Close control endpoint */
|
/* Close control endpoint */
|
||||||
usb_endpoint_close ( &usb->control );
|
usb_endpoint_close ( &usb->control );
|
||||||
|
|
|
@ -1034,13 +1034,13 @@ usb_get_config_descriptor ( struct usb_device *usb, unsigned int index,
|
||||||
* Set USB configuration
|
* Set USB configuration
|
||||||
*
|
*
|
||||||
* @v usb USB device
|
* @v usb USB device
|
||||||
* @v config Configuration value
|
* @v index Configuration index
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static inline __attribute__ (( always_inline )) int
|
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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue