USB: suspend/resume support for option driver

This patch implements suspend and resume methods for the
option driver. With my hardware I can even suspend the system
and keep up a connection for a short time.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-Off-By: Matthias Urlichs <smurf@smurf.noris.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Oliver Neukum 2009-01-27 17:21:40 +01:00 committed by Greg Kroah-Hartman
parent b633d28e2c
commit 4901b2c34e
1 changed files with 80 additions and 6 deletions

View File

@ -62,6 +62,8 @@ static int option_tiocmget(struct tty_struct *tty, struct file *file);
static int option_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port);
static int option_suspend(struct usb_serial *serial, pm_message_t message);
static int option_resume(struct usb_serial *serial);
/* Vendor and product IDs */
#define OPTION_VENDOR_ID 0x0AF0
@ -523,6 +525,8 @@ static struct usb_driver option_driver = {
.name = "option",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.suspend = usb_serial_suspend,
.resume = usb_serial_resume,
.id_table = option_ids,
.no_dynamic_id = 1,
};
@ -551,6 +555,8 @@ static struct usb_serial_driver option_1port_device = {
.attach = option_startup,
.shutdown = option_shutdown,
.read_int_callback = option_instat_callback,
.suspend = option_suspend,
.resume = option_resume,
};
static int debug;
@ -821,10 +827,10 @@ static void option_instat_callback(struct urb *urb)
req_pkt->bRequestType, req_pkt->bRequest);
}
} else
dbg("%s: error %d", __func__, status);
err("%s: error %d", __func__, status);
/* Resubmit urb so we continue receiving IRQ data */
if (status != -ESHUTDOWN) {
if (status != -ESHUTDOWN && status != -ENOENT) {
urb->dev = serial->dev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
@ -843,7 +849,6 @@ static int option_write_room(struct tty_struct *tty)
portdata = usb_get_serial_port_data(port);
for (i = 0; i < N_OUT_URB; i++) {
this_urb = portdata->out_urbs[i];
if (this_urb && !test_bit(i, &portdata->out_busy))
@ -1105,14 +1110,12 @@ bail_out_error:
return 1;
}
static void option_shutdown(struct usb_serial *serial)
static void stop_read_write_urbs(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct option_port_private *portdata;
dbg("%s", __func__);
/* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
@ -1122,6 +1125,17 @@ static void option_shutdown(struct usb_serial *serial)
for (j = 0; j < N_OUT_URB; j++)
usb_kill_urb(portdata->out_urbs[j]);
}
}
static void option_shutdown(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct option_port_private *portdata;
dbg("%s", __func__);
stop_read_write_urbs(serial);
/* Now free them */
for (i = 0; i < serial->num_ports; ++i) {
@ -1152,6 +1166,66 @@ static void option_shutdown(struct usb_serial *serial)
}
}
static int option_suspend(struct usb_serial *serial, pm_message_t message)
{
dbg("%s entered", __func__);
stop_read_write_urbs(serial);
return 0;
}
static int option_resume(struct usb_serial *serial)
{
int err, i, j;
struct usb_serial_port *port;
struct urb *urb;
struct option_port_private *portdata;
dbg("%s entered", __func__);
/* get the interrupt URBs resubmitted unconditionally */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
if (!port->interrupt_in_urb) {
dbg("%s: No interrupt URB for port %d\n", __func__, i);
continue;
}
port->interrupt_in_urb->dev = serial->dev;
err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
dbg("Submitted interrupt URB for port %d (result %d)", i, err);
if (err < 0) {
err("%s: Error %d for interrupt URB of port%d",
__func__, err, i);
return err;
}
}
for (i = 0; i < serial->num_ports; i++) {
/* 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);
continue;
}
for (j = 0; j < N_IN_URB; j++) {
urb = portdata->in_urbs[j];
err = usb_submit_urb(urb, GFP_NOIO);
if (err < 0) {
mutex_unlock(&port->mutex);
err("%s: Error %d for bulk URB %d",
__func__, err, i);
return err;
}
}
mutex_unlock(&port->mutex);
}
return 0;
}
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);