[patch]autosuspend for cdc-acm

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Novell
Message-ID <[email protected]>
Hi,

this implements autosuspend for cdc-acm devices.

	Regards
		Oliver
Signed-off-by: Oliver Neukum <[email protected]>
-- 

--- a/drivers/usb/class/cdc-acm.c	2007-06-11 13:27:17.000000000 +0200
+++ b/drivers/usb/class/cdc-acm.c	2007-05-30 20:56:00.000000000 +0200
@@ -330,26 +330,26 @@ static void acm_read_bulk(struct urb *ur
 		return;
 
 	if (status)
-		dev_dbg(&acm->data->dev, "bulk rx status %d", status);
+		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
 
 	buf = rcv->buffer;
 	buf->size = urb->actual_length;
 
+	spin_lock(&acm->read_lock);
 	if (likely(status == 0)) {
-		spin_lock(&acm->read_lock);
 		list_add_tail(&rcv->list, &acm->spare_read_urbs);
 		list_add_tail(&buf->list, &acm->filled_read_bufs);
-		spin_unlock(&acm->read_lock);
 	} else {
 		/* we drop the buffer due to an error */
-		spin_lock(&acm->read_lock);
 		list_add_tail(&rcv->list, &acm->spare_read_urbs);
 		list_add(&buf->list, &acm->spare_read_bufs);
-		spin_unlock(&acm->read_lock);
 		/* nevertheless the tasklet must be kicked unconditionally
 		so the queue cannot dry up */
 	}
-	tasklet_schedule(&acm->urb_task);
+
+	if (!acm->suspending)
+		tasklet_schedule(&acm->urb_task);
+	spin_unlock(&acm->read_lock);
 }
 
 static void acm_rx_tasklet(unsigned long _acm)
@@ -835,7 +835,7 @@ static int acm_probe (struct usb_interfa
 
 	if (!buflen) {
 		if (intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) {
-			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint");
+			dev_dbg(&intf->dev,"Seeking extra descriptors on endpoint\n");
 			buflen = intf->cur_altsetting->endpoint->extralen;
 			buffer = intf->cur_altsetting->endpoint->extra;
 		} else {
@@ -885,24 +885,24 @@ next_desc:
 
 	if (!union_header) {
 		if (call_interface_num > 0) {
-			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor");
+			dev_dbg(&intf->dev,"No union descriptor, using call management descriptor\n");
 			data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
 			control_interface = intf;
 		} else {
-			dev_dbg(&intf->dev,"No union descriptor, giving up");
+			dev_dbg(&intf->dev,"No union descriptor, giving up\n");
 			return -ENODEV;
 		}
 	} else {
 		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
 		data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
 		if (!control_interface || !data_interface) {
-			dev_dbg(&intf->dev,"no interfaces");
+			dev_dbg(&intf->dev,"no interfaces\n");
 			return -ENODEV;
 		}
 	}
 	
 	if (data_interface_num != call_interface_num)
-		dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.");
+		dev_dbg(&intf->dev,"Seperate call control interface. That is not fully supported.\n");
 
 skip_normal_probe:
 
@@ -910,7 +910,7 @@ skip_normal_probe:
 	if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) {
 		if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) {
 			struct usb_interface *t;
-			dev_dbg(&intf->dev,"Your device has switched interfaces.");
+			dev_dbg(&intf->dev,"Your device has switched interfaces.\n");
 
 			t = control_interface;
 			control_interface = data_interface;
@@ -921,7 +921,7 @@ skip_normal_probe:
 	}
 	
 	if (usb_interface_claimed(data_interface)) { /* valid in this context */
-		dev_dbg(&intf->dev,"The data interface isn't available");
+		dev_dbg(&intf->dev,"The data interface isn't available\n");
 		return -EBUSY;
 	}
 
@@ -938,7 +938,7 @@ skip_normal_probe:
 	if (!usb_endpoint_dir_in(epread)) {
 		/* descriptors are swapped */
 		struct usb_endpoint_descriptor *t;
-		dev_dbg(&intf->dev,"The data interface has switched endpoints");
+		dev_dbg(&intf->dev,"The data interface has switched endpoints\n");
 		
 		t = epread;
 		epread = epwrite;
@@ -1143,11 +1143,60 @@ static void acm_disconnect(struct usb_in
 	}
 
 	mutex_unlock(&open_mutex);
-
 	if (acm->tty)
 		tty_hangup(acm->tty);
 }
 
+static int acm_suspend (struct usb_interface *intf, pm_message_t message)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+	int i;
+
+	if (acm->suspend_counter++) {
+		dev_dbg(&intf->dev, "Suspend on additional interface\n");
+		return 0;
+	}
+
+	spin_lock_irq(&acm->read_lock);
+	acm->suspending = 1;
+	spin_unlock_irq(&acm->read_lock);
+
+	tasklet_disable(&acm->urb_task);
+
+	usb_kill_urb(acm->ctrlurb);
+	usb_kill_urb(acm->writeurb);
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_kill_urb(acm->ru[i].urb);
+
+	dev_dbg(&intf->dev, "suspension of ACM device.\n");
+	return 0;
+}
+
+static int acm_resume (struct usb_interface *intf)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+
+	if (--acm->suspend_counter)
+		return 0;
+
+	spin_lock_irq(&acm->read_lock);
+	acm->suspending = 0;
+	spin_unlock_irq(&acm->read_lock);
+
+	if (!acm->used)
+		return 0;
+
+	if (usb_submit_urb(acm->ctrlurb, GFP_NOIO)) {
+		dev_dbg(&intf->dev, "cannot resume control urb");
+		return -EIO;
+	}
+
+	/* This restarts the read queue */
+	tasklet_schedule(&acm->urb_task);
+
+	return 0;
+}
+
 /*
  * USB driver structure.
  */
@@ -1197,6 +1246,8 @@ static struct usb_driver acm_driver = {
 	.name =		"cdc_acm",
 	.probe =	acm_probe,
 	.disconnect =	acm_disconnect,
+	.suspend =	acm_suspend,
+	.resume =	acm_resume,
 	.id_table =	acm_ids,
 };
 
--- a/drivers/usb/class/cdc-acm.h	2007-06-11 13:27:17.000000000 +0200
+++ b/drivers/usb/class/cdc-acm.h	2007-05-30 11:57:43.000000000 +0200
@@ -119,6 +119,8 @@ struct acm {
 	unsigned int minor;				/* acm minor number */
 	unsigned char throttle;				/* throttled by tty layer */
 	unsigned char clocal;				/* termios CLOCAL */
+	unsigned char suspend_counter;			/* count of suspended interfaces */
+	unsigned char suspending;			/* suspension is in progress */
 	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 };
 

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.