Merge branch 'v3.10/topic/gator' into linux-linaro-lsk-v3.10
[firefly-linux-kernel-4.4.55.git] / drivers / usb / serial / sierra.c
index 2df566c0e9e8ee2361efdeeb0a556dd1014d5045..5aaa2b675116effe0149b7294bfae1be18dc2977 100644 (file)
@@ -58,6 +58,7 @@ struct sierra_intf_private {
        spinlock_t susp_lock;
        unsigned int suspended:1;
        int in_flight;
+       unsigned int open_ports;
 };
 
 static int sierra_set_power_state(struct usb_device *udev, __u16 swiState)
@@ -281,14 +282,19 @@ static const struct usb_device_id id_table[] = {
        /* Sierra Wireless HSPA Non-Composite Device */
        { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)},
        { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */
-       { USB_DEVICE(0x1199, 0x68A3),   /* Sierra Wireless Direct IP modems */
+       /* Sierra Wireless Direct IP modems */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68A3, 0xFF, 0xFF, 0xFF),
+         .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+       },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF),
          .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
        },
        /* AT&T Direct IP LTE modems */
        { USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
          .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
        },
-       { USB_DEVICE(0x0f3d, 0x68A3),   /* Airprime/Sierra Wireless Direct IP modems */
+       /* Airprime/Sierra Wireless Direct IP modems */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68A3, 0xFF, 0xFF, 0xFF),
          .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
        },
 
@@ -767,6 +773,7 @@ static void sierra_close(struct usb_serial_port *port)
        struct usb_serial *serial = port->serial;
        struct sierra_port_private *portdata;
        struct sierra_intf_private *intfdata = port->serial->private;
+       struct urb *urb;
 
        portdata = usb_get_serial_port_data(port);
 
@@ -775,7 +782,6 @@ static void sierra_close(struct usb_serial_port *port)
 
        mutex_lock(&serial->disc_mutex);
        if (!serial->disconnected) {
-               serial->interface->needs_remote_wakeup = 0;
                /* odd error handling due to pm counters */
                if (!usb_autopm_get_interface(serial->interface))
                        sierra_send_setup(port);
@@ -786,8 +792,22 @@ static void sierra_close(struct usb_serial_port *port)
        mutex_unlock(&serial->disc_mutex);
        spin_lock_irq(&intfdata->susp_lock);
        portdata->opened = 0;
+       if (--intfdata->open_ports == 0)
+               serial->interface->needs_remote_wakeup = 0;
        spin_unlock_irq(&intfdata->susp_lock);
 
+       for (;;) {
+               urb = usb_get_from_anchor(&portdata->delayed);
+               if (!urb)
+                       break;
+               kfree(urb->transfer_buffer);
+               usb_free_urb(urb);
+               usb_autopm_put_interface_async(serial->interface);
+               spin_lock(&portdata->lock);
+               portdata->outstanding_urbs--;
+               spin_unlock(&portdata->lock);
+       }
+
        sierra_stop_rx_urbs(port);
        for (i = 0; i < portdata->num_in_urbs; i++) {
                sierra_release_urb(portdata->in_urbs[i]);
@@ -824,23 +844,29 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
                        usb_sndbulkpipe(serial->dev, endpoint) | USB_DIR_IN);
 
        err = sierra_submit_rx_urbs(port, GFP_KERNEL);
-       if (err) {
-               /* get rid of everything as in close */
-               sierra_close(port);
-               /* restore balance for autopm */
-               if (!serial->disconnected)
-                       usb_autopm_put_interface(serial->interface);
-               return err;
-       }
+       if (err)
+               goto err_submit;
+
        sierra_send_setup(port);
 
-       serial->interface->needs_remote_wakeup = 1;
        spin_lock_irq(&intfdata->susp_lock);
        portdata->opened = 1;
+       if (++intfdata->open_ports == 1)
+               serial->interface->needs_remote_wakeup = 1;
        spin_unlock_irq(&intfdata->susp_lock);
        usb_autopm_put_interface(serial->interface);
 
        return 0;
+
+err_submit:
+       sierra_stop_rx_urbs(port);
+
+       for (i = 0; i < portdata->num_in_urbs; i++) {
+               sierra_release_urb(portdata->in_urbs[i]);
+               portdata->in_urbs[i] = NULL;
+       }
+
+       return err;
 }
 
 
@@ -936,6 +962,7 @@ static int sierra_port_remove(struct usb_serial_port *port)
        struct sierra_port_private *portdata;
 
        portdata = usb_get_serial_port_data(port);
+       usb_set_serial_port_data(port, NULL);
        kfree(portdata);
 
        return 0;
@@ -952,6 +979,8 @@ static void stop_read_write_urbs(struct usb_serial *serial)
        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
                portdata = usb_get_serial_port_data(port);
+               if (!portdata)
+                       continue;
                sierra_stop_rx_urbs(port);
                usb_kill_anchored_urbs(&portdata->active);
        }
@@ -994,6 +1023,9 @@ static int sierra_resume(struct usb_serial *serial)
                port = serial->port[i];
                portdata = usb_get_serial_port_data(port);
 
+               if (!portdata)
+                       continue;
+
                while ((urb = usb_get_from_anchor(&portdata->delayed))) {
                        usb_anchor_urb(urb, &portdata->active);
                        intfdata->in_flight++;
@@ -1001,8 +1033,12 @@ static int sierra_resume(struct usb_serial *serial)
                        if (err < 0) {
                                intfdata->in_flight--;
                                usb_unanchor_urb(urb);
-                               usb_scuttle_anchored_urbs(&portdata->delayed);
-                               break;
+                               kfree(urb->transfer_buffer);
+                               usb_free_urb(urb);
+                               spin_lock(&portdata->lock);
+                               portdata->outstanding_urbs--;
+                               spin_unlock(&portdata->lock);
+                               continue;
                        }
                }