Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / net / usb / cdc_ether.c
index 811b2dc..425e201 100644 (file)
@@ -83,6 +83,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
        struct cdc_state                *info = (void *) &dev->data;
        int                             status;
        int                             rndis;
+       bool                            android_rndis_quirk = false;
        struct usb_driver               *driver = driver_of(intf);
        struct usb_cdc_mdlm_desc        *desc = NULL;
        struct usb_cdc_mdlm_detail_desc *detail = NULL;
@@ -99,9 +100,7 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
                 */
                buf = dev->udev->actconfig->extra;
                len = dev->udev->actconfig->extralen;
-               if (len)
-                       dev_dbg(&intf->dev,
-                               "CDC descriptors on config\n");
+               dev_dbg(&intf->dev, "CDC descriptors on config\n");
        }
 
        /* Maybe CDC descriptors are after the endpoint?  This bug has
@@ -197,6 +196,11 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
                                        info->control,
                                        info->u->bSlaveInterface0,
                                        info->data);
+                               /* fall back to hard-wiring for RNDIS */
+                               if (rndis) {
+                                       android_rndis_quirk = true;
+                                       goto next_desc;
+                               }
                                goto bad_desc;
                        }
                        if (info->control != intf) {
@@ -273,11 +277,15 @@ next_desc:
        /* Microsoft ActiveSync based and some regular RNDIS devices lack the
         * CDC descriptors, so we'll hard-wire the interfaces and not check
         * for descriptors.
+        *
+        * Some Android RNDIS devices have a CDC Union descriptor pointing
+        * to non-existing interfaces.  Ignore that and attempt the same
+        * hard-wired 0 and 1 interfaces.
         */
-       if (rndis && !info->u) {
+       if (rndis && (!info->u || android_rndis_quirk)) {
                info->control = usb_ifnum_to_if(dev->udev, 0);
                info->data = usb_ifnum_to_if(dev->udev, 1);
-               if (!info->control || !info->data) {
+               if (!info->control || !info->data || info->control != intf) {
                        dev_dbg(&intf->dev,
                                "rndis: master #0/%p slave #1/%p\n",
                                info->control,
@@ -380,7 +388,7 @@ static void dumpspeed(struct usbnet *dev, __le32 *speeds)
                   __le32_to_cpu(speeds[1]) / 1000);
 }
 
-static void cdc_status(struct usbnet *dev, struct urb *urb)
+void usbnet_cdc_status(struct usbnet *dev, struct urb *urb)
 {
        struct usb_cdc_notification     *event;
 
@@ -420,12 +428,16 @@ static void cdc_status(struct usbnet *dev, struct urb *urb)
                break;
        }
 }
+EXPORT_SYMBOL_GPL(usbnet_cdc_status);
 
-static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
+int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        int                             status;
        struct cdc_state                *info = (void *) &dev->data;
 
+       BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
+                       < sizeof(struct cdc_state)));
+
        status = usbnet_generic_cdc_bind(dev, intf);
        if (status < 0)
                return status;
@@ -443,6 +455,7 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
         */
        return 0;
 }
+EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
 
 static int cdc_manage_power(struct usbnet *dev, int on)
 {
@@ -452,24 +465,27 @@ static int cdc_manage_power(struct usbnet *dev, int on)
 
 static const struct driver_info        cdc_info = {
        .description =  "CDC Ethernet Device",
-       .flags =        FLAG_ETHER,
+       .flags =        FLAG_ETHER | FLAG_POINTTOPOINT,
        // .check_connect = cdc_check_connect,
-       .bind =         cdc_bind,
+       .bind =         usbnet_cdc_bind,
        .unbind =       usbnet_cdc_unbind,
-       .status =       cdc_status,
+       .status =       usbnet_cdc_status,
        .manage_power = cdc_manage_power,
 };
 
-static const struct driver_info mbm_info = {
+static const struct driver_info wwan_info = {
        .description =  "Mobile Broadband Network Device",
        .flags =        FLAG_WWAN,
-       .bind =         cdc_bind,
+       .bind =         usbnet_cdc_bind,
        .unbind =       usbnet_cdc_unbind,
-       .status =       cdc_status,
+       .status =       usbnet_cdc_status,
+       .manage_power = cdc_manage_power,
 };
 
 /*-------------------------------------------------------------------------*/
 
+#define HUAWEI_VENDOR_ID       0x12D1
+#define NOVATEL_VENDOR_ID      0x1410
 
 static const struct usb_device_id      products [] = {
 /*
@@ -561,6 +577,20 @@ static const struct usb_device_id  products [] = {
        .driver_info            = 0,
 },
 
+/* LG Electronics VL600 wants additional headers on every frame */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(0x1004, 0x61aa, USB_CLASS_COMM,
+                       USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
+/* Logitech Harmony 900 - uses the pseudo-MDLM (BLAN) driver */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(0x046d, 0xc11f, USB_CLASS_COMM,
+                       USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+       .driver_info            = 0,
+},
+
 /*
  * WHITELIST!!!
  *
@@ -573,14 +603,38 @@ static const struct usb_device_id products [] = {
  * because of bugs/quirks in a given product (like Zaurus, above).
  */
 {
+       /* Novatel USB551L */
+       /* This match must come *before* the generic CDC-ETHER match so that
+        * we get FLAG_WWAN set on the device, since it's descriptors are
+        * generic CDC-ETHER.
+        */
+       .match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
+                | USB_DEVICE_ID_MATCH_PRODUCT
+                | USB_DEVICE_ID_MATCH_INT_INFO,
+       .idVendor               = NOVATEL_VENDOR_ID,
+       .idProduct              = 0xB001,
+       .bInterfaceClass        = USB_CLASS_COMM,
+       .bInterfaceSubClass     = USB_CDC_SUBCLASS_ETHERNET,
+       .bInterfaceProtocol     = USB_CDC_PROTO_NONE,
+       .driver_info = (unsigned long)&wwan_info,
+}, {
        USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
                        USB_CDC_PROTO_NONE),
        .driver_info = (unsigned long) &cdc_info,
 }, {
        USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
                        USB_CDC_PROTO_NONE),
-       .driver_info = (unsigned long)&mbm_info,
+       .driver_info = (unsigned long)&wwan_info,
 
+}, {
+       /* Various Huawei modems with a network port like the UMG1831 */
+       .match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
+                | USB_DEVICE_ID_MATCH_INT_INFO,
+       .idVendor               = HUAWEI_VENDOR_ID,
+       .bInterfaceClass        = USB_CLASS_COMM,
+       .bInterfaceSubClass     = USB_CDC_SUBCLASS_ETHERNET,
+       .bInterfaceProtocol     = 255,
+       .driver_info = (unsigned long)&wwan_info,
 },
        { },            // END
 };
@@ -597,21 +651,7 @@ static struct usb_driver cdc_driver = {
        .supports_autosuspend = 1,
 };
 
-
-static int __init cdc_init(void)
-{
-       BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data)
-                       < sizeof(struct cdc_state)));
-
-       return usb_register(&cdc_driver);
-}
-module_init(cdc_init);
-
-static void __exit cdc_exit(void)
-{
-       usb_deregister(&cdc_driver);
-}
-module_exit(cdc_exit);
+module_usb_driver(cdc_driver);
 
 MODULE_AUTHOR("David Brownell");
 MODULE_DESCRIPTION("USB CDC Ethernet devices");