diff options
author | John Michelau <john.michelau@motorola.com> | 2010-12-10 11:33:54 -0600 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2010-12-11 10:58:55 -0800 |
commit | f861368d37ee84404fd35ea2f37e8f94aa134012 (patch) | |
tree | 554a6a6efa6d168b9b28e0e2518fc73c15f3c09a | |
parent | 415c4295773a5e0f365cc2e9dcbf3ef3c378cc95 (diff) | |
download | qemu-f861368d37ee84404fd35ea2f37e8f94aa134012.tar.gz |
USB: gadget: android Fix gadget descriptor compliance for IAD's
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>
-rw-r--r-- | drivers/usb/gadget/android.c | 56 |
1 files changed, 43 insertions, 13 deletions
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index d9d4d2fd599..213e8a2d782 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -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) |