Browse Source

[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>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
cf153f60a5
3 changed files with 100 additions and 3 deletions
  1. 50
    0
      src/drivers/bus/cdc.c
  2. 33
    3
      src/drivers/bus/usb.c
  3. 17
    0
      src/include/ipxe/cdc.h

+ 50
- 0
src/drivers/bus/cdc.c View File

@@ -0,0 +1,50 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stddef.h>
23
+#include <ipxe/usb.h>
24
+#include <ipxe/cdc.h>
25
+
26
+/** @file
27
+ *
28
+ * USB Communications Device Class (CDC)
29
+ *
30
+ */
31
+
32
+/**
33
+ * Locate CDC union functional descriptor
34
+ *
35
+ * @v config		Configuration descriptor
36
+ * @v interface		Interface descriptor
37
+ * @ret desc		Union functional descriptor, or NULL if not found
38
+ */
39
+struct cdc_union_descriptor *
40
+cdc_union_descriptor ( struct usb_configuration_descriptor *config,
41
+		       struct usb_interface_descriptor *interface ) {
42
+	struct cdc_union_descriptor *desc;
43
+
44
+	for_each_interface_descriptor ( desc, config, interface ) {
45
+		if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) &&
46
+		     ( desc->subtype == CDC_SUBTYPE_UNION ) )
47
+			return desc;
48
+	}
49
+	return NULL;
50
+}

+ 33
- 3
src/drivers/bus/usb.c View File

@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
27 27
 #include <assert.h>
28 28
 #include <byteswap.h>
29 29
 #include <ipxe/usb.h>
30
+#include <ipxe/cdc.h>
30 31
 
31 32
 /** @file
32 33
  *
@@ -678,6 +679,7 @@ static int usb_function ( struct usb_function *func,
678 679
 	struct usb_device *usb = func->usb;
679 680
 	struct usb_interface_association_descriptor *association;
680 681
 	struct usb_interface_descriptor *interface;
682
+	struct cdc_union_descriptor *cdc_union;
681 683
 	unsigned int i;
682 684
 
683 685
 	/* First, look for an interface association descriptor */
@@ -685,8 +687,7 @@ static int usb_function ( struct usb_function *func,
685 687
 	if ( association ) {
686 688
 
687 689
 		/* Sanity check */
688
-		if ( ( association->first + association->count ) >
689
-		     config->interfaces ) {
690
+		if ( association->count > config->interfaces ) {
690 691
 			DBGC ( usb, "USB %s has invalid association [%d-%d)\n",
691 692
 			       func->name, association->first,
692 693
 			       ( association->first + association->count ) );
@@ -714,6 +715,30 @@ static int usb_function ( struct usb_function *func,
714 715
 	memcpy ( &func->class, &interface->class, sizeof ( func->class ) );
715 716
 	func->count = 1;
716 717
 	func->interface[0] = first;
718
+
719
+	/* Look for a CDC union descriptor, if applicable */
720
+	if ( ( func->class.class == USB_CLASS_CDC ) &&
721
+	     ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) {
722
+
723
+		/* Determine interface count */
724
+		func->count = ( ( cdc_union->header.len -
725
+				  offsetof ( typeof ( *cdc_union ),
726
+					     interface[0] ) ) /
727
+				sizeof ( cdc_union->interface[0] ) );
728
+		if ( func->count > config->interfaces ) {
729
+			DBGC ( usb, "USB %s has invalid union functional "
730
+			       "descriptor with %d interfaces\n",
731
+			       func->name, func->count );
732
+			return -ERANGE;
733
+		}
734
+
735
+		/* Describe function */
736
+		for ( i = 0 ; i < func->count ; i++ )
737
+			func->interface[i] = cdc_union->interface[i];
738
+
739
+		return 0;
740
+	}
741
+
717 742
 	return 0;
718 743
 }
719 744
 
@@ -842,7 +867,11 @@ usb_probe_all ( struct usb_device *usb,
842 867
 
843 868
 		/* Mark interfaces as used */
844 869
 		for ( i = 0 ; i < func->count ; i++ ) {
845
-			assert ( func->interface[i] < config->interfaces );
870
+			if ( func->interface[i] >= config->interfaces ) {
871
+				DBGC ( usb, "USB %s has invalid interface %d\n",
872
+				       func->name, func->interface[i] );
873
+				goto err_interface;
874
+			}
846 875
 			used[ func->interface[i] ] = 1;
847 876
 		}
848 877
 
@@ -872,6 +901,7 @@ usb_probe_all ( struct usb_device *usb,
872 901
 	err_probe:
873 902
 		free ( func );
874 903
 	err_alloc:
904
+	err_interface:
875 905
 	err_function:
876 906
 		/* Continue registering other functions */
877 907
 		continue;

+ 17
- 0
src/include/ipxe/cdc.h View File

@@ -14,6 +14,19 @@ FILE_LICENCE ( GPL2_OR_LATER );
14 14
 /** Class code for communications devices */
15 15
 #define USB_CLASS_CDC 2
16 16
 
17
+/** Union functional descriptor */
18
+struct cdc_union_descriptor {
19
+	/** Descriptor header */
20
+	struct usb_descriptor_header header;
21
+	/** Descriptor subtype */
22
+	uint8_t subtype;
23
+	/** Interfaces (variable-length) */
24
+	uint8_t interface[1];
25
+} __attribute__ (( packed ));
26
+
27
+/** Union functional descriptor subtype */
28
+#define CDC_SUBTYPE_UNION 6
29
+
17 30
 /** Ethernet descriptor subtype */
18 31
 #define CDC_SUBTYPE_ETHERNET 15
19 32
 
@@ -35,4 +48,8 @@ struct cdc_connection_speed_change {
35 48
 	uint32_t up;
36 49
 } __attribute__ (( packed ));
37 50
 
51
+extern struct cdc_union_descriptor *
52
+cdc_union_descriptor ( struct usb_configuration_descriptor *config,
53
+		       struct usb_interface_descriptor *interface );
54
+
38 55
 #endif /* _IPXE_CDC_H */

Loading…
Cancel
Save