summaryrefslogtreecommitdiffstats
path: root/src/drivers/bus
diff options
context:
space:
mode:
authorMichael Brown2015-02-09 17:33:49 +0100
committerMichael Brown2015-02-09 17:34:55 +0100
commitcf153f60a54b4706225f6326dc65b9343148c9f3 (patch)
tree7b395ac183c2d63895bae1cd83718301e78f8060 /src/drivers/bus
parent[usb] Try multiple USB device configurations (diff)
downloadipxe-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.c50
-rw-r--r--src/drivers/bus/usb.c36
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;