mirror of
https://github.com/ipxe/ipxe
synced 2026-02-28 03:11:18 +03:00
[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>
This commit is contained in:
50
src/drivers/bus/cdc.c
Normal file
50
src/drivers/bus/cdc.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
#include <ipxe/usb.h>
|
#include <ipxe/usb.h>
|
||||||
|
#include <ipxe/cdc.h>
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
*
|
*
|
||||||
@@ -678,6 +679,7 @@ static int usb_function ( struct usb_function *func,
|
|||||||
struct usb_device *usb = func->usb;
|
struct usb_device *usb = func->usb;
|
||||||
struct usb_interface_association_descriptor *association;
|
struct usb_interface_association_descriptor *association;
|
||||||
struct usb_interface_descriptor *interface;
|
struct usb_interface_descriptor *interface;
|
||||||
|
struct cdc_union_descriptor *cdc_union;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* First, look for an interface association descriptor */
|
/* First, look for an interface association descriptor */
|
||||||
@@ -685,8 +687,7 @@ static int usb_function ( struct usb_function *func,
|
|||||||
if ( association ) {
|
if ( association ) {
|
||||||
|
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
if ( ( association->first + association->count ) >
|
if ( association->count > config->interfaces ) {
|
||||||
config->interfaces ) {
|
|
||||||
DBGC ( usb, "USB %s has invalid association [%d-%d)\n",
|
DBGC ( usb, "USB %s has invalid association [%d-%d)\n",
|
||||||
func->name, association->first,
|
func->name, association->first,
|
||||||
( association->first + association->count ) );
|
( association->first + association->count ) );
|
||||||
@@ -714,6 +715,30 @@ static int usb_function ( struct usb_function *func,
|
|||||||
memcpy ( &func->class, &interface->class, sizeof ( func->class ) );
|
memcpy ( &func->class, &interface->class, sizeof ( func->class ) );
|
||||||
func->count = 1;
|
func->count = 1;
|
||||||
func->interface[0] = first;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -842,7 +867,11 @@ usb_probe_all ( struct usb_device *usb,
|
|||||||
|
|
||||||
/* Mark interfaces as used */
|
/* Mark interfaces as used */
|
||||||
for ( i = 0 ; i < func->count ; i++ ) {
|
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;
|
used[ func->interface[i] ] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,6 +901,7 @@ usb_probe_all ( struct usb_device *usb,
|
|||||||
err_probe:
|
err_probe:
|
||||||
free ( func );
|
free ( func );
|
||||||
err_alloc:
|
err_alloc:
|
||||||
|
err_interface:
|
||||||
err_function:
|
err_function:
|
||||||
/* Continue registering other functions */
|
/* Continue registering other functions */
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -14,6 +14,19 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
/** Class code for communications devices */
|
/** Class code for communications devices */
|
||||||
#define USB_CLASS_CDC 2
|
#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 */
|
/** Ethernet descriptor subtype */
|
||||||
#define CDC_SUBTYPE_ETHERNET 15
|
#define CDC_SUBTYPE_ETHERNET 15
|
||||||
|
|
||||||
@@ -35,4 +48,8 @@ struct cdc_connection_speed_change {
|
|||||||
uint32_t up;
|
uint32_t up;
|
||||||
} __attribute__ (( packed ));
|
} __attribute__ (( packed ));
|
||||||
|
|
||||||
|
extern struct cdc_union_descriptor *
|
||||||
|
cdc_union_descriptor ( struct usb_configuration_descriptor *config,
|
||||||
|
struct usb_interface_descriptor *interface );
|
||||||
|
|
||||||
#endif /* _IPXE_CDC_H */
|
#endif /* _IPXE_CDC_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user