diff options
author | Michael Brown | 2015-02-09 17:33:49 +0100 |
---|---|---|
committer | Michael Brown | 2015-02-09 17:34:55 +0100 |
commit | cf153f60a54b4706225f6326dc65b9343148c9f3 (patch) | |
tree | 7b395ac183c2d63895bae1cd83718301e78f8060 /src/drivers/bus | |
parent | [usb] Try multiple USB device configurations (diff) | |
download | ipxe-cf153f60a54b4706225f6326dc65b9343148c9f3.tar.gz ipxe-cf153f60a54b4706225f6326dc65b9343148c9f3.tar.xz ipxe-cf153f60a54b4706225f6326dc65b9343148c9f3.zip |
[usb] Handle CDC union functional descriptors
USB Communications Device Class devices may use a union functional
descriptor to group several interfaces into a function.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/bus')
-rw-r--r-- | src/drivers/bus/cdc.c | 50 | ||||
-rw-r--r-- | src/drivers/bus/usb.c | 36 |
2 files changed, 83 insertions, 3 deletions
diff --git a/src/drivers/bus/cdc.c b/src/drivers/bus/cdc.c new file mode 100644 index 00000000..935daff1 --- /dev/null +++ b/src/drivers/bus/cdc.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <ipxe/usb.h> +#include <ipxe/cdc.h> + +/** @file + * + * USB Communications Device Class (CDC) + * + */ + +/** + * Locate CDC union functional descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @ret desc Union functional descriptor, or NULL if not found + */ +struct cdc_union_descriptor * +cdc_union_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ) { + struct cdc_union_descriptor *desc; + + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) && + ( desc->subtype == CDC_SUBTYPE_UNION ) ) + return desc; + } + return NULL; +} diff --git a/src/drivers/bus/usb.c b/src/drivers/bus/usb.c index bbdc29cc..97c2e3fc 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <assert.h> #include <byteswap.h> #include <ipxe/usb.h> +#include <ipxe/cdc.h> /** @file * @@ -678,6 +679,7 @@ static int usb_function ( struct usb_function *func, struct usb_device *usb = func->usb; struct usb_interface_association_descriptor *association; struct usb_interface_descriptor *interface; + struct cdc_union_descriptor *cdc_union; unsigned int i; /* First, look for an interface association descriptor */ @@ -685,8 +687,7 @@ static int usb_function ( struct usb_function *func, if ( association ) { /* Sanity check */ - if ( ( association->first + association->count ) > - config->interfaces ) { + if ( association->count > config->interfaces ) { DBGC ( usb, "USB %s has invalid association [%d-%d)\n", func->name, association->first, ( association->first + association->count ) ); @@ -714,6 +715,30 @@ static int usb_function ( struct usb_function *func, memcpy ( &func->class, &interface->class, sizeof ( func->class ) ); func->count = 1; func->interface[0] = first; + + /* Look for a CDC union descriptor, if applicable */ + if ( ( func->class.class == USB_CLASS_CDC ) && + ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) { + + /* Determine interface count */ + func->count = ( ( cdc_union->header.len - + offsetof ( typeof ( *cdc_union ), + interface[0] ) ) / + sizeof ( cdc_union->interface[0] ) ); + if ( func->count > config->interfaces ) { + DBGC ( usb, "USB %s has invalid union functional " + "descriptor with %d interfaces\n", + func->name, func->count ); + return -ERANGE; + } + + /* Describe function */ + for ( i = 0 ; i < func->count ; i++ ) + func->interface[i] = cdc_union->interface[i]; + + return 0; + } + return 0; } @@ -842,7 +867,11 @@ usb_probe_all ( struct usb_device *usb, /* Mark interfaces as used */ for ( i = 0 ; i < func->count ; i++ ) { - assert ( func->interface[i] < config->interfaces ); + 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; } @@ -872,6 +901,7 @@ usb_probe_all ( struct usb_device *usb, err_probe: free ( func ); err_alloc: + err_interface: err_function: /* Continue registering other functions */ continue; |