diff --git a/src/drivers/bus/cdc.c b/src/drivers/bus/cdc.c new file mode 100644 index 000000000..935daff1a --- /dev/null +++ b/src/drivers/bus/cdc.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * 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 +#include +#include + +/** @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 bbdc29cc1..97c2e3fcf 100644 --- a/src/drivers/bus/usb.c +++ b/src/drivers/bus/usb.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @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; diff --git a/src/include/ipxe/cdc.h b/src/include/ipxe/cdc.h index 2eec598a9..929a6a659 100644 --- a/src/include/ipxe/cdc.h +++ b/src/include/ipxe/cdc.h @@ -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 */