Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[sfrench/cifs-2.6.git] / drivers / net / usb / qmi_wwan.c
index 679e404a5224fc33b8591b16a59e0910adb4c4cd..5c3ac97519b7d245141ad91551b887a6cfc9b7fb 100644 (file)
@@ -63,6 +63,7 @@ enum qmi_wwan_flags {
 
 enum qmi_wwan_quirks {
        QMI_WWAN_QUIRK_DTR = 1 << 0,    /* needs "set DTR" request */
+       QMI_WWAN_QUIRK_QUECTEL_DYNCFG = 1 << 1, /* check num. endpoints */
 };
 
 struct qmimux_hdr {
@@ -845,6 +846,16 @@ static const struct driver_info    qmi_wwan_info_quirk_dtr = {
        .data           = QMI_WWAN_QUIRK_DTR,
 };
 
+static const struct driver_info        qmi_wwan_info_quirk_quectel_dyncfg = {
+       .description    = "WWAN/QMI device",
+       .flags          = FLAG_WWAN | FLAG_SEND_ZLP,
+       .bind           = qmi_wwan_bind,
+       .unbind         = qmi_wwan_unbind,
+       .manage_power   = qmi_wwan_manage_power,
+       .rx_fixup       = qmi_wwan_rx_fixup,
+       .data           = QMI_WWAN_QUIRK_DTR | QMI_WWAN_QUIRK_QUECTEL_DYNCFG,
+};
+
 #define HUAWEI_VENDOR_ID       0x12D1
 
 /* map QMI/wwan function by a fixed interface number */
@@ -865,6 +876,15 @@ static const struct driver_info    qmi_wwan_info_quirk_dtr = {
 #define QMI_GOBI_DEVICE(vend, prod) \
        QMI_FIXED_INTF(vend, prod, 0)
 
+/* Quectel does not use fixed interface numbers on at least some of their
+ * devices. We need to check the number of endpoints to ensure that we bind to
+ * the correct interface.
+ */
+#define QMI_QUIRK_QUECTEL_DYNCFG(vend, prod) \
+       USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \
+                                     USB_SUBCLASS_VENDOR_SPEC, 0xff), \
+       .driver_info = (unsigned long)&qmi_wwan_info_quirk_quectel_dyncfg
+
 static const struct usb_device_id products[] = {
        /* 1. CDC ECM like devices match on the control interface */
        {       /* Huawei E392, E398 and possibly others sharing both device id and more... */
@@ -969,20 +989,9 @@ static const struct usb_device_id products[] = {
                USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
                .driver_info = (unsigned long)&qmi_wwan_info,
        },
-       {       /* Quectel EP06/EG06/EM06 */
-               USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x0306,
-                                             USB_CLASS_VENDOR_SPEC,
-                                             USB_SUBCLASS_VENDOR_SPEC,
-                                             0xff),
-               .driver_info        = (unsigned long)&qmi_wwan_info_quirk_dtr,
-       },
-       {       /* Quectel EG12/EM12 */
-               USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x0512,
-                                             USB_CLASS_VENDOR_SPEC,
-                                             USB_SUBCLASS_VENDOR_SPEC,
-                                             0xff),
-               .driver_info        = (unsigned long)&qmi_wwan_info_quirk_dtr,
-       },
+       {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0125)},     /* Quectel EC25, EC20 R2.0  Mini PCIe */
+       {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0306)},     /* Quectel EP06/EG06/EM06 */
+       {QMI_QUIRK_QUECTEL_DYNCFG(0x2c7c, 0x0512)},     /* Quectel EG12/EM12 */
 
        /* 3. Combined interface devices matching on interface number */
        {QMI_FIXED_INTF(0x0408, 0xea42, 4)},    /* Yota / Megafon M100-1 */
@@ -1281,7 +1290,6 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)},    /* HP lt4120 Snapdragon X5 LTE */
        {QMI_FIXED_INTF(0x22de, 0x9061, 3)},    /* WeTelecom WPD-600N */
        {QMI_QUIRK_SET_DTR(0x1e0e, 0x9001, 5)}, /* SIMCom 7100E, 7230E, 7600E ++ */
-       {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0  Mini PCIe */
        {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
        {QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */
        {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)},    /* Quectel BG96 */
@@ -1361,27 +1369,12 @@ static bool quectel_ec20_detected(struct usb_interface *intf)
        return false;
 }
 
-static bool quectel_diag_detected(struct usb_interface *intf)
-{
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct usb_interface_descriptor intf_desc = intf->cur_altsetting->desc;
-       u16 id_vendor = le16_to_cpu(dev->descriptor.idVendor);
-       u16 id_product = le16_to_cpu(dev->descriptor.idProduct);
-
-       if (id_vendor != 0x2c7c || intf_desc.bNumEndpoints != 2)
-               return false;
-
-       if (id_product == 0x0306 || id_product == 0x0512)
-               return true;
-       else
-               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;
+       const struct driver_info *info;
 
        /* Workaround to enable dynamic IDs.  This disables usbnet
         * blacklisting functionality.  Which, if required, can be
@@ -1415,10 +1408,14 @@ static int qmi_wwan_probe(struct usb_interface *intf,
         * we need to match on class/subclass/protocol. These values are
         * identical for the diagnostic- and QMI-interface, but bNumEndpoints is
         * different. Ignore the current interface if the number of endpoints
-        * the number for the diag interface (two).
+        * equals the number for the diag interface (two).
         */
-       if (quectel_diag_detected(intf))
-               return -ENODEV;
+       info = (void *)&id->driver_info;
+
+       if (info->data & QMI_WWAN_QUIRK_QUECTEL_DYNCFG) {
+               if (desc->bNumEndpoints == 2)
+                       return -ENODEV;
+       }
 
        return usbnet_probe(intf, id);
 }