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 c13f6e747748b31d16c0d087fa4f8b1953826285..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,17 +282,21 @@ 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
        },
-       { USB_DEVICE(0x413C, 0x08133) }, /* Dell Computer Corp. Wireless 5720 VZW Mobile Broadband (EVDO Rev-A) Minicard GPS Port */
 
        { }
 };
@@ -628,7 +633,6 @@ static void sierra_instat_callback(struct urb *urb)
                        unsigned char signals = *((unsigned char *)
                                        urb->transfer_buffer +
                                        sizeof(struct usb_ctrlrequest));
-                       struct tty_struct *tty;
 
                        dev_dbg(&port->dev, "%s: signal x%x\n", __func__,
                                signals);
@@ -639,11 +643,8 @@ static void sierra_instat_callback(struct urb *urb)
                        portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
                        portdata->ri_state = ((signals & 0x08) ? 1 : 0);
 
-                       tty = tty_port_tty_get(&port->port);
-                       if (tty && !C_CLOCAL(tty) &&
-                                       old_dcd_state && !portdata->dcd_state)
-                               tty_hangup(tty);
-                       tty_kref_put(tty);
+                       if (old_dcd_state && !portdata->dcd_state)
+                               tty_port_tty_hangup(&port->port, true);
                } else {
                        dev_dbg(&port->dev, "%s: type %x req %x\n",
                                __func__, req_pkt->bRequestType,
@@ -772,36 +773,45 @@ 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);
 
        portdata->rts_state = 0;
        portdata->dtr_state = 0;
 
-       if (serial->dev) {
-               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);
-                       else
-                               usb_autopm_get_interface_no_resume(serial->interface);
-                               
-               }
-               mutex_unlock(&serial->disc_mutex);
-               spin_lock_irq(&intfdata->susp_lock);
-               portdata->opened = 0;
-               spin_unlock_irq(&intfdata->susp_lock);
+       mutex_lock(&serial->disc_mutex);
+       if (!serial->disconnected) {
+               /* odd error handling due to pm counters */
+               if (!usb_autopm_get_interface(serial->interface))
+                       sierra_send_setup(port);
+               else
+                       usb_autopm_get_interface_no_resume(serial->interface);
 
+       }
+       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);
 
-               /* Stop reading urbs */
-               sierra_stop_rx_urbs(port);
-               /* .. and release them */
-               for (i = 0; i < portdata->num_in_urbs; i++) {
-                       sierra_release_urb(portdata->in_urbs[i]);
-                       portdata->in_urbs[i] = NULL;
-               }
+       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]);
+               portdata->in_urbs[i] = NULL;
        }
 }
 
@@ -834,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;
 }
 
 
@@ -946,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;
@@ -962,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);
        }
@@ -1004,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++;
@@ -1011,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;
                        }
                }