USB: gadget: android Fix gadget descriptor compliance for IAD's
authorJohn Michelau <john.michelau@motorola.com>
Fri, 10 Dec 2010 17:33:54 +0000 (11:33 -0600)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 20:03:33 +0000 (12:03 -0800)
USB-IF core team mandates that composite devices containing
IAD's must use bDeviceClass=0xEF, bDeviceSubClass=0x02, &
bDeviceProtocol=0x01 instead of the usual 0x00 in all fields.
This is not a problem currently on Linux hosts, but it is a
problem when connecting to Windows hosts. Without this change
the default Microsoft composite driver will not group
interfaces properly, which is what allows child function
drivers with IAD's to load correctly.

Signed-off-by: Mike Lockwood <lockwood@android.com>
drivers/usb/gadget/android.c

index d9d4d2f..213e8a2 100644 (file)
@@ -351,6 +351,47 @@ void android_register_function(struct android_usb_function *f)
                bind_functions(dev);
 }
 
+void update_dev_desc(struct android_dev *dev)
+{
+       struct usb_function *f;
+       struct usb_function *last_enabled_f = NULL;
+       int num_enabled = 0;
+       int has_iad = 0;
+
+       dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE;
+       dev->cdev->desc.bDeviceSubClass = 0x00;
+       dev->cdev->desc.bDeviceProtocol = 0x00;
+
+       list_for_each_entry(f, &android_config_driver.functions, list) {
+               if (!f->disabled) {
+                       num_enabled++;
+                       last_enabled_f = f;
+                       if (f->descriptors[0]->bDescriptorType ==
+                                       USB_DT_INTERFACE_ASSOCIATION)
+                               has_iad = 1;
+               }
+               if (num_enabled > 1 && has_iad) {
+                       dev->cdev->desc.bDeviceClass = USB_CLASS_MISC;
+                       dev->cdev->desc.bDeviceSubClass = 0x02;
+                       dev->cdev->desc.bDeviceProtocol = 0x01;
+                       break;
+               }
+       }
+
+       if (num_enabled == 1) {
+#ifdef CONFIG_USB_ANDROID_RNDIS
+               if (!strcmp(last_enabled_f->name, "rndis")) {
+#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
+                       dev->cdev->desc.bDeviceClass =
+                                       USB_CLASS_WIRELESS_CONTROLLER;
+#else
+                       dev->cdev->desc.bDeviceClass = USB_CLASS_COMM;
+#endif
+               }
+#endif
+       }
+}
+
 void android_enable_function(struct usb_function *f, int enable)
 {
        struct android_dev *dev = _android_dev;
@@ -363,19 +404,6 @@ void android_enable_function(struct usb_function *f, int enable)
 #ifdef CONFIG_USB_ANDROID_RNDIS
                if (!strcmp(f->name, "rndis")) {
                        struct usb_function             *func;
-
-                       /* We need to specify the COMM class in the device descriptor
-                        * if we are using RNDIS.
-                        */
-                       if (enable)
-#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
-                               dev->cdev->desc.bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER;
-#else
-                               dev->cdev->desc.bDeviceClass = USB_CLASS_COMM;
-#endif
-                       else
-                               dev->cdev->desc.bDeviceClass = USB_CLASS_PER_INTERFACE;
-
                        /* Windows does not support other interfaces when RNDIS is enabled,
                         * so we disable UMS and MTP when RNDIS is on.
                         */
@@ -388,6 +416,8 @@ void android_enable_function(struct usb_function *f, int enable)
                }
 #endif
 
+               update_dev_desc(dev);
+
                product_id = get_product_id(dev);
                device_desc.idProduct = __constant_cpu_to_le16(product_id);
                if (dev->cdev)