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.c220
1 files changed, 118 insertions, 102 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 78f0f85bebdc..e35b1508d3eb 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -133,8 +133,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
buf, len, 5000);
dev_dbg(&acm->control->dev,
- "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
- __func__, request, value, len, retval);
+ "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
+ __func__, request, value, len, retval);
usb_autopm_put_interface(acm->control);
@@ -158,6 +158,17 @@ static inline int acm_set_control(struct acm *acm, int control)
#define acm_send_break(acm, ms) \
acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
+static void acm_kill_urbs(struct acm *acm)
+{
+ int i;
+
+ usb_kill_urb(acm->ctrlurb);
+ for (i = 0; i < ACM_NW; i++)
+ usb_kill_urb(acm->wb[i].urb);
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->read_urbs[i]);
+}
+
/*
* Write buffer management.
* All of these assume proper locks taken by the caller.
@@ -291,13 +302,13 @@ static void acm_ctrl_irq(struct urb *urb)
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(&acm->control->dev,
- "%s - urb shutting down with status: %d\n",
- __func__, status);
+ "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
dev_dbg(&acm->control->dev,
- "%s - nonzero urb status received: %d\n",
- __func__, status);
+ "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
@@ -306,16 +317,16 @@ static void acm_ctrl_irq(struct urb *urb)
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
- dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
- __func__, dr->wValue);
+ dev_dbg(&acm->control->dev,
+ "%s - network connection: %d\n", __func__, dr->wValue);
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
newctrl = get_unaligned_le16(data);
if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
- dev_dbg(&acm->control->dev, "%s - calling hangup\n",
- __func__);
+ dev_dbg(&acm->control->dev,
+ "%s - calling hangup\n", __func__);
tty_port_tty_hangup(&acm->port, false);
}
@@ -357,8 +368,8 @@ static void acm_ctrl_irq(struct urb *urb)
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval && retval != -EPERM)
- dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
- __func__, retval);
+ dev_err(&acm->control->dev,
+ "%s - usb_submit_urb failed: %d\n", __func__, retval);
}
static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
@@ -372,8 +383,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
if (res) {
if (res != -EPERM) {
dev_err(&acm->data->dev,
- "urb %d failed submission with %d\n",
- index, res);
+ "urb %d failed submission with %d\n",
+ index, res);
}
set_bit(index, &acm->read_urbs_free);
return res;
@@ -416,30 +427,43 @@ static void acm_read_bulk_callback(struct urb *urb)
int status = urb->status;
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
- rb->index, urb->actual_length,
- status);
+ rb->index, urb->actual_length, status);
+
+ set_bit(rb->index, &acm->read_urbs_free);
if (!acm->dev) {
- set_bit(rb->index, &acm->read_urbs_free);
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
return;
}
- if (status) {
- set_bit(rb->index, &acm->read_urbs_free);
- if ((status != -ENOENT) || (urb->actual_length == 0))
- return;
+ switch (status) {
+ case 0:
+ usb_mark_last_busy(acm->dev);
+ acm_process_read_urb(acm, urb);
+ break;
+ case -EPIPE:
+ set_bit(EVENT_RX_STALL, &acm->flags);
+ schedule_work(&acm->work);
+ return;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&acm->data->dev,
+ "%s - urb shutting down with status: %d\n",
+ __func__, status);
+ return;
+ default:
+ dev_dbg(&acm->data->dev,
+ "%s - nonzero urb status received: %d\n",
+ __func__, status);
+ break;
}
- usb_mark_last_busy(acm->dev);
-
- acm_process_read_urb(acm, urb);
/*
* Unthrottle may run on another CPU which needs to see events
* in the same order. Submission has an implict barrier
*/
smp_mb__before_atomic();
- set_bit(rb->index, &acm->read_urbs_free);
/* throttle device if requested by tty */
spin_lock_irqsave(&acm->read_lock, flags);
@@ -469,14 +493,30 @@ static void acm_write_bulk(struct urb *urb)
spin_lock_irqsave(&acm->write_lock, flags);
acm_write_done(acm, wb);
spin_unlock_irqrestore(&acm->write_lock, flags);
+ set_bit(EVENT_TTY_WAKEUP, &acm->flags);
schedule_work(&acm->work);
}
static void acm_softint(struct work_struct *work)
{
+ int i;
struct acm *acm = container_of(work, struct acm, work);
- tty_port_tty_wakeup(&acm->port);
+ if (test_bit(EVENT_RX_STALL, &acm->flags)) {
+ if (!(usb_autopm_get_interface(acm->data))) {
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->read_urbs[i]);
+ usb_clear_halt(acm->dev, acm->in);
+ acm_submit_read_urbs(acm, GFP_KERNEL);
+ usb_autopm_put_interface(acm->data);
+ }
+ clear_bit(EVENT_RX_STALL, &acm->flags);
+ }
+
+ if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) {
+ tty_port_tty_wakeup(&acm->port);
+ clear_bit(EVENT_TTY_WAKEUP, &acm->flags);
+ }
}
/*
@@ -608,7 +648,6 @@ static void acm_port_shutdown(struct tty_port *port)
struct acm *acm = container_of(port, struct acm, port);
struct urb *urb;
struct acm_wb *wb;
- int i;
/*
* Need to grab write_lock to prevent race with resume, but no need to
@@ -630,11 +669,7 @@ static void acm_port_shutdown(struct tty_port *port)
usb_autopm_put_interface_async(acm->control);
}
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_kill_urb(acm->read_urbs[i]);
+ acm_kill_urbs(acm);
}
static void acm_tty_cleanup(struct tty_struct *tty)
@@ -837,8 +872,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
retval = acm_send_break(acm, state ? 0xffff : 0);
if (retval < 0)
- dev_dbg(&acm->control->dev, "%s - send break failed\n",
- __func__);
+ dev_dbg(&acm->control->dev,
+ "%s - send break failed\n", __func__);
return retval;
}
@@ -877,9 +912,6 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
{
struct serial_struct tmp;
- if (!info)
- return -EINVAL;
-
memset(&tmp, 0, sizeof(tmp));
tmp.flags = ASYNC_LOW_LATENCY;
tmp.xmit_fifo_size = acm->writesize;
@@ -932,8 +964,6 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))
- return -EINVAL;
do {
spin_lock_irq(&acm->read_lock);
old = acm->oldcount;
@@ -971,25 +1001,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
return rv;
}
-static int get_serial_usage(struct acm *acm,
- struct serial_icounter_struct __user *count)
+static int acm_tty_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
{
- struct serial_icounter_struct icount;
- int rv = 0;
-
- memset(&icount, 0, sizeof(icount));
- icount.dsr = acm->iocount.dsr;
- icount.rng = acm->iocount.rng;
- icount.dcd = acm->iocount.dcd;
- icount.frame = acm->iocount.frame;
- icount.overrun = acm->iocount.overrun;
- icount.parity = acm->iocount.parity;
- icount.brk = acm->iocount.brk;
+ struct acm *acm = tty->driver_data;
- if (copy_to_user(count, &icount, sizeof(icount)) > 0)
- rv = -EFAULT;
+ icount->dsr = acm->iocount.dsr;
+ icount->rng = acm->iocount.rng;
+ icount->dcd = acm->iocount.dcd;
+ icount->frame = acm->iocount.frame;
+ icount->overrun = acm->iocount.overrun;
+ icount->parity = acm->iocount.parity;
+ icount->brk = acm->iocount.brk;
- return rv;
+ return 0;
}
static int acm_tty_ioctl(struct tty_struct *tty,
@@ -1014,9 +1039,6 @@ static int acm_tty_ioctl(struct tty_struct *tty,
rv = wait_serial_change(acm, arg);
usb_autopm_put_interface(acm->control);
break;
- case TIOCGICOUNT:
- rv = get_serial_usage(acm, (struct serial_icounter_struct __user *) arg);
- break;
}
return rv;
@@ -1090,19 +1112,17 @@ static void acm_write_buffers_free(struct acm *acm)
{
int i;
struct acm_wb *wb;
- struct usb_device *usb_dev = interface_to_usbdev(acm->control);
for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
- usb_free_coherent(usb_dev, acm->writesize, wb->buf, wb->dmah);
+ usb_free_coherent(acm->dev, acm->writesize, wb->buf, wb->dmah);
}
static void acm_read_buffers_free(struct acm *acm)
{
- struct usb_device *usb_dev = interface_to_usbdev(acm->control);
int i;
for (i = 0; i < acm->rx_buflimit; i++)
- usb_free_coherent(usb_dev, acm->readsize,
+ usb_free_coherent(acm->dev, acm->readsize,
acm->read_buffers[i].base, acm->read_buffers[i].dma);
}
@@ -1161,6 +1181,8 @@ static int acm_probe(struct usb_interface *intf,
if (quirks == IGNORE_DEVICE)
return -ENODEV;
+ memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header));
+
num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
/* handle quirks deadly to normal probing*/
@@ -1345,9 +1367,16 @@ made_compressed_probe:
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
- acm->is_int_ep = usb_endpoint_xfer_int(epread);
- if (acm->is_int_ep)
+ if (usb_endpoint_xfer_int(epread)) {
acm->bInterval = epread->bInterval;
+ acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress);
+ } else {
+ acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+ }
+ if (usb_endpoint_xfer_int(epwrite))
+ acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
+ else
+ acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
tty_port_init(&acm->port);
acm->port.ops = &acm_port_ops;
init_usb_anchor(&acm->delayed);
@@ -1382,20 +1411,15 @@ made_compressed_probe:
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = rb->dma;
- if (acm->is_int_ep) {
- usb_fill_int_urb(urb, acm->dev,
- usb_rcvintpipe(usb_dev, epread->bEndpointAddress),
- rb->base,
+ if (usb_endpoint_xfer_int(epread))
+ usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,
acm->readsize,
acm_read_bulk_callback, rb,
acm->bInterval);
- } else {
- usb_fill_bulk_urb(urb, acm->dev,
- usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
- rb->base,
+ else
+ usb_fill_bulk_urb(urb, acm->dev, acm->in, rb->base,
acm->readsize,
acm_read_bulk_callback, rb);
- }
acm->read_urbs[i] = urb;
__set_bit(i, &acm->read_urbs_free);
@@ -1408,12 +1432,10 @@ made_compressed_probe:
goto alloc_fail7;
if (usb_endpoint_xfer_int(epwrite))
- usb_fill_int_urb(snd->urb, usb_dev,
- usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_fill_int_urb(snd->urb, usb_dev, acm->out,
NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
else
- usb_fill_bulk_urb(snd->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,
NULL, acm->writesize, acm_write_bulk, snd);
snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (quirks & SEND_ZERO_PACKET)
@@ -1485,8 +1507,8 @@ skip_countries:
}
if (quirks & CLEAR_HALT_CONDITIONS) {
- usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress));
- usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress));
+ usb_clear_halt(usb_dev, acm->in);
+ usb_clear_halt(usb_dev, acm->out);
}
return 0;
@@ -1520,25 +1542,10 @@ alloc_fail:
return rv;
}
-static void stop_data_traffic(struct acm *acm)
-{
- int i;
-
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_kill_urb(acm->read_urbs[i]);
-
- cancel_work_sync(&acm->work);
-}
-
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
- struct usb_device *usb_dev = interface_to_usbdev(intf);
struct tty_struct *tty;
- int i;
/* sibling interface is already cleaning up */
if (!acm)
@@ -1564,17 +1571,13 @@ static void acm_disconnect(struct usb_interface *intf)
tty_kref_put(tty);
}
- stop_data_traffic(acm);
+ acm_kill_urbs(acm);
+ cancel_work_sync(&acm->work);
tty_unregister_device(acm_tty_driver, acm->minor);
- usb_free_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_free_urb(acm->wb[i].urb);
- for (i = 0; i < acm->rx_buflimit; i++)
- usb_free_urb(acm->read_urbs[i]);
acm_write_buffers_free(acm);
- usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+ usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
acm_read_buffers_free(acm);
if (!acm->combined_interfaces)
@@ -1603,7 +1606,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
if (cnt)
return 0;
- stop_data_traffic(acm);
+ acm_kill_urbs(acm);
+ cancel_work_sync(&acm->work);
return 0;
}
@@ -1657,6 +1661,15 @@ static int acm_reset_resume(struct usb_interface *intf)
#endif /* CONFIG_PM */
+static int acm_pre_reset(struct usb_interface *intf)
+{
+ struct acm *acm = usb_get_intfdata(intf);
+
+ clear_bit(EVENT_RX_STALL, &acm->flags);
+
+ return 0;
+}
+
#define NOKIA_PCSUITE_ACM_INFO(x) \
USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
@@ -1719,6 +1732,7 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
@@ -1898,6 +1912,7 @@ static struct usb_driver acm_driver = {
.resume = acm_resume,
.reset_resume = acm_reset_resume,
#endif
+ .pre_reset = acm_pre_reset,
.id_table = acm_ids,
#ifdef CONFIG_PM
.supports_autosuspend = 1,
@@ -1927,6 +1942,7 @@ static const struct tty_operations acm_ops = {
.set_termios = acm_tty_set_termios,
.tiocmget = acm_tty_tiocmget,
.tiocmset = acm_tty_tiocmset,
+ .get_icount = acm_tty_get_icount,
};
/*