summaryrefslogtreecommitdiff
path: root/drivers/usb/class/cdc-acm.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-12-13 11:10:36 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-13 11:10:36 -0800
commit03f8d4cca352fd41f26b5c88dec1e4d3f507f5de (patch)
treed6aece65d391a0d55cc67fadb558a0910b13616c /drivers/usb/class/cdc-acm.c
parenta67485d4bf97918225dfb5246e531643755a7ee1 (diff)
parentd8a12b7117b42fd708f1e908498350232bdbd5ff (diff)
Merge tag 'usb-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH: "Here's the big set of USB/PHY patches for 4.10-rc1. A number of new drivers are here in this set of changes. We have a new USB controller type "mtu3", a new usb-serial driver, and the usual churn in the gadget subsystem and the xhci host controller driver, along with a few other new small drivers added. And lots of little other changes all over the USB and PHY driver tree. Full details are in the shortlog All of these have been in linux-next for a while with no reported issues" * tag 'usb-4.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (309 commits) USB: serial: option: add dlink dwm-158 USB: serial: option: add support for Telit LE922A PIDs 0x1040, 0x1041 USB: OHCI: nxp: fix code warnings USB: OHCI: nxp: remove useless extern declaration USB: OHCI: at91: remove useless extern declaration usb: misc: rio500: fix result type for error message usb: mtu3: fix U3 port link issue usb: mtu3: enable auto switch from U3 to U2 usbip: fix warning in vhci_hcd_probe/lockdep_init_map usb: core: usbport: Use proper LED API to fix potential crash usbip: add missing compile time generated files to .gitignore usb: hcd.h: construct hub class request constants from simpler constants USB: OHCI: ohci-pxa27x: remove useless functions USB: OHCI: omap: remove useless extern declaration USB: OHCI: ohci-omap: remove useless functions USB: OHCI: ohci-s3c2410: remove useless functions USB: cdc-acm: add device id for GW Instek AFG-125 fsl/usb: Workarourd for USB erratum-A005697 usb: hub: Wait for connection to be reestablished after port reset usbip: vudc: Refactor init_vudc_hw() to be more obvious ...
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r--drivers/usb/class/cdc-acm.c216
1 files changed, 116 insertions, 100 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index fada988512a1..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;
@@ -969,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,
@@ -1012,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;
@@ -1088,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);
}
@@ -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,
};
/*