diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-13 11:10:36 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-13 11:10:36 -0800 |
| commit | 03f8d4cca352fd41f26b5c88dec1e4d3f507f5de (patch) | |
| tree | d6aece65d391a0d55cc67fadb558a0910b13616c /drivers/usb/class/cdc-acm.c | |
| parent | a67485d4bf97918225dfb5246e531643755a7ee1 (diff) | |
| parent | d8a12b7117b42fd708f1e908498350232bdbd5ff (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.c | 216 |
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, }; /* |
