These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / usb / qmi_wwan.c
index f603f36..982e0ac 100644 (file)
@@ -229,11 +229,11 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
        u8 *buf = intf->cur_altsetting->extra;
        int len = intf->cur_altsetting->extralen;
        struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
-       struct usb_cdc_union_desc *cdc_union = NULL;
-       struct usb_cdc_ether_desc *cdc_ether = NULL;
-       u32 found = 0;
+       struct usb_cdc_union_desc *cdc_union;
+       struct usb_cdc_ether_desc *cdc_ether;
        struct usb_driver *driver = driver_of(intf);
        struct qmi_wwan_state *info = (void *)&dev->data;
+       struct usb_cdc_parsed_header hdr;
 
        BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) <
                      sizeof(struct qmi_wwan_state)));
@@ -243,63 +243,9 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
        info->data = intf;
 
        /* and a number of CDC descriptors */
-       while (len > 3) {
-               struct usb_descriptor_header *h = (void *)buf;
-
-               /* ignore any misplaced descriptors */
-               if (h->bDescriptorType != USB_DT_CS_INTERFACE)
-                       goto next_desc;
-
-               /* buf[2] is CDC descriptor subtype */
-               switch (buf[2]) {
-               case USB_CDC_HEADER_TYPE:
-                       if (found & 1 << USB_CDC_HEADER_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC header\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
-                               dev_dbg(&intf->dev, "CDC header len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       break;
-               case USB_CDC_UNION_TYPE:
-                       if (found & 1 << USB_CDC_UNION_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC union\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
-                               dev_dbg(&intf->dev, "CDC union len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       cdc_union = (struct usb_cdc_union_desc *)buf;
-                       break;
-               case USB_CDC_ETHERNET_TYPE:
-                       if (found & 1 << USB_CDC_ETHERNET_TYPE) {
-                               dev_dbg(&intf->dev, "extra CDC ether\n");
-                               goto err;
-                       }
-                       if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
-                               dev_dbg(&intf->dev, "CDC ether len %u\n",
-                                       h->bLength);
-                               goto err;
-                       }
-                       cdc_ether = (struct usb_cdc_ether_desc *)buf;
-                       break;
-               }
-
-               /* Remember which CDC functional descriptors we've seen.  Works
-                * for all types we care about, of which USB_CDC_ETHERNET_TYPE
-                * (0x0f) is the highest numbered
-                */
-               if (buf[2] < 32)
-                       found |= 1 << buf[2];
-
-next_desc:
-               len -= h->bLength;
-               buf += h->bLength;
-       }
+       cdc_parse_cdc_header(&hdr, intf, buf, len);
+       cdc_union = hdr.usb_cdc_union_desc;
+       cdc_ether = hdr.usb_cdc_ether_desc;
 
        /* Use separate control and data interfaces if we found a CDC Union */
        if (cdc_union) {
@@ -539,9 +485,14 @@ static const struct usb_device_id products[] = {
                                              USB_CDC_PROTO_NONE),
                .driver_info        = (unsigned long)&qmi_wwan_info,
        },
+       {       /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
+               USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
+               .driver_info = (unsigned long)&qmi_wwan_info,
+       },
 
        /* 3. Combined interface devices matching on interface number */
        {QMI_FIXED_INTF(0x0408, 0xea42, 4)},    /* Yota / Megafon M100-1 */
+       {QMI_FIXED_INTF(0x05c6, 0x6001, 3)},    /* 4G LTE usb-modem U901 */
        {QMI_FIXED_INTF(0x05c6, 0x7000, 0)},
        {QMI_FIXED_INTF(0x05c6, 0x7001, 1)},
        {QMI_FIXED_INTF(0x05c6, 0x7002, 1)},
@@ -752,11 +703,12 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x114f, 0x68a2, 8)},    /* Sierra Wireless MC7750 */
        {QMI_FIXED_INTF(0x1199, 0x68a2, 8)},    /* Sierra Wireless MC7710 in QMI mode */
        {QMI_FIXED_INTF(0x1199, 0x68a2, 19)},   /* Sierra Wireless MC7710 in QMI mode */
-       {QMI_FIXED_INTF(0x1199, 0x68c0, 8)},    /* Sierra Wireless MC73xx */
-       {QMI_FIXED_INTF(0x1199, 0x68c0, 10)},   /* Sierra Wireless MC73xx */
+       {QMI_FIXED_INTF(0x1199, 0x68c0, 8)},    /* Sierra Wireless MC7304/MC7354 */
+       {QMI_FIXED_INTF(0x1199, 0x68c0, 10)},   /* Sierra Wireless MC7304/MC7354 */
        {QMI_FIXED_INTF(0x1199, 0x901c, 8)},    /* Sierra Wireless EM7700 */
        {QMI_FIXED_INTF(0x1199, 0x901f, 8)},    /* Sierra Wireless EM7355 */
        {QMI_FIXED_INTF(0x1199, 0x9041, 8)},    /* Sierra Wireless MC7305/MC7355 */
+       {QMI_FIXED_INTF(0x1199, 0x9041, 10)},   /* Sierra Wireless MC7305/MC7355 */
        {QMI_FIXED_INTF(0x1199, 0x9051, 8)},    /* Netgear AirCard 340U */
        {QMI_FIXED_INTF(0x1199, 0x9053, 8)},    /* Sierra Wireless Modem */
        {QMI_FIXED_INTF(0x1199, 0x9054, 8)},    /* Sierra Wireless Modem */
@@ -764,12 +716,17 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1199, 0x9056, 8)},    /* Sierra Wireless Modem */
        {QMI_FIXED_INTF(0x1199, 0x9057, 8)},
        {QMI_FIXED_INTF(0x1199, 0x9061, 8)},    /* Sierra Wireless Modem */
+       {QMI_FIXED_INTF(0x1199, 0x9070, 8)},    /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9070, 10)},   /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9071, 8)},    /* Sierra Wireless MC74xx/EM74xx */
+       {QMI_FIXED_INTF(0x1199, 0x9071, 10)},   /* Sierra Wireless MC74xx/EM74xx */
        {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},    /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
        {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)},    /* Alcatel L800MA */
        {QMI_FIXED_INTF(0x2357, 0x0201, 4)},    /* TP-LINK HSUPA Modem MA180 */
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
        {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)},    /* Telit LE920 */
+       {QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)},    /* XS Stick W100-2 from 4G Systems */
        {QMI_FIXED_INTF(0x0b3c, 0xc000, 4)},    /* Olivetti Olicard 100 */
        {QMI_FIXED_INTF(0x0b3c, 0xc001, 4)},    /* Olivetti Olicard 120 */
        {QMI_FIXED_INTF(0x0b3c, 0xc002, 4)},    /* Olivetti Olicard 140 */
@@ -784,7 +741,9 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x413c, 0x81a4, 8)},    /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
        {QMI_FIXED_INTF(0x413c, 0x81a8, 8)},    /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
        {QMI_FIXED_INTF(0x413c, 0x81a9, 8)},    /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
-       {QMI_FIXED_INTF(0x03f0, 0x581d, 4)},    /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
+       {QMI_FIXED_INTF(0x413c, 0x81b1, 8)},    /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
+       {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)},    /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+       {QMI_FIXED_INTF(0x22de, 0x9061, 3)},    /* WeTelecom WPD-600N */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
@@ -815,6 +774,7 @@ static const struct usb_device_id products[] = {
        {QMI_GOBI_DEVICE(0x05c6, 0x9245)},      /* Samsung Gobi 2000 Modem device (VL176) */
        {QMI_GOBI_DEVICE(0x03f0, 0x251d)},      /* HP Gobi 2000 Modem device (VP412) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9215)},      /* Acer Gobi 2000 Modem device (VP413) */
+       {QMI_FIXED_INTF(0x05c6, 0x9215, 4)},    /* Quectel EC20 Mini PCIe */
        {QMI_GOBI_DEVICE(0x05c6, 0x9265)},      /* Asus Gobi 2000 Modem device (VR305) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9235)},      /* Top Global Gobi 2000 Modem device (VR306) */
        {QMI_GOBI_DEVICE(0x05c6, 0x9275)},      /* iRex Technologies Gobi 2000 Modem device (VR307) */
@@ -846,10 +806,24 @@ static const struct usb_device_id products[] = {
 };
 MODULE_DEVICE_TABLE(usb, products);
 
+static bool quectel_ec20_detected(struct usb_interface *intf)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+
+       if (dev->actconfig &&
+           le16_to_cpu(dev->descriptor.idVendor) == 0x05c6 &&
+           le16_to_cpu(dev->descriptor.idProduct) == 0x9215 &&
+           dev->actconfig->desc.bNumInterfaces == 5)
+               return true;
+
+       return false;
+}
+
 static int qmi_wwan_probe(struct usb_interface *intf,
                          const struct usb_device_id *prod)
 {
        struct usb_device_id *id = (struct usb_device_id *)prod;
+       struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
 
        /* Workaround to enable dynamic IDs.  This disables usbnet
         * blacklisting functionality.  Which, if required, can be
@@ -861,6 +835,12 @@ static int qmi_wwan_probe(struct usb_interface *intf,
                id->driver_info = (unsigned long)&qmi_wwan_info;
        }
 
+       /* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */
+       if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) {
+               dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n");
+               return -ENODEV;
+       }
+
        return usbnet_probe(intf, id);
 }