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:
Родитель
b633d28e2c
Коммит
4901b2c34e
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче