UPSTREAM: USB: option: Updated Huawei K4605 has better id
[cascardo/linux.git] / drivers / usb / serial / option.c
index 46ce786..59ac2d0 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
@@ -668,6 +670,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) },
@@ -734,7 +741,7 @@ static const struct usb_device_id option_ids[] = {
        /*
        { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_G2) },
        */
-       { USB_DEVICE_AND_INTERFACE_INFO(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_LTE_E362, 0xff, 0xff, 0xff) },
+       { 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) },
@@ -1271,7 +1278,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,
@@ -1373,6 +1380,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;
@@ -1382,6 +1399,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;
@@ -1426,7 +1452,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 */