From: xiao jin <jin.xiao@xxxxxxxxx> Date: Tue, 8 Oct 2013 16:57:29 +0800 Subject: [PATCH] cdc-acm: put delayed_wb to list 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.xiao@xxxxxxxxx> --- 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-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html