|
@@ -596,6 +596,7 @@ static struct usb_driver option_driver = {
|
|
|
#ifdef CONFIG_PM
|
|
|
.suspend = usb_serial_suspend,
|
|
|
.resume = usb_serial_resume,
|
|
|
+ .supports_autosuspend = 1,
|
|
|
#endif
|
|
|
.id_table = option_ids,
|
|
|
.no_dynamic_id = 1,
|
|
@@ -643,6 +644,12 @@ static int debug;
|
|
|
#define IN_BUFLEN 4096
|
|
|
#define OUT_BUFLEN 4096
|
|
|
|
|
|
+struct option_intf_private {
|
|
|
+ spinlock_t susp_lock;
|
|
|
+ unsigned int suspended:1;
|
|
|
+ int in_flight;
|
|
|
+};
|
|
|
+
|
|
|
struct option_port_private {
|
|
|
/* Input endpoints and buffer for this port */
|
|
|
struct urb *in_urbs[N_IN_URB];
|
|
@@ -651,6 +658,8 @@ struct option_port_private {
|
|
|
struct urb *out_urbs[N_OUT_URB];
|
|
|
u8 *out_buffer[N_OUT_URB];
|
|
|
unsigned long out_busy; /* Bit vector of URBs in use */
|
|
|
+ int opened;
|
|
|
+ struct usb_anchor delayed;
|
|
|
|
|
|
/* Settings for the port */
|
|
|
int rts_state; /* Handshaking pins (outputs) */
|
|
@@ -697,12 +706,17 @@ module_exit(option_exit);
|
|
|
static int option_probe(struct usb_serial *serial,
|
|
|
const struct usb_device_id *id)
|
|
|
{
|
|
|
+ struct option_intf_private *data;
|
|
|
/* 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)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL);
|
|
|
+ if (!data)
|
|
|
+ return -ENOMEM;
|
|
|
+ spin_lock_init(&data->susp_lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -759,12 +773,15 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
|
const unsigned char *buf, int count)
|
|
|
{
|
|
|
struct option_port_private *portdata;
|
|
|
+ struct option_intf_private *intfdata;
|
|
|
int i;
|
|
|
int left, todo;
|
|
|
struct urb *this_urb = NULL; /* spurious */
|
|
|
int err;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
portdata = usb_get_serial_port_data(port);
|
|
|
+ intfdata = port->serial->private;
|
|
|
|
|
|
dbg("%s: write (%d chars)", __func__, count);
|
|
|
|
|
@@ -786,17 +803,33 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
|
dbg("%s: endpoint %d buf %d", __func__,
|
|
|
usb_pipeendpoint(this_urb->pipe), i);
|
|
|
|
|
|
+ err = usb_autopm_get_interface_async(port->serial->interface);
|
|
|
+ if (err < 0)
|
|
|
+ break;
|
|
|
+
|
|
|
/* send the data */
|
|
|
memcpy(this_urb->transfer_buffer, buf, todo);
|
|
|
this_urb->transfer_buffer_length = todo;
|
|
|
|
|
|
- err = usb_submit_urb(this_urb, GFP_ATOMIC);
|
|
|
- if (err) {
|
|
|
- dbg("usb_submit_urb %p (write bulk) failed "
|
|
|
- "(%d)", this_urb, err);
|
|
|
- clear_bit(i, &portdata->out_busy);
|
|
|
- continue;
|
|
|
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
|
|
|
+ if (intfdata->suspended) {
|
|
|
+ usb_anchor_urb(this_urb, &portdata->delayed);
|
|
|
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
|
|
|
+ } else {
|
|
|
+ intfdata->in_flight++;
|
|
|
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
|
|
|
+ err = usb_submit_urb(this_urb, GFP_ATOMIC);
|
|
|
+ if (err) {
|
|
|
+ dbg("usb_submit_urb %p (write bulk) failed "
|
|
|
+ "(%d)", this_urb, err);
|
|
|
+ clear_bit(i, &portdata->out_busy);
|
|
|
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
|
|
|
+ intfdata->in_flight--;
|
|
|
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
portdata->tx_start_time[i] = jiffies;
|
|
|
buf += todo;
|
|
|
left -= todo;
|
|
@@ -840,7 +873,10 @@ static void option_indat_callback(struct urb *urb)
|
|
|
if (err)
|
|
|
printk(KERN_ERR "%s: resubmit read urb failed. "
|
|
|
"(%d)", __func__, err);
|
|
|
+ else
|
|
|
+ usb_mark_last_busy(port->serial->dev);
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
@@ -849,15 +885,21 @@ static void option_outdat_callback(struct urb *urb)
|
|
|
{
|
|
|
struct usb_serial_port *port;
|
|
|
struct option_port_private *portdata;
|
|
|
+ struct option_intf_private *intfdata;
|
|
|
int i;
|
|
|
|
|
|
dbg("%s", __func__);
|
|
|
|
|
|
port = urb->context;
|
|
|
+ intfdata = port->serial->private;
|
|
|
|
|
|
usb_serial_port_softint(port);
|
|
|
-
|
|
|
+ usb_autopm_put_interface_async(port->serial->interface);
|
|
|
portdata = usb_get_serial_port_data(port);
|
|
|
+ spin_lock(&intfdata->susp_lock);
|
|
|
+ intfdata->in_flight--;
|
|
|
+ spin_unlock(&intfdata->susp_lock);
|
|
|
+
|
|
|
for (i = 0; i < N_OUT_URB; ++i) {
|
|
|
if (portdata->out_urbs[i] == urb) {
|
|
|
smp_mb__before_clear_bit();
|
|
@@ -967,10 +1009,13 @@ static int option_chars_in_buffer(struct tty_struct *tty)
|
|
|
static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|
|
{
|
|
|
struct option_port_private *portdata;
|
|
|
+ struct option_intf_private *intfdata;
|
|
|
+ struct usb_serial *serial = port->serial;
|
|
|
int i, err;
|
|
|
struct urb *urb;
|
|
|
|
|
|
portdata = usb_get_serial_port_data(port);
|
|
|
+ intfdata = serial->private;
|
|
|
|
|
|
dbg("%s", __func__);
|
|
|
|
|
@@ -989,6 +1034,12 @@ static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|
|
|
|
|
option_send_setup(port);
|
|
|
|
|
|
+ serial->interface->needs_remote_wakeup = 1;
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ portdata->opened = 1;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
+ usb_autopm_put_interface(serial->interface);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1013,16 +1064,23 @@ static void option_close(struct usb_serial_port *port)
|
|
|
int i;
|
|
|
struct usb_serial *serial = port->serial;
|
|
|
struct option_port_private *portdata;
|
|
|
+ struct option_intf_private *intfdata = port->serial->private;
|
|
|
|
|
|
dbg("%s", __func__);
|
|
|
portdata = usb_get_serial_port_data(port);
|
|
|
|
|
|
if (serial->dev) {
|
|
|
/* Stop reading/writing urbs */
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ portdata->opened = 0;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
+
|
|
|
for (i = 0; i < N_IN_URB; i++)
|
|
|
usb_kill_urb(portdata->in_urbs[i]);
|
|
|
for (i = 0; i < N_OUT_URB; i++)
|
|
|
usb_kill_urb(portdata->out_urbs[i]);
|
|
|
+ usb_autopm_get_interface(serial->interface);
|
|
|
+ serial->interface->needs_remote_wakeup = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1127,6 +1185,7 @@ static int option_startup(struct usb_serial *serial)
|
|
|
__func__, i);
|
|
|
return 1;
|
|
|
}
|
|
|
+ init_usb_anchor(&portdata->delayed);
|
|
|
|
|
|
for (j = 0; j < N_IN_URB; j++) {
|
|
|
buffer = (u8 *)__get_free_page(GFP_KERNEL);
|
|
@@ -1229,18 +1288,52 @@ static void option_release(struct usb_serial *serial)
|
|
|
#ifdef CONFIG_PM
|
|
|
static int option_suspend(struct usb_serial *serial, pm_message_t message)
|
|
|
{
|
|
|
+ struct option_intf_private *intfdata = serial->private;
|
|
|
+ int b;
|
|
|
+
|
|
|
dbg("%s entered", __func__);
|
|
|
+
|
|
|
+ if (serial->dev->auto_pm) {
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ b = intfdata->in_flight;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
+
|
|
|
+ if (b)
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ intfdata->suspended = 1;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
stop_read_write_urbs(serial);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void play_delayed(struct usb_serial_port *port)
|
|
|
+{
|
|
|
+ struct option_intf_private *data;
|
|
|
+ struct option_port_private *portdata;
|
|
|
+ struct urb *urb;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ portdata = usb_get_serial_port_data(port);
|
|
|
+ data = port->serial->private;
|
|
|
+ while ((urb = usb_get_from_anchor(&portdata->delayed))) {
|
|
|
+ err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
+ if (!err)
|
|
|
+ data->in_flight++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int option_resume(struct usb_serial *serial)
|
|
|
{
|
|
|
- int err, i, j;
|
|
|
+ int i, j;
|
|
|
struct usb_serial_port *port;
|
|
|
- struct urb *urb;
|
|
|
+ struct option_intf_private *intfdata = serial->private;
|
|
|
struct option_port_private *portdata;
|
|
|
+ struct urb *urb;
|
|
|
+ int err = 0;
|
|
|
|
|
|
dbg("%s entered", __func__);
|
|
|
/* get the interrupt URBs resubmitted unconditionally */
|
|
@@ -1255,7 +1348,7 @@ static int option_resume(struct usb_serial *serial)
|
|
|
if (err < 0) {
|
|
|
err("%s: Error %d for interrupt URB of port%d",
|
|
|
__func__, err, i);
|
|
|
- return err;
|
|
|
+ goto err_out;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1263,27 +1356,32 @@ static int option_resume(struct usb_serial *serial)
|
|
|
/* walk all ports */
|
|
|
port = serial->port[i];
|
|
|
portdata = usb_get_serial_port_data(port);
|
|
|
- mutex_lock(&port->mutex);
|
|
|
|
|
|
/* skip closed ports */
|
|
|
- if (!port->port.count) {
|
|
|
- mutex_unlock(&port->mutex);
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ if (!portdata->opened) {
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
for (j = 0; j < N_IN_URB; j++) {
|
|
|
urb = portdata->in_urbs[j];
|
|
|
- err = usb_submit_urb(urb, GFP_NOIO);
|
|
|
+ err = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
if (err < 0) {
|
|
|
- mutex_unlock(&port->mutex);
|
|
|
err("%s: Error %d for bulk URB %d",
|
|
|
__func__, err, i);
|
|
|
- return err;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
+ goto err_out;
|
|
|
}
|
|
|
}
|
|
|
- mutex_unlock(&port->mutex);
|
|
|
+ play_delayed(port);
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
}
|
|
|
- return 0;
|
|
|
+ spin_lock_irq(&intfdata->susp_lock);
|
|
|
+ intfdata->suspended = 0;
|
|
|
+ spin_unlock_irq(&intfdata->susp_lock);
|
|
|
+err_out:
|
|
|
+ return err;
|
|
|
}
|
|
|
#endif
|
|
|
|