]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[usb] Handle CDC union functional descriptors
authorMichael Brown <mcb30@ipxe.org>
Mon, 9 Feb 2015 16:33:49 +0000 (16:33 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 9 Feb 2015 16:34:55 +0000 (16:34 +0000)
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>
src/drivers/bus/cdc.c [new file with mode: 0644]
src/drivers/bus/usb.c
src/include/ipxe/cdc.h

diff --git a/src/drivers/bus/cdc.c b/src/drivers/bus/cdc.c
new file mode 100644 (file)
index 0000000..935daff
--- /dev/null
@@ -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;
+}
index bbdc29cc11da18d7d1cebd17b0a2181a154212cb..97c2e3fcf940fafc1d9ae44f7caf8a1774f48439 100644 (file)
@@ -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;
index 2eec598a985a9fd834d4802f367dd856d5a4e6d0..929a6a6595a60176b39cedd9555a7800b793a208 100644 (file)
@@ -14,6 +14,19 @@ FILE_LICENCE ( GPL2_OR_LATER );
 /** Class code for communications devices */
 #define USB_CLASS_CDC 2
 
+/** Union functional descriptor */
+struct cdc_union_descriptor {
+       /** Descriptor header */
+       struct usb_descriptor_header header;
+       /** Descriptor subtype */
+       uint8_t subtype;
+       /** Interfaces (variable-length) */
+       uint8_t interface[1];
+} __attribute__ (( packed ));
+
+/** Union functional descriptor subtype */
+#define CDC_SUBTYPE_UNION 6
+
 /** Ethernet descriptor subtype */
 #define CDC_SUBTYPE_ETHERNET 15
 
@@ -35,4 +48,8 @@ struct cdc_connection_speed_change {
        uint32_t up;
 } __attribute__ (( packed ));
 
+extern struct cdc_union_descriptor *
+cdc_union_descriptor ( struct usb_configuration_descriptor *config,
+                      struct usb_interface_descriptor *interface );
+
 #endif /* _IPXE_CDC_H */