diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 52 |
1 files changed, 47 insertions, 5 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8faa23cd74f1..a9193e3de7e1 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -155,7 +155,6 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) wb->urb->transfer_dma = wb->dmah; wb->urb->transfer_buffer_length = wb->len; wb->urb->dev = acm->dev; - rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { dev_err(&acm->data->dev, @@ -183,6 +182,15 @@ static int acm_write_start(struct acm *acm, int wbn) acm->susp_count); usb_autopm_get_interface_async(acm->control); if (acm->susp_count) { +#ifdef CONFIG_PM + printk("%s buffer urb\n", __func__); + acm->transmitting++; + wb->urb->transfer_buffer = wb->buf; + wb->urb->transfer_dma = wb->dmah; + wb->urb->transfer_buffer_length = wb->len; + wb->urb->dev = acm->dev; + usb_anchor_urb(wb->urb, &acm->deferred); +#endif if (!acm->delayed_wb) acm->delayed_wb = wb; else @@ -476,7 +484,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) if (usb_autopm_get_interface(acm->control) < 0) goto early_bail; else - acm->control->needs_remote_wakeup = 1; + acm->control->needs_remote_wakeup = 0; mutex_lock(&acm->mutex); if (acm->port.count++) { @@ -865,8 +873,12 @@ static int acm_probe(struct usb_interface *intf, quirks = (unsigned long)id->driver_info; num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR; + /* not a real CDC ACM device */ + if (quirks & NOT_REAL_ACM) + return -ENODEV; + /* handle quirks deadly to normal probing*/ - if (quirks == NO_UNION_NORMAL) { + if (quirks & NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); goto skip_normal_probe; @@ -1076,6 +1088,7 @@ made_compressed_probe: acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; INIT_WORK(&acm->work, acm_softint); + init_usb_anchor(&acm->deferred); spin_lock_init(&acm->write_lock); spin_lock_init(&acm->read_lock); mutex_init(&acm->mutex); @@ -1083,6 +1096,8 @@ made_compressed_probe: acm->is_int_ep = usb_endpoint_xfer_int(epread); if (acm->is_int_ep) acm->bInterval = epread->bInterval; + if (quirks & NO_HANGUP_IN_RESET_RESUME) + acm->no_hangup_in_reset_resume = 1; tty_port_init(&acm->port); acm->port.ops = &acm_port_ops; @@ -1343,6 +1358,7 @@ static int acm_resume(struct usb_interface *intf) struct acm *acm = usb_get_intfdata(intf); struct acm_wb *wb; int rv = 0; + struct urb *res; int cnt; spin_lock_irq(&acm->read_lock); @@ -1353,10 +1369,21 @@ static int acm_resume(struct usb_interface *intf) if (cnt) return 0; + mutex_lock(&acm->mutex); + +#ifdef CONFIG_PM + while ((res = usb_get_from_anchor(&acm->deferred))) { + printk("%s process buffered request \n", __func__); + rv = usb_submit_urb(res, GFP_ATOMIC); + if (rv < 0) { + dbg("usb_submit_urb(pending request) failed: %d", rv); + } + } +#endif + if (acm->port.count) { rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO); - spin_lock_irq(&acm->write_lock); if (acm->delayed_wb) { wb = acm->delayed_wb; @@ -1367,6 +1394,7 @@ static int acm_resume(struct usb_interface *intf) spin_unlock_irq(&acm->write_lock); } + /* * delayed error checking because we must * do the write path at all cost @@ -1391,7 +1419,8 @@ static int acm_reset_resume(struct usb_interface *intf) if (acm->port.count) { tty = tty_port_tty_get(&acm->port); if (tty) { - tty_hangup(tty); + if (!acm->no_hangup_in_reset_resume) + tty_hangup(tty); tty_kref_put(tty); } } @@ -1481,6 +1510,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x1519, 0x0020), + .driver_info = NO_UNION_NORMAL | NO_HANGUP_IN_RESET_RESUME, /* has no union descriptor */ + }, /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command @@ -1561,6 +1593,16 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NO_DATA_INTERFACE, }, + /* Exclude XMM6260 boot rom (not running modem software yet) */ + { USB_DEVICE(0x058b, 0x0041), + .driver_info = NOT_REAL_ACM, + }, + + /* Icera 450 */ + { USB_DEVICE(0x1983, 0x0321), + .driver_info = NO_HANGUP_IN_RESET_RESUME, + }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, |