This is a note to let you know that I've just added the patch titled Subject: USB: serial: full autosuspend support for the option driver to my gregkh-2.6 tree. Its filename is usb-serial-full-autosuspend-support-for-the-option-driver.patch This tree can be found at http://www.kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/patches/ >From oliver@xxxxxxxxxx Fri Jul 10 15:11:40 2009 From: Oliver Neukum <oliver@xxxxxxxxxx> Date: Wed, 1 Jul 2009 16:00:32 +0200 Subject: USB: serial: full autosuspend support for the option driver To: zhao.ming9@xxxxxxxxxx, greg@xxxxxxxxx, USB list <linux-usb@xxxxxxxxxxxxxxx> Message-ID: <200907011600.33423.oliver@xxxxxxxxxx> this adds autosupport usable even in an always online mode. - enables remote wakeup on open - autoresume for sending - timeout based autosuspend if nothing is sent or recieved - autosuspend without remote wakeup support on open/close Signed-off-by: Oliver Neukum <oliver@xxxxxxxxxx> Tested-off-by: Zhao Ming <zhao.ming9@xxxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx> --- drivers/usb/serial/option.c | 134 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 19 deletions(-) --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -559,6 +559,7 @@ static struct usb_driver option_driver = .resume = usb_serial_resume, .id_table = option_ids, .no_dynamic_id = 1, + .supports_autosuspend = 1, }; /* The card has three separate interfaces, which the serial driver @@ -601,6 +602,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]; @@ -609,6 +616,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) */ @@ -655,12 +664,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; } @@ -717,12 +731,15 @@ static int option_write(struct tty_struc 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); @@ -744,17 +761,33 @@ static int option_write(struct tty_struc 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; @@ -798,7 +831,10 @@ static void option_indat_callback(struct if (err) printk(KERN_ERR "%s: resubmit read urb failed. " "(%d)", __func__, err); + else + usb_mark_last_busy(port->serial->dev); } + } return; } @@ -807,15 +843,21 @@ static void option_outdat_callback(struc { 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(); @@ -831,7 +873,6 @@ static void option_instat_callback(struc int status = urb->status; struct usb_serial_port *port = urb->context; struct option_port_private *portdata = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; dbg("%s", __func__); dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata); @@ -927,11 +968,13 @@ static int option_open(struct tty_struct struct usb_serial_port *port, struct file *filp) { 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 = port->serial->private; dbg("%s", __func__); @@ -950,6 +993,12 @@ static int option_open(struct tty_struct 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; } @@ -974,16 +1023,23 @@ static void option_close(struct usb_seri 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; } } @@ -1088,6 +1144,7 @@ static int option_startup(struct usb_ser __func__, i); return 1; } + init_usb_anchor(&portdata->delayed); for (j = 0; j < N_IN_URB; j++) { buffer = (u8 *)__get_free_page(GFP_KERNEL); @@ -1189,18 +1246,52 @@ static void option_release(struct usb_se 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 */ @@ -1215,7 +1306,7 @@ static int option_resume(struct usb_seri if (err < 0) { err("%s: Error %d for interrupt URB of port%d", __func__, err, i); - return err; + goto err_out; } } @@ -1223,27 +1314,32 @@ static int option_resume(struct usb_seri /* 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; } MODULE_AUTHOR(DRIVER_AUTHOR); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html