summaryrefslogtreecommitdiff
path: root/drivers/usb/class/cdc-acm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r--drivers/usb/class/cdc-acm.c52
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) },