If acm_write_start during acm suspend, write acm_wb is backuped
to delayed_wb. When acm resume, the delayed_wb will be started.
This mechanism can only record one write during acm suspend. More
acm write will be abandoned.

This patch is to use list_head to record the write acm_wb during acm
suspend. It can ensure no acm write abandoned.

Signed-off-by: xiao jin <jin.x...@intel.com>
---
 drivers/usb/class/cdc-acm.c |   30 ++++++++++++++++++++----------
 drivers/usb/class/cdc-acm.h |    7 ++++++-
 2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 9f49bfe..be679be 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -607,6 +607,7 @@ static int acm_tty_write(struct tty_struct *tty,
        unsigned long flags;
        int wbn;
        struct acm_wb *wb;
+       struct delayed_wb *d_wb;
 
        if (!count)
                return 0;
@@ -634,12 +635,17 @@ static int acm_tty_write(struct tty_struct *tty,
 
        usb_autopm_get_interface_async(acm->control);
        if (acm->susp_count) {
-               if (!acm->delayed_wb)
-                       acm->delayed_wb = wb;
-               else
+               d_wb = kmalloc(sizeof(struct delayed_wb), GFP_ATOMIC);
+               if (d_wb == NULL) {
                        usb_autopm_put_interface_async(acm->control);
-               spin_unlock_irqrestore(&acm->write_lock, flags);
-               return count;   /* A white lie */
+                       spin_unlock_irqrestore(&acm->write_lock, flags);
+                       return -ENOMEM;
+               } else {
+                       d_wb->wb = wb;
+                       list_add_tail(&d_wb->list, &acm->delayed_wb_list);
+                       spin_unlock_irqrestore(&acm->write_lock, flags);
+                       return count;   /* A white lie */
+               }
        }
        usb_mark_last_busy(acm->dev);
 
@@ -1255,6 +1261,7 @@ made_compressed_probe:
                snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
                snd->instance = acm;
        }
+       INIT_LIST_HEAD(&acm->delayed_wb_list);
 
        usb_set_intfdata(intf, acm);
 
@@ -1449,6 +1456,7 @@ static int acm_resume(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
        struct acm_wb *wb;
+       struct delayed_wb *d_wb, *nd_wb;
        int rv = 0;
        int cnt;
 
@@ -1464,14 +1472,16 @@ static int acm_resume(struct usb_interface *intf)
                rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
 
                spin_lock_irq(&acm->write_lock);
-               if (acm->delayed_wb) {
-                       wb = acm->delayed_wb;
-                       acm->delayed_wb = NULL;
+               list_for_each_entry_safe(d_wb, nd_wb,
+                               &acm->delayed_wb_list, list) {
+                       wb = d_wb->wb;
+                       list_del(&d_wb->list);
+                       kfree(d_wb);
                        spin_unlock_irq(&acm->write_lock);
                        acm_start_wb(acm, wb);
-               } else {
-                       spin_unlock_irq(&acm->write_lock);
+                       spin_lock_irq(&acm->write_lock);
                }
+               spin_unlock_irq(&acm->write_lock);
 
                /*
                 * delayed error checking because we must
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 0f76e4a..5eed93f 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -79,6 +79,11 @@ struct acm_rb {
        struct acm              *instance;
 };
 
+struct delayed_wb {
+       struct list_head        list;
+       struct acm_wb           *wb;
+}
+
 struct acm {
        struct usb_device *dev;                         /* the corresponding 
usb device */
        struct usb_interface *control;                  /* control interface */
@@ -117,7 +122,7 @@ struct acm {
        unsigned int throttled:1;                       /* actually throttled */
        unsigned int throttle_req:1;                    /* throttle requested */
        u8 bInterval;
-       struct acm_wb *delayed_wb;                      /* write queued for a 
device about to be woken */
+       struct list_head delayed_wb_list;               /* delayed wb list */
 };
 
 #define CDC_DATA_INTERFACE_TYPE        0x0a
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to