Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=1365baf7249bb2d05e774e7681237b8e86f5007a
Commit:     1365baf7249bb2d05e774e7681237b8e86f5007a
Parent:     eedffd12e07d8fd150618d603d010b491dc90354
Author:     Oliver Neukum <[EMAIL PROTECTED]>
AuthorDate: Fri Oct 12 17:24:28 2007 +0200
Committer:  Greg Kroah-Hartman <[EMAIL PROTECTED]>
CommitDate: Fri Feb 1 14:34:47 2008 -0800

    USB: autosuspend for cdc-acm
    
    Here we go. This patch implements suspend/resume and autosuspend
    for the CDC ACM driver.
    
    Signed-off-by: Oliver Neukum <[EMAIL PROTECTED]>
    Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 drivers/usb/class/cdc-acm.c |   92 ++++++++++++++++++++++++++++++++++++-------
 drivers/usb/class/cdc-acm.h |    2 +
 2 files changed, 79 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 912d97a..b0f9873 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -496,10 +496,19 @@ static int acm_tty_open(struct tty_struct *tty, struct 
file *filp)
           otherwise it is scheduled, and with high data rates data can get 
lost. */
        tty->low_latency = 1;
 
+       if (usb_autopm_get_interface(acm->control)) {
+               mutex_unlock(&open_mutex);
+               return -EIO;
+       }
+
+       mutex_lock(&acm->mutex);
+       mutex_unlock(&open_mutex);
        if (acm->used++) {
+               usb_autopm_put_interface(acm->control);
                goto done;
         }
 
+
        acm->ctrlurb->dev = acm->dev;
        if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
                dbg("usb_submit_urb(ctrl irq) failed");
@@ -526,14 +535,15 @@ static int acm_tty_open(struct tty_struct *tty, struct 
file *filp)
 
 done:
 err_out:
-       mutex_unlock(&open_mutex);
+       mutex_unlock(&acm->mutex);
        return rv;
 
 full_bailout:
        usb_kill_urb(acm->ctrlurb);
 bail_out:
+       usb_autopm_put_interface(acm->control);
        acm->used--;
-       mutex_unlock(&open_mutex);
+       mutex_unlock(&acm->mutex);
        return -EIO;
 }
 
@@ -570,6 +580,7 @@ static void acm_tty_close(struct tty_struct *tty, struct 
file *filp)
                        usb_kill_urb(acm->writeurb);
                        for (i = 0; i < nr; i++)
                                usb_kill_urb(acm->ru[i].urb);
+                       usb_autopm_put_interface(acm->control);
                } else
                        acm_tty_unregister(acm);
        }
@@ -980,6 +991,7 @@ skip_normal_probe:
        spin_lock_init(&acm->throttle_lock);
        spin_lock_init(&acm->write_lock);
        spin_lock_init(&acm->read_lock);
+       mutex_init(&acm->mutex);
        acm->write_ready = 1;
        acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
 
@@ -1096,6 +1108,25 @@ alloc_fail:
        return -ENOMEM;
 }
 
+static void stop_data_traffic(struct acm *acm)
+{
+       int i;
+
+       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);
+
+       INIT_LIST_HEAD(&acm->filled_read_bufs);
+       INIT_LIST_HEAD(&acm->spare_read_bufs);
+
+       tasklet_enable(&acm->urb_task);
+
+       cancel_work_sync(&acm->work);
+}
+
 static void acm_disconnect(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
@@ -1123,19 +1154,7 @@ static void acm_disconnect(struct usb_interface *intf)
        usb_set_intfdata(acm->control, NULL);
        usb_set_intfdata(acm->data, NULL);
 
-       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);
-
-       INIT_LIST_HEAD(&acm->filled_read_bufs);
-       INIT_LIST_HEAD(&acm->spare_read_bufs);
-
-       tasklet_enable(&acm->urb_task);
-
-       flush_scheduled_work(); /* wait for acm_softint */
+       stop_data_traffic(acm);
 
        acm_write_buffers_free(acm);
        usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, 
acm->ctrl_dma);
@@ -1156,6 +1175,46 @@ static void acm_disconnect(struct usb_interface *intf)
                tty_hangup(acm->tty);
 }
 
+static int acm_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct acm *acm = usb_get_intfdata(intf);
+
+       if (acm->susp_count++)
+               return 0;
+       /*
+       we treat opened interfaces differently,
+       we must guard against open
+       */
+       mutex_lock(&acm->mutex);
+
+       if (acm->used)
+               stop_data_traffic(acm);
+
+       mutex_unlock(&acm->mutex);
+       return 0;
+}
+
+static int acm_resume(struct usb_interface *intf)
+{
+       struct acm *acm = usb_get_intfdata(intf);
+       int rv = 0;
+
+       if (--acm->susp_count)
+               return 0;
+
+       mutex_lock(&acm->mutex);
+       if (acm->used) {
+               rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
+               if (rv < 0)
+               goto err_out;
+
+               tasklet_schedule(&acm->urb_task);
+       }
+
+err_out:
+       mutex_unlock(&acm->mutex);
+       return rv;
+}
 /*
  * USB driver structure.
  */
@@ -1208,7 +1267,10 @@ 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,
+       .supports_autosuspend = 1,
 };
 
 /*
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 09f7765..8df6a57 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -107,6 +107,7 @@ struct acm {
        int write_used;                                 /* number of non-empty 
write buffers */
        int write_ready;                                /* write urb is not 
running */
        spinlock_t write_lock;
+       struct mutex mutex;
        struct usb_cdc_line_coding line;                /* bits, stop, parity */
        struct work_struct work;                        /* work queue entry for 
line discipline waking up */
        struct tasklet_struct urb_task;                 /* rx processing */
@@ -120,6 +121,7 @@ struct acm {
        unsigned char throttle;                         /* throttled by tty 
layer */
        unsigned char clocal;                           /* termios CLOCAL */
        unsigned int ctrl_caps;                         /* control capabilities 
from the class specific header */
+       unsigned int susp_count;                        /* number of suspended 
interfaces */
 };
 
 #define CDC_DATA_INTERFACE_TYPE        0x0a
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to