Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4901b2c3 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

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: default avatarOliver Neukum <oneukum@suse.de>
Signed-Off-By: default avatarMatthias Urlichs <smurf@smurf.noris.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b633d28e
Loading
Loading
Loading
Loading
+80 −6
Original line number Diff line number Diff line
@@ -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 @@ static int option_startup(struct usb_serial *serial)
	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);