UPSTREAM: USB: option: Add Vodafone/Huawei K5005 support
[cascardo/linux.git] / drivers / usb / serial / option.c
index f4465cc..048c6d3 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
@@ -666,6 +670,9 @@ 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_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 +728,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) },
@@ -1257,7 +1276,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,
@@ -1359,6 +1378,16 @@ static int option_probe(struct usb_serial *serial,
                serial->interface->cur_altsetting->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 +1397,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 +1450,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 */