[PATCH 002/002] USB: serial: sierra driver autosuspend support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Subject: [PATCH 002/002] USB: serial: sierra driver autosuspend support
From: Elina Pasheva <epasheva@xxxxxxxxxxxxxxxxxx>

This patch [PATCH 002/002] addresses Oliver Neukum's comments for the similar
patch we submitted in the spring:
[PATCH 002/002] dealing with autosuspend support:
- Renamed module parameter "suspend_support" to "support_autopm" for clarity
- Implemented anchor interface on the transmit path so that the Tx urbs are
kept track of and are released during suspend
- Added sierra_suspend(), sierra_resume() functions and support.
- Moved sierra_device structure at the bottom
- Added entries for suspend and resume interfaces to sierra_device structure.
- Added new device attribute "suspend_status" which reflects the power state
for each device served by the sierra driver.
- Added provisions to enable debugging by using DEBUG definition
- Version number set to 1.7.2 to match our repository.
Signed-off-by: Elina Pasheva <epasheva@xxxxxxxxxxxxxxxxxx>
---

 drivers/usb/serial/sierra.c |  150 ++++++++++++++++++++++++++++++----
 1 file changed, 135 insertions(+), 15 deletions(-)

--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -16,8 +16,9 @@
   Portions based on the option driver by Matthias Urlichs <smurf@xxxxxxxxxxxxxx>
   Whom based his on the Keyspan driver by Hugh Blemings <hugh@xxxxxxxxxxxx>
 */
-
-#define DRIVER_VERSION "v.1.3.7"
+/* Uncomment to log function calls */
+/* #define DEBUG */
+#define DRIVER_VERSION "v.1.7.2"
 #define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
 #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
 
@@ -44,6 +45,11 @@
 
 static int debug;
 static int nmea;
+static int support_autopm;
+
+/* sysfs attributes */
+static int sierra_create_sysfs_attrs(struct usb_serial_port *port);
+/* Note: attribute created for each port; removed on disconnect */
 
 /* Used in interface blacklisting */
 struct sierra_iface_info {
@@ -257,18 +263,13 @@ static struct usb_device_id id_table [] 
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
-static struct usb_driver sierra_driver = {
-	.name       = "sierra",
-	.probe      = usb_serial_probe,
-	.disconnect = usb_serial_disconnect,
-	.id_table   = id_table,
-	.no_dynamic_id = 	1,
-};
-
 struct sierra_port_private {
 	spinlock_t lock;	/* lock the structure */
 	int outstanding_urbs;	/* number of out urbs in flight */
-
+	struct usb_anchor submitted;	/* in case we need to retract our
+					 * submissions */
+	int suspend_status;	/* indicates whether device power has been
+				 * suspended */
 	/* Input endpoints and buffers for this port */
 	struct urb *in_urbs[N_IN_URB];
 
@@ -386,6 +387,28 @@ static void sierra_release_urb(struct ur
 	}
 }
 
+/* Sysfs Attributes */
+
+static ssize_t show_suspend_status(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port;
+	struct sierra_port_private *portdata;
+
+	port = to_usb_serial_port(dev);
+	portdata = usb_get_serial_port_data(port);
+
+	return snprintf(buf, 4096, "%i\n", portdata->suspend_status);
+}
+
+static DEVICE_ATTR(suspend_status, S_IWUSR | S_IRUGO, show_suspend_status,
+							NULL);
+
+static int sierra_create_sysfs_attrs(struct usb_serial_port *port)
+{
+	return device_create_file(&port->dev, &dev_attr_suspend_status);
+}
+
 static void sierra_outdat_callback(struct urb *urb)
 {
 	struct usb_serial_port *port = urb->context;
@@ -467,21 +490,24 @@ static int sierra_write(struct tty_struc
 
 	/* Handle the need to send a zero length packet */
 	urb->transfer_flags |= URB_ZERO_PACKET;
+	usb_anchor_urb(urb, &portdata->submitted);
 
 	/* send it down the pipe */
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
 	if (retval) {
 		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
 			"with status = %d\n", __func__, retval);
-		goto error;
+		goto error_anchor;
 	}
 
-	/* we are done with this urb, so let the host driver
-	 * really free it when it is finished with it */
+	/* release our reference to this urb, the USB core will eventually
+	 * free it entirely */
 	usb_free_urb(urb);
 
 	return writesize;
-error:
+
+error_anchor:
+	usb_unanchor_urb(urb);
 	usb_free_urb(urb);
 error_no_urb:
 	kfree(buffer);
@@ -711,6 +737,7 @@ static void sierra_close(struct usb_seri
 	int i;
 	struct usb_serial *serial = port->serial;
 	struct sierra_port_private *portdata;
+	int time;
 
 	dev_dbg(&port->dev, "%s\n", __func__);
 	portdata = usb_get_serial_port_data(port);
@@ -731,6 +758,11 @@ static void sierra_close(struct usb_seri
 			sierra_release_urb(portdata->in_urbs[i]);
 			portdata->in_urbs[i] = NULL;
 		}
+
+		time = usb_wait_anchor_empty_timeout(&portdata->submitted,
+							1000);
+		if (!time)
+			usb_kill_anchored_urbs(&portdata->submitted);
 	}
 }
 
@@ -819,8 +851,19 @@ static int sierra_startup(struct usb_ser
 			return -ENOMEM;
 		}
 		spin_lock_init(&portdata->lock);
+		init_usb_anchor(&portdata->submitted);
+		portdata->suspend_status = 0;
 		/* Set the port private data pointer */
 		usb_set_serial_port_data(port, portdata);
+		if (support_autopm == 0) {
+			/* When the device does not support suspend condition
+			 * indicate the device is busy */
+			usb_autopm_disable(serial->interface);
+		} else {
+			/* When the device does support autosuspend
+			 * indicate the device is not busy */
+			usb_autopm_enable(serial->interface);
+		}
 	}
 
 	return 0;
@@ -841,11 +884,82 @@ static void sierra_disconnect(struct usb
 		portdata = usb_get_serial_port_data(port);
 		if (!portdata)
 			continue;
+		portdata->suspend_status = 0;
+		device_remove_file(&port->dev, &dev_attr_suspend_status);
+
 		kfree(portdata);
 		usb_set_serial_port_data(port, NULL);
 	}
 }
 
+int sierra_suspend(struct usb_serial *serial, pm_message_t message)
+{
+	int i;
+	struct usb_serial_port *port;
+	struct sierra_port_private *portdata;
+	unsigned long flags;
+	int time;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		if (!port)
+			continue;
+		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+		/* indicate suspended power mode */
+		portdata->suspend_status = 1;
+		sierra_stop_rx_urbs(port);
+		/* release tx urbs */
+		time = usb_wait_anchor_empty_timeout(&portdata->submitted,
+							1000);
+		if (!time) {
+			usb_kill_anchored_urbs(&portdata->submitted);
+			spin_lock_irqsave(&portdata->lock, flags);
+			/* set outstanding tx urbs counter to 0 */
+			portdata->outstanding_urbs = 0;
+			spin_unlock_irqrestore(&portdata->lock, flags);
+		}
+	} /* end for loop */
+	return 0;
+}
+
+int sierra_resume(struct usb_serial *serial)
+{
+	int i;
+	struct usb_serial_port *port;
+	struct sierra_port_private *portdata;
+
+	dev_dbg(&serial->dev->dev, "%s\n", __func__);
+
+	for (i = 0; i < serial->num_ports ; i++) {
+		port = serial->port[i];
+		if (!port)
+			continue;
+		portdata = usb_get_serial_port_data(port);
+		if (!portdata)
+			continue;
+		sierra_submit_rx_urbs(port, GFP_NOIO);
+		/* indicate non-suspended power mode */
+		portdata->suspend_status = 0;
+	}
+	return 0;
+}
+
+static struct usb_driver sierra_driver = {
+	.name       = "sierra",
+	.probe      = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+	.suspend    = usb_serial_suspend,
+	.resume     = usb_serial_resume,
+	.id_table   = id_table,
+
+	.no_dynamic_id        =	1,
+	.supports_autosuspend = 1,
+};
+
 static struct usb_serial_driver sierra_device = {
 	.driver = {
 		.owner =	THIS_MODULE,
@@ -866,7 +980,10 @@ static struct usb_serial_driver sierra_d
 	.tiocmset          = sierra_tiocmset,
 	.attach            = sierra_startup,
 	.disconnect        = sierra_disconnect,
+	.port_probe	   = sierra_create_sysfs_attrs,
 	.read_int_callback = sierra_instat_callback,
+	.suspend           = sierra_suspend,
+	.resume            = sierra_resume,
 };
 
 /* Functions used by new usb-serial code. */
@@ -912,3 +1029,6 @@ MODULE_PARM_DESC(nmea, "NMEA streaming")
 
 module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug messages");
+
+module_param(support_autopm, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(support_autopm, "Device auto suspend support");



--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux