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/
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to