summaryrefslogtreecommitdiffstats
path: root/src/drivers/bus/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/bus/usb.c')
-rw-r--r--src/drivers/bus/usb.c400
1 files changed, 254 insertions, 146 deletions
diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c
index cd80c320..d8d7f6b3 100644
--- a/src/drivers/bus/usb.c
+++ b/src/drivers/bus/usb.c
@@ -901,75 +901,154 @@ int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index,
*/
/**
+ * Get USB configuration descriptor
+ *
+ * @v usb USB device
+ * @v index Configuration index
+ * @ret config Configuration descriptor
+ * @ret rc Return status code
+ *
+ * The configuration descriptor is dynamically allocated and must
+ * eventually be freed by the caller.
+ */
+static int
+usb_config_descriptor ( struct usb_device *usb, unsigned int index,
+ struct usb_configuration_descriptor **config ) {
+ struct usb_configuration_descriptor partial;
+ 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;
+ }
+
+ return 0;
+
+ err_config_len:
+ err_get_config_descriptor:
+ free ( *config );
+ err_alloc_config:
+ err_partial_len:
+ err_get_partial:
+ return rc;
+}
+
+/**
* Describe USB function
*
- * @v func USB function
+ * @v usb USB device
* @v config Configuration descriptor
* @v first First interface number
+ * @v interfaces Interface list to fill in
+ * @v desc Function descriptor to fill in
* @ret rc Return status code
*/
-static int usb_function ( struct usb_function *func,
+static int usb_describe ( struct usb_device *usb,
struct usb_configuration_descriptor *config,
- unsigned int first ) {
- struct usb_device *usb = func->usb;
+ unsigned int first, uint8_t *interfaces,
+ struct usb_function_descriptor *desc ) {
struct usb_interface_association_descriptor *association;
struct usb_interface_descriptor *interface;
struct cdc_union_descriptor *cdc_union;
unsigned int i;
+ /* Fill in vendor and product ID */
+ desc->vendor = le16_to_cpu ( usb->device.vendor );
+ desc->product = le16_to_cpu ( usb->device.product );
+
/* First, look for an interface association descriptor */
association = usb_interface_association_descriptor ( config, first );
if ( association ) {
/* Sanity check */
- if ( association->count > config->interfaces ) {
+ assert ( association->first == first );
+ if ( ( first + association->count ) > config->interfaces ) {
DBGC ( usb, "USB %s has invalid association [%d-%d)\n",
- func->name, association->first,
- ( association->first + association->count ) );
+ usb->name, first, ( first + association->count));
return -ERANGE;
}
/* Describe function */
- memcpy ( &func->class, &association->class,
- sizeof ( func->class ) );
- func->count = association->count;
+ memcpy ( &desc->class, &association->class,
+ sizeof ( desc->class ) );
+ desc->count = association->count;
for ( i = 0 ; i < association->count ; i++ )
- func->interface[i] = ( association->first + i );
+ interfaces[i] = ( first + i );
return 0;
}
/* Next, look for an interface descriptor */
interface = usb_interface_descriptor ( config, first, 0 );
if ( ! interface ) {
- DBGC ( usb, "USB %s has no interface descriptor\n",
- func->name );
+ DBGC ( usb, "USB %s has no descriptor for interface %d\n",
+ usb->name, first );
return -ENOENT;
}
/* Describe function */
- memcpy ( &func->class, &interface->class, sizeof ( func->class ) );
- func->count = 1;
- func->interface[0] = first;
+ memcpy ( &desc->class, &interface->class, sizeof ( desc->class ) );
+ desc->count = 1;
+ interfaces[0] = first;
/* Look for a CDC union descriptor, if applicable */
- if ( ( func->class.class == USB_CLASS_CDC ) &&
+ if ( ( desc->class.class == USB_CLASS_CDC ) &&
( cdc_union = cdc_union_descriptor ( config, interface ) ) ) {
/* Determine interface count */
- func->count = ( ( cdc_union->header.len -
+ desc->count = ( ( cdc_union->header.len -
offsetof ( typeof ( *cdc_union ),
interface[0] ) ) /
sizeof ( cdc_union->interface[0] ) );
- if ( func->count > config->interfaces ) {
+ if ( desc->count > config->interfaces ) {
DBGC ( usb, "USB %s has invalid union functional "
"descriptor with %d interfaces\n",
- func->name, func->count );
+ usb->name, desc->count );
return -ERANGE;
}
/* Describe function */
- for ( i = 0 ; i < func->count ; i++ )
- func->interface[i] = cdc_union->interface[i];
+ for ( i = 0 ; i < desc->count ; i++ ) {
+ if ( cdc_union->interface[i] >= config->interfaces ) {
+ DBGC ( usb, "USB %s has invalid union "
+ "functional descriptor covering "
+ "interface %d\n", usb->name,
+ cdc_union->interface[i] );
+ return -ERANGE;
+ }
+ interfaces[i] = cdc_union->interface[i];
+ }
return 0;
}
@@ -978,16 +1057,37 @@ static int usb_function ( struct usb_function *func,
}
/**
+ * Update list of used interface
+ *
+ * @v usb USB device
+ * @v count Number of interfaces
+ * @v interface List of interfaces
+ * @v used List of already-used interfaces
+ * @ret rc Return status code
+ */
+static int usb_used ( struct usb_device *usb, unsigned int count,
+ uint8_t *interface, uint8_t *used ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < count ; i++ ) {
+ if ( used[interface[i]] ) {
+ DBGC ( usb, "USB %s interface %d already in use\n",
+ usb->name, interface[i] );
+ return -EINVAL;
+ }
+ used[interface[i]] = 1;
+ }
+ return 0;
+}
+
+/**
* Find USB device driver
*
- * @v vendor Vendor ID
- * @v product Product ID
- * @v class Class
+ * @v desc Function descriptor
* @ret id USB device ID, or NULL
* @ret driver USB device driver, or NULL
*/
-struct usb_driver * usb_find_driver ( unsigned int vendor, unsigned int product,
- struct usb_class *class,
+struct usb_driver * usb_find_driver ( struct usb_function_descriptor *desc,
struct usb_device_id **id ) {
struct usb_driver *driver;
unsigned int i;
@@ -998,13 +1098,13 @@ struct usb_driver * usb_find_driver ( unsigned int vendor, unsigned int product,
/* Check for a matching ID */
*id = &driver->ids[i];
- if ( ( ( (*id)->vendor == vendor ) ||
+ if ( ( ( (*id)->vendor == desc->vendor ) ||
( (*id)->vendor == USB_ANY_ID ) ) &&
- ( ( (*id)->product == product ) ||
+ ( ( (*id)->product == desc->product ) ||
( (*id)->product == USB_ANY_ID ) ) &&
- ( (*id)->class.class == class->class ) &&
- ( (*id)->class.subclass == class->subclass ) &&
- ( (*id)->class.protocol == class->protocol ) )
+ ( (*id)->class.class == desc->class.class ) &&
+ ( (*id)->class.subclass == desc->class.subclass )&&
+ ( (*id)->class.protocol == desc->class.protocol ) )
return driver;
}
}
@@ -1015,6 +1115,51 @@ struct usb_driver * usb_find_driver ( unsigned int vendor, unsigned int product,
}
/**
+ * Get USB device configuration score
+ *
+ * @v usb USB device
+ * @v config Configuration descriptor
+ * @ret score Device configuration score, or negative error
+ */
+static int usb_score ( struct usb_device *usb,
+ struct usb_configuration_descriptor *config ) {
+ uint8_t used[config->interfaces];
+ uint8_t interface[config->interfaces];
+ struct usb_function_descriptor desc;
+ struct usb_driver *driver;
+ struct usb_device_id *id;
+ unsigned int first;
+ unsigned int score = 0;
+ int rc;
+
+ /* Identify each function in turn */
+ memset ( used, 0, sizeof ( used ) );
+ for ( first = 0 ; first < config->interfaces ; first++ ) {
+
+ /* Skip interfaces already used */
+ if ( used[first] )
+ continue;
+
+ /* Describe function */
+ if ( ( rc = usb_describe ( usb, config, first, interface,
+ &desc ) ) != 0 )
+ return rc;
+
+ /* Update used interfaces */
+ if ( ( rc = usb_used ( usb, desc.count, interface,
+ used ) ) != 0 )
+ return rc;
+
+ /* Look for a driver for this function */
+ driver = usb_find_driver ( &desc, &id );
+ if ( driver )
+ score += driver->score;
+ }
+
+ return score;
+}
+
+/**
* Probe USB device driver
*
* @v func USB function
@@ -1029,13 +1174,12 @@ static int usb_probe ( struct usb_function *func,
int rc;
/* Identify driver */
- driver = usb_find_driver ( func->dev.desc.vendor, func->dev.desc.device,
- &func->class, &id );
+ driver = usb_find_driver ( &func->desc, &id );
if ( ! driver ) {
DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d has no driver\n",
- func->name, func->dev.desc.vendor, func->dev.desc.device,
- func->class.class, func->class.subclass,
- func->class.protocol );
+ func->name, func->desc.vendor, func->desc.product,
+ func->desc.class.class, func->desc.class.subclass,
+ func->desc.class.protocol );
return -ENOENT;
}
@@ -1106,28 +1250,24 @@ usb_probe_all ( struct usb_device *usb,
list_add_tail ( &func->list, &usb->functions );
/* Identify function */
- if ( ( rc = usb_function ( func, config, first ) ) != 0 )
- goto err_function;
- assert ( func->count <= config->interfaces );
+ if ( ( rc = usb_describe ( usb, config, first, func->interface,
+ &func->desc ) ) != 0 )
+ goto err_describe;
+ assert ( func->desc.count <= config->interfaces );
/* Mark interfaces as used */
- for ( i = 0 ; i < func->count ; i++ ) {
- if ( func->interface[i] >= config->interfaces ) {
- DBGC ( usb, "USB %s has invalid interface %d\n",
- func->name, func->interface[i] );
- goto err_interface;
- }
- used[ func->interface[i] ] = 1;
- }
+ if ( ( rc = usb_used ( usb, func->desc.count, func->interface,
+ used ) ) != 0 )
+ goto err_used;
/* Probe device driver */
if ( ( rc = usb_probe ( func, config ) ) != 0 )
goto err_probe;
DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d interfaces ",
- func->name, func->dev.desc.vendor, func->dev.desc.device,
- func->class.class, func->class.subclass,
- func->class.protocol );
- for ( i = 0 ; i < func->count ; i++ )
+ func->name, func->desc.vendor, func->desc.product,
+ func->desc.class.class, func->desc.class.subclass,
+ func->desc.class.protocol );
+ for ( i = 0 ; i < func->desc.count ; i++ )
DBGC ( usb, "%s%d", ( i ? "," : "" ),
func->interface[i] );
DBGC ( usb, " using driver %s\n", func->dev.driver_name );
@@ -1140,8 +1280,8 @@ usb_probe_all ( struct usb_device *usb,
list_del ( &func->dev.siblings );
usb_remove ( func );
err_probe:
- err_interface:
- err_function:
+ err_used:
+ err_describe:
list_del ( &func->list );
free ( func );
err_alloc:
@@ -1178,82 +1318,6 @@ 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
@@ -1275,32 +1339,76 @@ static void usb_deconfigure ( struct usb_device *usb ) {
}
/**
- * Find and select a supported USB device configuration
+ * Choose our preferred USB device configuration
*
* @v usb USB device
* @ret rc Return status code
*/
-static int usb_configure_any ( struct usb_device *usb ) {
+static int usb_autoconfigure ( struct usb_device *usb ) {
+ struct usb_configuration_descriptor *config;
+ unsigned int preferred = 0;
unsigned int index;
- int rc = -ENOENT;
+ int score;
+ int best = 0;
+ int rc;
- /* Attempt all configuration indexes */
+ /* Calculate driver score for each configuration index */
for ( index = 0 ; index < usb->device.configurations ; index++ ) {
- /* Attempt this configuration index */
- if ( ( rc = usb_configure ( usb, index ) ) != 0 )
- continue;
+ /* Read configuration descriptor */
+ if ( ( rc = usb_config_descriptor ( usb, index,
+ &config ) ) != 0 )
+ goto err_config;
- /* If we have no drivers, then try the next configuration */
- if ( list_empty ( &usb->functions ) ) {
- rc = -ENOTSUP;
- usb_deconfigure ( usb );
- continue;
+ /* Get score for this configuration */
+ score = usb_score ( usb, config );
+ if ( score < 0 ) {
+ rc = score;
+ goto err_score;
}
+ DBGC2 ( usb, "USB %s configuration %d score %d\n",
+ usb->name, config->config, score );
- return 0;
+ /* Record as preferred configuration, if applicable */
+ if ( score > best ) {
+ best = score;
+ preferred = index;
+ }
+
+ /* Free configuration descriptor */
+ free ( config );
+ config = NULL;
}
+ /* Read preferred configuration descriptor */
+ if ( ( rc = usb_config_descriptor ( usb, preferred, &config ) ) != 0 )
+ goto err_preferred;
+
+ /* 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:
+ free ( config );
+ err_preferred:
+ return rc;
+
+ err_score:
+ free ( config );
+ err_config:
return rc;
}
@@ -1444,13 +1552,13 @@ static int register_usb ( struct usb_device *usb ) {
usb_speed_name ( port->speed ), usb->control.mtu );
/* Configure device */
- if ( ( rc = usb_configure_any ( usb ) ) != 0 )
- goto err_configure_any;
+ if ( ( rc = usb_autoconfigure ( usb ) ) != 0 )
+ goto err_autoconfigure;
return 0;
usb_deconfigure ( usb );
- err_configure_any:
+ err_autoconfigure:
err_get_device_descriptor:
err_mtu:
err_get_mtu: