diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-01-27 17:21:40 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-08 11:17:28 -0800 |
commit | e1897f9fc227055733cea2b7449af38276b2e6a5 (patch) | |
tree | b04f32a464c2ef7237345a46eb6c9f8826a66fa9 | |
parent | e72432d716f8555c27fc95b39969ba0a1d0c988b (diff) |
USB: suspend/resume support for option driver
commit 4901b2c34ecb6fc45909228ad269c8126efe4401 upstream.
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>
-rw-r--r-- | drivers/usb/serial/option.c | 86 |
1 files changed, 80 insertions, 6 deletions
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index faa30ad8ae4..58f44bc7a62 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -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 @@ -511,6 +513,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, }; @@ -539,6 +543,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; @@ -809,10 +815,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) @@ -831,7 +837,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)) @@ -1090,14 +1095,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]; @@ -1107,6 +1110,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) { @@ -1137,6 +1151,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); |