diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 220 |
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, }; /* |