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:
committed by
Greg Kroah-Hartman
parent
b633d28e2c
commit
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,
|
static int option_tiocmset(struct tty_struct *tty, struct file *file,
|
||||||
unsigned int set, unsigned int clear);
|
unsigned int set, unsigned int clear);
|
||||||
static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *port);
|
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 */
|
/* Vendor and product IDs */
|
||||||
#define OPTION_VENDOR_ID 0x0AF0
|
#define OPTION_VENDOR_ID 0x0AF0
|
||||||
@@ -523,6 +525,8 @@ static struct usb_driver option_driver = {
|
|||||||
.name = "option",
|
.name = "option",
|
||||||
.probe = usb_serial_probe,
|
.probe = usb_serial_probe,
|
||||||
.disconnect = usb_serial_disconnect,
|
.disconnect = usb_serial_disconnect,
|
||||||
|
.suspend = usb_serial_suspend,
|
||||||
|
.resume = usb_serial_resume,
|
||||||
.id_table = option_ids,
|
.id_table = option_ids,
|
||||||
.no_dynamic_id = 1,
|
.no_dynamic_id = 1,
|
||||||
};
|
};
|
||||||
@@ -551,6 +555,8 @@ static struct usb_serial_driver option_1port_device = {
|
|||||||
.attach = option_startup,
|
.attach = option_startup,
|
||||||
.shutdown = option_shutdown,
|
.shutdown = option_shutdown,
|
||||||
.read_int_callback = option_instat_callback,
|
.read_int_callback = option_instat_callback,
|
||||||
|
.suspend = option_suspend,
|
||||||
|
.resume = option_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int debug;
|
static int debug;
|
||||||
@@ -821,10 +827,10 @@ static void option_instat_callback(struct urb *urb)
|
|||||||
req_pkt->bRequestType, req_pkt->bRequest);
|
req_pkt->bRequestType, req_pkt->bRequest);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
dbg("%s: error %d", __func__, status);
|
err("%s: error %d", __func__, status);
|
||||||
|
|
||||||
/* Resubmit urb so we continue receiving IRQ data */
|
/* Resubmit urb so we continue receiving IRQ data */
|
||||||
if (status != -ESHUTDOWN) {
|
if (status != -ESHUTDOWN && status != -ENOENT) {
|
||||||
urb->dev = serial->dev;
|
urb->dev = serial->dev;
|
||||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -843,7 +849,6 @@ static int option_write_room(struct tty_struct *tty)
|
|||||||
|
|
||||||
portdata = usb_get_serial_port_data(port);
|
portdata = usb_get_serial_port_data(port);
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < N_OUT_URB; i++) {
|
for (i = 0; i < N_OUT_URB; i++) {
|
||||||
this_urb = portdata->out_urbs[i];
|
this_urb = portdata->out_urbs[i];
|
||||||
if (this_urb && !test_bit(i, &portdata->out_busy))
|
if (this_urb && !test_bit(i, &portdata->out_busy))
|
||||||
@@ -1105,14 +1110,12 @@ bail_out_error:
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void option_shutdown(struct usb_serial *serial)
|
static void stop_read_write_urbs(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
struct usb_serial_port *port;
|
struct usb_serial_port *port;
|
||||||
struct option_port_private *portdata;
|
struct option_port_private *portdata;
|
||||||
|
|
||||||
dbg("%s", __func__);
|
|
||||||
|
|
||||||
/* Stop reading/writing urbs */
|
/* Stop reading/writing urbs */
|
||||||
for (i = 0; i < serial->num_ports; ++i) {
|
for (i = 0; i < serial->num_ports; ++i) {
|
||||||
port = serial->port[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++)
|
for (j = 0; j < N_OUT_URB; j++)
|
||||||
usb_kill_urb(portdata->out_urbs[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 */
|
/* Now free them */
|
||||||
for (i = 0; i < serial->num_ports; ++i) {
|
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_AUTHOR(DRIVER_AUTHOR);
|
||||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||||
MODULE_VERSION(DRIVER_VERSION);
|
MODULE_VERSION(DRIVER_VERSION);
|
||||||
|
Reference in New Issue
Block a user