UPSTREAM: USB: option: add ZTE MF821D
[cascardo/linux.git] / drivers / usb / serial / option.c
index f4465cc..6a90569 100644 (file)
@@ -47,6 +47,7 @@
 /* Function prototypes */
 static int  option_probe(struct usb_serial *serial,
                        const struct usb_device_id *id);
+static void option_release(struct usb_serial *serial);
 static int option_send_setup(struct usb_serial_port *port);
 static void option_instat_callback(struct urb *urb);
 
@@ -150,6 +151,7 @@ static void option_instat_callback(struct urb *urb);
 #define HUAWEI_PRODUCT_E14AC                   0x14AC
 #define HUAWEI_PRODUCT_K3806                   0x14AE
 #define HUAWEI_PRODUCT_K4605                   0x14C6
+#define HUAWEI_PRODUCT_K5005                   0x14C8
 #define HUAWEI_PRODUCT_K3770                   0x14C9
 #define HUAWEI_PRODUCT_K3771                   0x14CA
 #define HUAWEI_PRODUCT_K4510                   0x14CB
@@ -231,9 +233,11 @@ static void option_instat_callback(struct urb *urb);
 #define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED        0x8001
 #define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED        0x9000
 #define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED        0x9001
+#define NOVATELWIRELESS_PRODUCT_LTE_E362       0X9010
 #define NOVATELWIRELESS_PRODUCT_G1             0xA001
 #define NOVATELWIRELESS_PRODUCT_G1_M           0xA002
 #define NOVATELWIRELESS_PRODUCT_G2             0xA010
+#define NOVATELWIRELESS_PRODUCT_GW             0XB001
 
 /* AMOI PRODUCTS */
 #define AMOI_VENDOR_ID                         0x1614
@@ -542,6 +546,10 @@ static const struct option_blacklist_info net_intf1_blacklist = {
        .reserved = BIT(1),
 };
 
+static const struct option_blacklist_info net_intf2_blacklist = {
+       .reserved = BIT(2),
+};
+
 static const struct option_blacklist_info net_intf3_blacklist = {
        .reserved = BIT(3),
 };
@@ -666,6 +674,11 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
                .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x31) },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x32) },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x31) },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x32) },
+       { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x33) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
        { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
@@ -721,7 +734,19 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED) },
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G1) },
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G1_M) },
+       /*
+        * Temporarily take out the VID/PID 1410:a010 to ensure that the
+        * qcserial driver is always used for the Novatel Gobi 2000 modem.
+        * See crosbug.com/32819 for details.
+        *
+        * TODO(benchan): Revert this after deprecating support for Gobi 2000
+        * modems.
+        */
+       /*
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G2) },
+       */
+       { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_LTE_E362) },
+       { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GW, 0xff, 0xff, 0xff) },
 
        { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
        { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
@@ -904,6 +929,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff),
+         .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
          .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
@@ -1080,6 +1107,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) },
+       { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff),
+               .driver_info = (kernel_ulong_t)&net_intf2_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff,
          0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist },
        { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
@@ -1257,7 +1286,7 @@ static struct usb_serial_driver option_1port_device = {
        .ioctl             = usb_wwan_ioctl,
        .attach            = usb_wwan_startup,
        .disconnect        = usb_wwan_disconnect,
-       .release           = usb_wwan_release,
+       .release           = option_release,
        .read_int_callback = option_instat_callback,
 #ifdef CONFIG_PM
        .suspend           = usb_wwan_suspend,
@@ -1330,35 +1359,53 @@ static int option_probe(struct usb_serial *serial,
                        const struct usb_device_id *id)
 {
        struct usb_wwan_intf_private *data;
+       struct usb_interface_descriptor *iface_desc =
+                               &serial->interface->cur_altsetting->desc;
+       struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
 
-       /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
-       if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
-               serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
+       /*
+        * D-Link DWM 652 still exposes CD-Rom emulation interface in modem
+        * mode.
+        */
+       if (dev_desc->idVendor == DLINK_VENDOR_ID &&
+               dev_desc->idProduct == DLINK_PRODUCT_DWM_652 &&
+               iface_desc->bInterfaceClass == 0x08)
                return -ENODEV;
 
        /* Bandrich modem and AT command interface is 0xff */
-       if ((serial->dev->descriptor.idVendor == BANDRICH_VENDOR_ID ||
-               serial->dev->descriptor.idVendor == PIRELLI_VENDOR_ID) &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
+       if ((dev_desc->idVendor == BANDRICH_VENDOR_ID ||
+               dev_desc->idVendor == PIRELLI_VENDOR_ID) &&
+               iface_desc->bInterfaceClass != 0xff)
                return -ENODEV;
-
-       /* Don't bind reserved interfaces (like network ones) which often have
+       /*
+        * Don't bind reserved interfaces (like network ones) which often have
         * the same class/subclass/protocol as the serial interfaces.  Look at
         * the Windows driver .INF files for reserved interface numbers.
         */
        if (is_blacklisted(
-               serial->interface->cur_altsetting->desc.bInterfaceNumber,
+               iface_desc->bInterfaceNumber,
                OPTION_BLACKLIST_RESERVED_IF,
                (const struct option_blacklist_info *) id->driver_info))
                return -ENODEV;
-
-       /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */
-       if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID &&
-               serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 &&
-               serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA)
+       /*
+        * Don't bind network interface on Samsung GT-B3730, it is handled by
+        * a separate module.
+        */
+       if (dev_desc->idVendor == SAMSUNG_VENDOR_ID &&
+               dev_desc->idProduct == SAMSUNG_PRODUCT_GT_B3730 &&
+               iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
                return -ENODEV;
 
+       /* For Novatel E362 modem, set to configuration #1 if identity morphing
+        * is detected.
+        */
+       if (serial->dev->descriptor.idVendor == NOVATELWIRELESS_VENDOR_ID &&
+               serial->dev->descriptor.idProduct == NOVATELWIRELESS_PRODUCT_LTE_E362 &&
+               serial->dev->actconfig->desc.bConfigurationValue != 1) {
+               if (usb_driver_set_configuration(serial->dev, 1) != 0)
+                       return -ENODEV;
+       }
+
        data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
@@ -1368,6 +1415,15 @@ static int option_probe(struct usb_serial *serial,
        return 0;
 }
 
+static void option_release(struct usb_serial *serial)
+{
+       struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+
+       usb_wwan_release(serial);
+
+       kfree(priv);
+}
+
 static void option_instat_callback(struct urb *urb)
 {
        int err;
@@ -1412,7 +1468,7 @@ static void option_instat_callback(struct urb *urb)
                        dbg("%s: type %x req %x", __func__,
                                req_pkt->bRequestType, req_pkt->bRequest);
                }
-       } else
+       } else if (status != -ESHUTDOWN && status != -ENOENT)
                err("%s: error %d", __func__, status);
 
        /* Resubmit urb so we continue receiving IRQ data */