aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/class
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/Kconfig6
-rw-r--r--drivers/usb/class/cdc-acm.c1266
-rw-r--r--drivers/usb/class/cdc-acm.h41
-rw-r--r--drivers/usb/class/cdc-wdm.c562
-rw-r--r--drivers/usb/class/usblp.c105
-rw-r--r--drivers/usb/class/usbtmc.c366
6 files changed, 1419 insertions, 927 deletions
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index 2519e320098..bb8b73682a7 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -2,11 +2,10 @@
# USB Class driver configuration
#
comment "USB Device Class drivers"
- depends on USB
config USB_ACM
tristate "USB Modem (CDC ACM) support"
- depends on USB
+ depends on TTY
---help---
This driver supports USB modems and ISDN adapters which support the
Communication Device Class Abstract Control Model interface.
@@ -21,7 +20,6 @@ config USB_ACM
config USB_PRINTER
tristate "USB Printer support"
- depends on USB
help
Say Y here if you want to connect a USB printer to your computer's
USB port.
@@ -31,7 +29,6 @@ config USB_PRINTER
config USB_WDM
tristate "USB Wireless Device Management support"
- depends on USB
---help---
This driver supports the WMC Device Management functionality
of cell phones compliant to the CDC WMC specification. You can use
@@ -42,7 +39,6 @@ config USB_WDM
config USB_TMC
tristate "USB Test and Measurement Class support"
- depends on USB
help
Say Y here if you want to connect a USB device that follows
the USB.org specification for USB Test and Measurement devices
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index d6ede989ff2..e934e19f49f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -7,35 +7,12 @@
* Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2004 Oliver Neukum <oliver@neukum.name>
* Copyright (c) 2005 David Kubicek <dave@awk.cz>
+ * Copyright (c) 2011 Johan Hovold <jhovold@gmail.com>
*
* USB Abstract Control Model driver for USB modems and ISDN adapters
*
* Sponsored by SuSE
*
- * ChangeLog:
- * v0.9 - thorough cleaning, URBification, almost a rewrite
- * v0.10 - some more cleanups
- * v0.11 - fixed flow control, read error doesn't stop reads
- * v0.12 - added TIOCM ioctls, added break handling, made struct acm
- * kmalloced
- * v0.13 - added termios, added hangup
- * v0.14 - sized down struct acm
- * v0.15 - fixed flow control again - characters could be lost
- * v0.16 - added code for modems with swapped data and control interfaces
- * v0.17 - added new style probing
- * v0.18 - fixed new style probing for devices with more configurations
- * v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
- * v0.20 - switched to probing on interface (rather than device) class
- * v0.21 - revert to probing on device for devices with multiple configs
- * v0.22 - probe only the control interface. if usbcore doesn't choose the
- * config we want, sysadmin changes bConfigurationValue in sysfs.
- * v0.23 - use softirq for rx processing, as needed by tty layer
- * v0.24 - change probe method to evaluate CDC union descriptor
- * v0.25 - downstream tasks paralelized to maximize throughput
- * v0.26 - multiple write urbs, writesize increased
- */
-
-/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -74,31 +51,69 @@
#include "cdc-acm.h"
-#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.26"
-#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
+#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
#define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
static struct usb_driver acm_driver;
static struct tty_driver *acm_tty_driver;
static struct acm *acm_table[ACM_TTY_MINORS];
-static DEFINE_MUTEX(open_mutex);
+static DEFINE_MUTEX(acm_table_lock);
-#define ACM_READY(acm) (acm && acm->dev && acm->port.count)
+/*
+ * acm_table accessors
+ */
-static const struct tty_port_operations acm_port_ops = {
-};
+/*
+ * Look up an ACM structure by index. If found and not disconnected, increment
+ * its refcount and return it with its mutex held.
+ */
+static struct acm *acm_get_by_index(unsigned index)
+{
+ struct acm *acm;
-#ifdef VERBOSE_DEBUG
-#define verbose 1
-#else
-#define verbose 0
-#endif
+ mutex_lock(&acm_table_lock);
+ acm = acm_table[index];
+ if (acm) {
+ mutex_lock(&acm->mutex);
+ if (acm->disconnected) {
+ mutex_unlock(&acm->mutex);
+ acm = NULL;
+ } else {
+ tty_port_get(&acm->port);
+ mutex_unlock(&acm->mutex);
+ }
+ }
+ mutex_unlock(&acm_table_lock);
+ return acm;
+}
+
+/*
+ * Try to find an available minor number and if found, associate it with 'acm'.
+ */
+static int acm_alloc_minor(struct acm *acm)
+{
+ int minor;
+
+ mutex_lock(&acm_table_lock);
+ for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
+ if (!acm_table[minor]) {
+ acm_table[minor] = acm;
+ break;
+ }
+ }
+ mutex_unlock(&acm_table_lock);
+
+ return minor;
+}
+
+/* Release the minor number associated with 'acm'. */
+static void acm_release_minor(struct acm *acm)
+{
+ mutex_lock(&acm_table_lock);
+ acm_table[acm->minor] = NULL;
+ mutex_unlock(&acm_table_lock);
+}
/*
* Functions for ACM control messages.
@@ -107,12 +122,23 @@ static const struct tty_port_operations acm_port_ops = {
static int acm_ctrl_msg(struct acm *acm, int request, int value,
void *buf, int len)
{
- int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+ int retval;
+
+ retval = usb_autopm_get_interface(acm->control);
+ if (retval)
+ return retval;
+
+ retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
request, USB_RT_ACM, value,
acm->control->altsetting[0].desc.bInterfaceNumber,
buf, len, 5000);
- dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
- request, value, len, retval);
+
+ dev_dbg(&acm->control->dev,
+ "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
+ __func__, request, value, len, retval);
+
+ usb_autopm_put_interface(acm->control);
+
return retval < 0 ? retval : 0;
}
@@ -192,43 +218,14 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
if (rc < 0) {
- dbg("usb_submit_urb(write bulk) failed: %d", rc);
+ dev_err(&acm->data->dev,
+ "%s - usb_submit_urb(write bulk) failed: %d\n",
+ __func__, rc);
acm_write_done(acm, wb);
}
return rc;
}
-static int acm_write_start(struct acm *acm, int wbn)
-{
- unsigned long flags;
- struct acm_wb *wb = &acm->wb[wbn];
- int rc;
-
- spin_lock_irqsave(&acm->write_lock, flags);
- if (!acm->dev) {
- wb->use = 0;
- spin_unlock_irqrestore(&acm->write_lock, flags);
- return -ENODEV;
- }
-
- dbg("%s susp_count: %d", __func__, acm->susp_count);
- usb_autopm_get_interface_async(acm->control);
- if (acm->susp_count) {
- if (!acm->delayed_wb)
- acm->delayed_wb = wb;
- else
- usb_autopm_put_interface_async(acm->control);
- spin_unlock_irqrestore(&acm->write_lock, flags);
- return 0; /* A white lie */
- }
- usb_mark_last_busy(acm->dev);
-
- rc = acm_start_wb(acm, wb);
- spin_unlock_irqrestore(&acm->write_lock, flags);
-
- return rc;
-
-}
/*
* attributes exported through sysfs
*/
@@ -273,9 +270,9 @@ static void acm_ctrl_irq(struct urb *urb)
{
struct acm *acm = urb->context;
struct usb_cdc_notification *dr = urb->transfer_buffer;
- struct tty_struct *tty;
unsigned char *data;
int newctrl;
+ int difference;
int retval;
int status = urb->status;
@@ -287,218 +284,158 @@ static void acm_ctrl_irq(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__, status);
+ dev_dbg(&acm->control->dev,
+ "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__, status);
+ dev_dbg(&acm->control->dev,
+ "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
- if (!ACM_READY(acm))
- goto exit;
+ usb_mark_last_busy(acm->dev);
data = (unsigned char *)(dr + 1);
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
- dbg("%s network", dr->wValue ?
- "connected to" : "disconnected from");
+ dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
+ __func__, dr->wValue);
break;
case USB_CDC_NOTIFY_SERIAL_STATE:
- tty = tty_port_tty_get(&acm->port);
newctrl = get_unaligned_le16(data);
- if (tty) {
- if (!acm->clocal &&
- (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
- dbg("calling hangup");
- tty_hangup(tty);
- }
- tty_kref_put(tty);
+ if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
+ dev_dbg(&acm->control->dev, "%s - calling hangup\n",
+ __func__);
+ tty_port_tty_hangup(&acm->port, false);
}
+ difference = acm->ctrlin ^ newctrl;
+ spin_lock(&acm->read_lock);
acm->ctrlin = newctrl;
+ acm->oldcount = acm->iocount;
+
+ if (difference & ACM_CTRL_DSR)
+ acm->iocount.dsr++;
+ if (difference & ACM_CTRL_BRK)
+ acm->iocount.brk++;
+ if (difference & ACM_CTRL_RI)
+ acm->iocount.rng++;
+ if (difference & ACM_CTRL_DCD)
+ acm->iocount.dcd++;
+ if (difference & ACM_CTRL_FRAMING)
+ acm->iocount.frame++;
+ if (difference & ACM_CTRL_PARITY)
+ acm->iocount.parity++;
+ if (difference & ACM_CTRL_OVERRUN)
+ acm->iocount.overrun++;
+ spin_unlock(&acm->read_lock);
- dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
- acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
- acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
- acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
- acm->ctrlin & ACM_CTRL_RI ? '+' : '-',
- acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
- acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
- acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
- break;
+ if (difference)
+ wake_up_all(&acm->wioctl);
+
+ break;
default:
- dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
+ dev_dbg(&acm->control->dev,
+ "%s - unknown notification %d received: index %d "
+ "len %d data0 %d data1 %d\n",
+ __func__,
dr->bNotificationType, dr->wIndex,
dr->wLength, data[0], data[1]);
break;
}
exit:
- usb_mark_last_busy(acm->dev);
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
- "result %d", __func__, retval);
+ dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
+ __func__, retval);
}
-/* data interface returns incoming bytes, or we got unthrottled */
-static void acm_read_bulk(struct urb *urb)
+static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
{
- struct acm_rb *buf;
- struct acm_ru *rcv = urb->context;
- struct acm *acm = rcv->instance;
- int status = urb->status;
+ int res;
- dbg("Entering acm_read_bulk with status %d", status);
+ if (!test_and_clear_bit(index, &acm->read_urbs_free))
+ return 0;
- if (!ACM_READY(acm)) {
- dev_dbg(&acm->data->dev, "Aborting, acm not ready");
- return;
+ dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
+
+ res = usb_submit_urb(acm->read_urbs[index], mem_flags);
+ if (res) {
+ if (res != -EPERM) {
+ dev_err(&acm->data->dev,
+ "%s - usb_submit_urb failed: %d\n",
+ __func__, res);
+ }
+ set_bit(index, &acm->read_urbs_free);
+ return res;
}
- usb_mark_last_busy(acm->dev);
- if (status)
- dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
+ return 0;
+}
- buf = rcv->buffer;
- buf->size = urb->actual_length;
+static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
+{
+ int res;
+ int i;
- if (likely(status == 0)) {
- spin_lock(&acm->read_lock);
- acm->processing++;
- list_add_tail(&rcv->list, &acm->spare_read_urbs);
- list_add_tail(&buf->list, &acm->filled_read_bufs);
- spin_unlock(&acm->read_lock);
- } else {
- /* we drop the buffer due to an error */
- spin_lock(&acm->read_lock);
- list_add_tail(&rcv->list, &acm->spare_read_urbs);
- list_add(&buf->list, &acm->spare_read_bufs);
- spin_unlock(&acm->read_lock);
- /* nevertheless the tasklet must be kicked unconditionally
- so the queue cannot dry up */
+ for (i = 0; i < acm->rx_buflimit; ++i) {
+ res = acm_submit_read_urb(acm, i, mem_flags);
+ if (res)
+ return res;
}
- if (likely(!acm->susp_count))
- tasklet_schedule(&acm->urb_task);
+
+ return 0;
}
-static void acm_rx_tasklet(unsigned long _acm)
+static void acm_process_read_urb(struct acm *acm, struct urb *urb)
{
- struct acm *acm = (void *)_acm;
- struct acm_rb *buf;
- struct tty_struct *tty;
- struct acm_ru *rcv;
+ if (!urb->actual_length)
+ return;
+
+ tty_insert_flip_string(&acm->port, urb->transfer_buffer,
+ urb->actual_length);
+ tty_flip_buffer_push(&acm->port);
+}
+
+static void acm_read_bulk_callback(struct urb *urb)
+{
+ struct acm_rb *rb = urb->context;
+ struct acm *acm = rb->instance;
unsigned long flags;
- unsigned char throttled;
- dbg("Entering acm_rx_tasklet");
+ dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
+ rb->index, urb->actual_length);
+ set_bit(rb->index, &acm->read_urbs_free);
- if (!ACM_READY(acm)) {
- dbg("acm_rx_tasklet: ACM not ready");
+ if (!acm->dev) {
+ dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
return;
}
- spin_lock_irqsave(&acm->throttle_lock, flags);
- throttled = acm->throttle;
- spin_unlock_irqrestore(&acm->throttle_lock, flags);
- if (throttled) {
- dbg("acm_rx_tasklet: throttled");
+ if (urb->status) {
+ dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
+ __func__, urb->status);
return;
}
- tty = tty_port_tty_get(&acm->port);
-
-next_buffer:
- spin_lock_irqsave(&acm->read_lock, flags);
- if (list_empty(&acm->filled_read_bufs)) {
- spin_unlock_irqrestore(&acm->read_lock, flags);
- goto urbs;
- }
- buf = list_entry(acm->filled_read_bufs.next,
- struct acm_rb, list);
- list_del(&buf->list);
- spin_unlock_irqrestore(&acm->read_lock, flags);
-
- dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
+ usb_mark_last_busy(acm->dev);
- if (tty) {
- spin_lock_irqsave(&acm->throttle_lock, flags);
- throttled = acm->throttle;
- spin_unlock_irqrestore(&acm->throttle_lock, flags);
- if (!throttled) {
- tty_insert_flip_string(tty, buf->base, buf->size);
- tty_flip_buffer_push(tty);
- } else {
- tty_kref_put(tty);
- dbg("Throttling noticed");
- spin_lock_irqsave(&acm->read_lock, flags);
- list_add(&buf->list, &acm->filled_read_bufs);
- spin_unlock_irqrestore(&acm->read_lock, flags);
- return;
- }
- }
+ acm_process_read_urb(acm, urb);
+ /* throttle device if requested by tty */
spin_lock_irqsave(&acm->read_lock, flags);
- list_add(&buf->list, &acm->spare_read_bufs);
- spin_unlock_irqrestore(&acm->read_lock, flags);
- goto next_buffer;
-
-urbs:
- tty_kref_put(tty);
-
- while (!list_empty(&acm->spare_read_bufs)) {
- spin_lock_irqsave(&acm->read_lock, flags);
- if (list_empty(&acm->spare_read_urbs)) {
- acm->processing = 0;
- spin_unlock_irqrestore(&acm->read_lock, flags);
- return;
- }
- rcv = list_entry(acm->spare_read_urbs.next,
- struct acm_ru, list);
- list_del(&rcv->list);
+ acm->throttled = acm->throttle_req;
+ if (!acm->throttled) {
+ spin_unlock_irqrestore(&acm->read_lock, flags);
+ acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
+ } else {
spin_unlock_irqrestore(&acm->read_lock, flags);
-
- buf = list_entry(acm->spare_read_bufs.next,
- struct acm_rb, list);
- list_del(&buf->list);
-
- rcv->buffer = buf;
-
- if (acm->is_int_ep)
- usb_fill_int_urb(rcv->urb, acm->dev,
- acm->rx_endpoint,
- buf->base,
- acm->readsize,
- acm_read_bulk, rcv, acm->bInterval);
- else
- usb_fill_bulk_urb(rcv->urb, acm->dev,
- acm->rx_endpoint,
- buf->base,
- acm->readsize,
- acm_read_bulk, rcv);
- rcv->urb->transfer_dma = buf->dma;
- rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- /* This shouldn't kill the driver as unsuccessful URBs are
- returned to the free-urbs-pool and resubmited ASAP */
- spin_lock_irqsave(&acm->read_lock, flags);
- if (acm->susp_count ||
- usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
- list_add(&buf->list, &acm->spare_read_bufs);
- list_add(&rcv->list, &acm->spare_read_urbs);
- acm->processing = 0;
- spin_unlock_irqrestore(&acm->read_lock, flags);
- return;
- } else {
- spin_unlock_irqrestore(&acm->read_lock, flags);
- dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
- }
}
- spin_lock_irqsave(&acm->read_lock, flags);
- acm->processing = 0;
- spin_unlock_irqrestore(&acm->read_lock, flags);
}
/* data interface wrote those outgoing bytes */
@@ -508,9 +445,9 @@ static void acm_write_bulk(struct urb *urb)
struct acm *acm = wb->instance;
unsigned long flags;
- if (verbose || urb->status
- || (urb->actual_length != urb->transfer_buffer_length))
- dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
+ if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
+ dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
+ __func__,
urb->actual_length,
urb->transfer_buffer_length,
urb->status);
@@ -518,170 +455,203 @@ 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);
- if (ACM_READY(acm))
- schedule_work(&acm->work);
- else
- wake_up_interruptible(&acm->drain_wait);
+ schedule_work(&acm->work);
}
static void acm_softint(struct work_struct *work)
{
struct acm *acm = container_of(work, struct acm, work);
- struct tty_struct *tty;
- dev_vdbg(&acm->data->dev, "tx work\n");
- if (!ACM_READY(acm))
- return;
- tty = tty_port_tty_get(&acm->port);
- tty_wakeup(tty);
- tty_kref_put(tty);
+ dev_vdbg(&acm->data->dev, "%s\n", __func__);
+
+ tty_port_tty_wakeup(&acm->port);
}
/*
* TTY handlers
*/
-static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct acm *acm;
- int rv = -ENODEV;
- int i;
- dbg("Entering acm_tty_open.");
+ int retval;
- mutex_lock(&open_mutex);
+ dev_dbg(tty->dev, "%s\n", __func__);
- acm = acm_table[tty->index];
- if (!acm || !acm->dev)
- goto out;
- else
- rv = 0;
+ acm = acm_get_by_index(tty->index);
+ if (!acm)
+ return -ENODEV;
- set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
+ retval = tty_standard_install(driver, tty);
+ if (retval)
+ goto error_init_termios;
tty->driver_data = acm;
- tty_port_tty_set(&acm->port, tty);
- if (usb_autopm_get_interface(acm->control) < 0)
- goto early_bail;
+ return 0;
+
+error_init_termios:
+ tty_port_put(&acm->port);
+ return retval;
+}
+
+static int acm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct acm *acm = tty->driver_data;
+
+ dev_dbg(tty->dev, "%s\n", __func__);
+
+ return tty_port_open(&acm->port, tty, filp);
+}
+
+static void acm_port_dtr_rts(struct tty_port *port, int raise)
+{
+ struct acm *acm = container_of(port, struct acm, port);
+ int val;
+ int res;
+
+ if (raise)
+ val = ACM_CTRL_DTR | ACM_CTRL_RTS;
else
- acm->control->needs_remote_wakeup = 1;
+ val = 0;
- mutex_lock(&acm->mutex);
- if (acm->port.count++) {
- mutex_unlock(&acm->mutex);
- usb_autopm_put_interface(acm->control);
- goto out;
- }
+ /* FIXME: add missing ctrlout locking throughout driver */
+ acm->ctrlout = val;
- acm->ctrlurb->dev = acm->dev;
- if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
- dbg("usb_submit_urb(ctrl irq) failed");
- goto bail_out;
- }
+ res = acm_set_control(acm, val);
+ if (res && (acm->ctrl_caps & USB_CDC_CAP_LINE))
+ dev_err(&acm->control->dev, "failed to set dtr/rts\n");
+}
- if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
- (acm->ctrl_caps & USB_CDC_CAP_LINE))
- goto full_bailout;
+static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct acm *acm = container_of(port, struct acm, port);
+ int retval = -ENODEV;
+ int i;
- usb_autopm_put_interface(acm->control);
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
- INIT_LIST_HEAD(&acm->spare_read_urbs);
- INIT_LIST_HEAD(&acm->spare_read_bufs);
- INIT_LIST_HEAD(&acm->filled_read_bufs);
+ mutex_lock(&acm->mutex);
+ if (acm->disconnected)
+ goto disconnected;
- for (i = 0; i < acm->rx_buflimit; i++)
- list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
- for (i = 0; i < acm->rx_buflimit; i++)
- list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
+ retval = usb_autopm_get_interface(acm->control);
+ if (retval)
+ goto error_get_interface;
- acm->throttle = 0;
+ /*
+ * FIXME: Why do we need this? Allocating 64K of physically contiguous
+ * memory is really nasty...
+ */
+ set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
+ acm->control->needs_remote_wakeup = 1;
- set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
- rv = tty_port_block_til_ready(&acm->port, tty, filp);
- tasklet_schedule(&acm->urb_task);
+ acm->ctrlurb->dev = acm->dev;
+ retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&acm->control->dev,
+ "%s - usb_submit_urb(ctrl irq) failed\n", __func__);
+ goto error_submit_urb;
+ }
+
+ /*
+ * Unthrottle device in case the TTY was closed while throttled.
+ */
+ spin_lock_irq(&acm->read_lock);
+ acm->throttled = 0;
+ acm->throttle_req = 0;
+ spin_unlock_irq(&acm->read_lock);
+
+ retval = acm_submit_read_urbs(acm, GFP_KERNEL);
+ if (retval)
+ goto error_submit_read_urbs;
+
+ usb_autopm_put_interface(acm->control);
mutex_unlock(&acm->mutex);
-out:
- mutex_unlock(&open_mutex);
- return rv;
-full_bailout:
+ return 0;
+
+error_submit_read_urbs:
+ for (i = 0; i < acm->rx_buflimit; i++)
+ usb_kill_urb(acm->read_urbs[i]);
usb_kill_urb(acm->ctrlurb);
-bail_out:
- acm->port.count--;
- mutex_unlock(&acm->mutex);
+error_submit_urb:
usb_autopm_put_interface(acm->control);
-early_bail:
- mutex_unlock(&open_mutex);
- tty_port_tty_set(&acm->port, NULL);
- return -EIO;
+error_get_interface:
+disconnected:
+ mutex_unlock(&acm->mutex);
+
+ return usb_translate_errors(retval);
}
-static void acm_tty_unregister(struct acm *acm)
+static void acm_port_destruct(struct tty_port *port)
{
- int i, nr;
+ struct acm *acm = container_of(port, struct acm, port);
- nr = acm->rx_buflimit;
- tty_unregister_device(acm_tty_driver, acm->minor);
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
+
+ acm_release_minor(acm);
usb_put_intf(acm->control);
- acm_table[acm->minor] = NULL;
- usb_free_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_free_urb(acm->wb[i].urb);
- for (i = 0; i < nr; i++)
- usb_free_urb(acm->ru[i].urb);
kfree(acm->country_codes);
kfree(acm);
}
-static int acm_tty_chars_in_buffer(struct tty_struct *tty);
-
-static void acm_port_down(struct acm *acm)
+static void acm_port_shutdown(struct tty_port *port)
{
- int i, nr = acm->rx_buflimit;
- mutex_lock(&open_mutex);
- if (acm->dev) {
- usb_autopm_get_interface(acm->control);
- acm_set_control(acm, acm->ctrlout = 0);
- usb_kill_urb(acm->ctrlurb);
- for (i = 0; i < ACM_NW; i++)
- usb_kill_urb(acm->wb[i].urb);
- for (i = 0; i < nr; i++)
- usb_kill_urb(acm->ru[i].urb);
- acm->control->needs_remote_wakeup = 0;
- usb_autopm_put_interface(acm->control);
+ struct acm *acm = container_of(port, struct acm, port);
+ struct urb *urb;
+ struct acm_wb *wb;
+ int i;
+
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
+
+ /*
+ * Need to grab write_lock to prevent race with resume, but no need to
+ * hold it due to the tty-port initialised flag.
+ */
+ spin_lock_irq(&acm->write_lock);
+ spin_unlock_irq(&acm->write_lock);
+
+ usb_autopm_get_interface_no_resume(acm->control);
+ acm->control->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(acm->control);
+
+ for (;;) {
+ urb = usb_get_from_anchor(&acm->delayed);
+ if (!urb)
+ break;
+ wb = urb->context;
+ wb->use = 0;
+ usb_autopm_put_interface_async(acm->control);
}
- mutex_unlock(&open_mutex);
+
+ 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]);
+}
+
+static void acm_tty_cleanup(struct tty_struct *tty)
+{
+ struct acm *acm = tty->driver_data;
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
+ tty_port_put(&acm->port);
}
static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
tty_port_hangup(&acm->port);
- acm_port_down(acm);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
-
- /* Perform the closing process and see if we need to do the hardware
- shutdown */
- if (!acm)
- return;
- if (tty_port_close_start(&acm->port, tty, filp) == 0) {
- mutex_lock(&open_mutex);
- if (!acm->dev) {
- tty_port_tty_set(&acm->port, NULL);
- acm_tty_unregister(acm);
- tty->driver_data = NULL;
- }
- mutex_unlock(&open_mutex);
- return;
- }
- acm_port_down(acm);
- tty_port_close_end(&acm->port, tty);
- tty_port_tty_set(&acm->port, NULL);
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
+ tty_port_close(&acm->port, tty, filp);
}
static int acm_tty_write(struct tty_struct *tty,
@@ -693,13 +663,11 @@ static int acm_tty_write(struct tty_struct *tty,
int wbn;
struct acm_wb *wb;
- dbg("Entering acm_tty_write to write %d bytes,", count);
-
- if (!ACM_READY(acm))
- return -EINVAL;
if (!count)
return 0;
+ dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
+
spin_lock_irqsave(&acm->write_lock, flags);
wbn = acm_wb_alloc(acm);
if (wbn < 0) {
@@ -708,13 +676,33 @@ static int acm_tty_write(struct tty_struct *tty,
}
wb = &acm->wb[wbn];
+ if (!acm->dev) {
+ wb->use = 0;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return -ENODEV;
+ }
+
count = (count > acm->writesize) ? acm->writesize : count;
- dbg("Get %d bytes...", count);
+ dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
memcpy(wb->buf, buf, count);
wb->len = count;
+
+ stat = usb_autopm_get_interface_async(acm->control);
+ if (stat) {
+ wb->use = 0;
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return stat;
+ }
+
+ if (acm->susp_count) {
+ usb_anchor_urb(wb->urb, &acm->delayed);
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return count;
+ }
+
+ stat = acm_start_wb(acm, wb);
spin_unlock_irqrestore(&acm->write_lock, flags);
- stat = acm_write_start(acm, wbn);
if (stat < 0)
return stat;
return count;
@@ -723,8 +711,6 @@ static int acm_tty_write(struct tty_struct *tty,
static int acm_tty_write_room(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- if (!ACM_READY(acm))
- return -EINVAL;
/*
* Do not let the line discipline to know that we have a reserve,
* or it might get too enthusiastic.
@@ -735,7 +721,11 @@ static int acm_tty_write_room(struct tty_struct *tty)
static int acm_tty_chars_in_buffer(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- if (!ACM_READY(acm))
+ /*
+ * if the device was unplugged then any remaining characters fell out
+ * of the connector ;)
+ */
+ if (acm->disconnected)
return 0;
/*
* This is inaccurate (overcounts), but it works.
@@ -746,43 +736,43 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
static void acm_tty_throttle(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- if (!ACM_READY(acm))
- return;
- spin_lock_bh(&acm->throttle_lock);
- acm->throttle = 1;
- spin_unlock_bh(&acm->throttle_lock);
+
+ spin_lock_irq(&acm->read_lock);
+ acm->throttle_req = 1;
+ spin_unlock_irq(&acm->read_lock);
}
static void acm_tty_unthrottle(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- if (!ACM_READY(acm))
- return;
- spin_lock_bh(&acm->throttle_lock);
- acm->throttle = 0;
- spin_unlock_bh(&acm->throttle_lock);
- tasklet_schedule(&acm->urb_task);
+ unsigned int was_throttled;
+
+ spin_lock_irq(&acm->read_lock);
+ was_throttled = acm->throttled;
+ acm->throttled = 0;
+ acm->throttle_req = 0;
+ spin_unlock_irq(&acm->read_lock);
+
+ if (was_throttled)
+ acm_submit_read_urbs(acm, GFP_KERNEL);
}
static int acm_tty_break_ctl(struct tty_struct *tty, int state)
{
struct acm *acm = tty->driver_data;
int retval;
- if (!ACM_READY(acm))
- return -EINVAL;
+
retval = acm_send_break(acm, state ? 0xffff : 0);
if (retval < 0)
- dbg("send break failed");
+ dev_dbg(&acm->control->dev, "%s - send break failed\n",
+ __func__);
return retval;
}
-static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
+static int acm_tty_tiocmget(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- if (!ACM_READY(acm))
- return -EINVAL;
-
return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
(acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
(acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
@@ -791,15 +781,12 @@ static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
TIOCM_CTS;
}
-static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
+static int acm_tty_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct acm *acm = tty->driver_data;
unsigned int newctrl;
- if (!ACM_READY(acm))
- return -EINVAL;
-
newctrl = acm->ctrlout;
set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
@@ -813,46 +800,183 @@ static int acm_tty_tiocmset(struct tty_struct *tty, struct file *file,
return acm_set_control(acm, acm->ctrlout = newctrl);
}
-static int acm_tty_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
{
- struct acm *acm = tty->driver_data;
+ struct serial_struct tmp;
- if (!ACM_READY(acm))
+ if (!info)
return -EINVAL;
- return -ENOIOCTLCMD;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.flags = ASYNC_LOW_LATENCY;
+ tmp.xmit_fifo_size = acm->writesize;
+ tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
+ tmp.close_delay = acm->port.close_delay / 10;
+ tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ ASYNC_CLOSING_WAIT_NONE :
+ acm->port.closing_wait / 10;
+
+ if (copy_to_user(info, &tmp, sizeof(tmp)))
+ return -EFAULT;
+ else
+ return 0;
}
-static const __u32 acm_tty_speed[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600,
- 1200, 1800, 2400, 4800, 9600, 19200, 38400,
- 57600, 115200, 230400, 460800, 500000, 576000,
- 921600, 1000000, 1152000, 1500000, 2000000,
- 2500000, 3000000, 3500000, 4000000
-};
+static int set_serial_info(struct acm *acm,
+ struct serial_struct __user *newinfo)
+{
+ struct serial_struct new_serial;
+ unsigned int closing_wait, close_delay;
+ int retval = 0;
-static const __u8 acm_tty_size[] = {
- 5, 6, 7, 8
-};
+ if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+ return -EFAULT;
+
+ close_delay = new_serial.close_delay * 10;
+ closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+ ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
+
+ mutex_lock(&acm->port.mutex);
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if ((close_delay != acm->port.close_delay) ||
+ (closing_wait != acm->port.closing_wait))
+ retval = -EPERM;
+ else
+ retval = -EOPNOTSUPP;
+ } else {
+ acm->port.close_delay = close_delay;
+ acm->port.closing_wait = closing_wait;
+ }
+
+ mutex_unlock(&acm->port.mutex);
+ return retval;
+}
+
+static int wait_serial_change(struct acm *acm, unsigned long arg)
+{
+ int rv = 0;
+ 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;
+ new = acm->iocount;
+ acm->oldcount = new;
+ spin_unlock_irq(&acm->read_lock);
+
+ if ((arg & TIOCM_DSR) &&
+ old.dsr != new.dsr)
+ break;
+ if ((arg & TIOCM_CD) &&
+ old.dcd != new.dcd)
+ break;
+ if ((arg & TIOCM_RI) &&
+ old.rng != new.rng)
+ break;
+
+ add_wait_queue(&acm->wioctl, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ remove_wait_queue(&acm->wioctl, &wait);
+ if (acm->disconnected) {
+ if (arg & TIOCM_CD)
+ break;
+ else
+ rv = -ENODEV;
+ } else {
+ if (signal_pending(current))
+ rv = -ERESTARTSYS;
+ }
+ } while (!rv);
+
+
+
+ return rv;
+}
+
+static int get_serial_usage(struct acm *acm,
+ struct serial_icounter_struct __user *count)
+{
+ 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;
+
+ if (copy_to_user(count, &icount, sizeof(icount)) > 0)
+ rv = -EFAULT;
+
+ return rv;
+}
+
+static int acm_tty_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ struct acm *acm = tty->driver_data;
+ int rv = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case TIOCGSERIAL: /* gets serial port data */
+ rv = get_serial_info(acm, (struct serial_struct __user *) arg);
+ break;
+ case TIOCSSERIAL:
+ rv = set_serial_info(acm, (struct serial_struct __user *) arg);
+ break;
+ case TIOCMIWAIT:
+ rv = usb_autopm_get_interface(acm->control);
+ if (rv < 0) {
+ rv = -EIO;
+ break;
+ }
+ 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;
+}
static void acm_tty_set_termios(struct tty_struct *tty,
struct ktermios *termios_old)
{
struct acm *acm = tty->driver_data;
- struct ktermios *termios = tty->termios;
+ struct ktermios *termios = &tty->termios;
struct usb_cdc_line_coding newline;
int newctrl = acm->ctrlout;
- if (!ACM_READY(acm))
- return;
-
newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty));
newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0;
newline.bParityType = termios->c_cflag & PARENB ?
(termios->c_cflag & PARODD ? 1 : 2) +
(termios->c_cflag & CMSPAR ? 2 : 0) : 0;
- newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ newline.bDataBits = 5;
+ break;
+ case CS6:
+ newline.bDataBits = 6;
+ break;
+ case CS7:
+ newline.bDataBits = 7;
+ break;
+ case CS8:
+ default:
+ newline.bDataBits = 8;
+ break;
+ }
/* FIXME: Needs to clear unsupported bits in the termios */
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
@@ -867,13 +991,22 @@ static void acm_tty_set_termios(struct tty_struct *tty,
if (memcmp(&acm->line, &newline, sizeof newline)) {
memcpy(&acm->line, &newline, sizeof newline);
- dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
+ dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
+ __func__,
+ le32_to_cpu(newline.dwDTERate),
newline.bCharFormat, newline.bParityType,
newline.bDataBits);
acm_set_line(acm, &acm->line);
}
}
+static const struct tty_port_operations acm_port_ops = {
+ .dtr_rts = acm_port_dtr_rts,
+ .shutdown = acm_port_shutdown,
+ .activate = acm_port_activate,
+ .destruct = acm_port_destruct,
+};
+
/*
* USB probe and disconnect routines.
*/
@@ -892,11 +1025,11 @@ static void acm_write_buffers_free(struct acm *acm)
static void acm_read_buffers_free(struct acm *acm)
{
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
- int i, n = acm->rx_buflimit;
+ int i;
- for (i = 0; i < n; i++)
+ for (i = 0; i < acm->rx_buflimit; i++)
usb_free_coherent(usb_dev, acm->readsize,
- acm->rb[i].base, acm->rb[i].dma);
+ acm->read_buffers[i].base, acm->read_buffers[i].dma);
}
/* Little helper: write buffers allocate */
@@ -941,14 +1074,20 @@ static int acm_probe(struct usb_interface *intf,
u8 ac_management_function = 0;
u8 call_management_function = 0;
int call_interface_num = -1;
- int data_interface_num;
+ int data_interface_num = -1;
unsigned long quirks;
int num_rx_buf;
int i;
int combined_interfaces = 0;
+ struct device *tty_dev;
+ int rv = -ENOMEM;
/* normal quirks */
quirks = (unsigned long)id->driver_info;
+
+ if (quirks == IGNORE_DEVICE)
+ return -ENODEV;
+
num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
/* handle quirks deadly to normal probing*/
@@ -1005,7 +1144,7 @@ static int acm_probe(struct usb_interface *intf,
case USB_CDC_CALL_MANAGEMENT_TYPE:
call_management_function = buffer[3];
call_interface_num = buffer[4];
- if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
+ if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
break;
default:
@@ -1025,7 +1164,11 @@ next_desc:
if (!union_header) {
if (call_interface_num > 0) {
dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
- data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
+ /* quirks for Droids MuIn LCD */
+ if (quirks & NO_DATA_INTERFACE)
+ data_interface = usb_ifnum_to_if(usb_dev, 0);
+ else
+ data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num));
control_interface = intf;
} else {
if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
@@ -1109,7 +1252,8 @@ skip_normal_probe:
}
- if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
+ if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+ control_interface->cur_altsetting->desc.bNumEndpoints == 0)
return -EINVAL;
epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
@@ -1128,25 +1272,26 @@ skip_normal_probe:
epwrite = t;
}
made_compressed_probe:
- dbg("interfaces are valid");
- for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
-
- if (minor == ACM_TTY_MINORS) {
- dev_err(&intf->dev, "no more free acm devices\n");
- return -ENODEV;
- }
+ dev_dbg(&intf->dev, "interfaces are valid\n");
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
if (acm == NULL) {
- dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
+ dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
goto alloc_fail;
}
- ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize);
- readsize = le16_to_cpu(epread->wMaxPacketSize) *
+ minor = acm_alloc_minor(acm);
+ if (minor == ACM_TTY_MINORS) {
+ dev_err(&intf->dev, "no more free acm devices\n");
+ kfree(acm);
+ return -ENODEV;
+ }
+
+ ctrlsize = usb_endpoint_maxp(epctrl);
+ readsize = usb_endpoint_maxp(epread) *
(quirks == SINGLE_RX_URB ? 1 : 2);
acm->combined_interfaces = combined_interfaces;
- acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20;
+ acm->writesize = usb_endpoint_maxp(epwrite) * 20;
acm->control = control_interface;
acm->data = data_interface;
acm->minor = minor;
@@ -1157,11 +1302,8 @@ made_compressed_probe:
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf;
- acm->urb_task.func = acm_rx_tasklet;
- acm->urb_task.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint);
- init_waitqueue_head(&acm->drain_wait);
- spin_lock_init(&acm->throttle_lock);
+ init_waitqueue_head(&acm->wioctl);
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
@@ -1171,61 +1313,78 @@ made_compressed_probe:
acm->bInterval = epread->bInterval;
tty_port_init(&acm->port);
acm->port.ops = &acm_port_ops;
+ init_usb_anchor(&acm->delayed);
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
- dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
+ dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
goto alloc_fail2;
}
acm->ctrl_buffer = buf;
if (acm_write_buffers_alloc(acm) < 0) {
- dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
+ dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
goto alloc_fail4;
}
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) {
- dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+ dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
goto alloc_fail5;
}
for (i = 0; i < num_rx_buf; i++) {
- struct acm_ru *rcv = &(acm->ru[i]);
+ struct acm_rb *rb = &(acm->read_buffers[i]);
+ struct urb *urb;
- rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (rcv->urb == NULL) {
- dev_dbg(&intf->dev,
- "out of memory (read urbs usb_alloc_urb)\n");
+ rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
+ &rb->dma);
+ if (!rb->base) {
+ dev_err(&intf->dev, "out of memory "
+ "(read bufs usb_alloc_coherent)\n");
goto alloc_fail6;
}
+ rb->index = i;
+ rb->instance = acm;
- rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- rcv->instance = acm;
- }
- for (i = 0; i < num_rx_buf; i++) {
- struct acm_rb *rb = &(acm->rb[i]);
-
- rb->base = usb_alloc_coherent(acm->dev, readsize,
- GFP_KERNEL, &rb->dma);
- if (!rb->base) {
- dev_dbg(&intf->dev,
- "out of memory (read bufs usb_alloc_coherent)\n");
- goto alloc_fail7;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&intf->dev,
+ "out of memory (read urbs usb_alloc_urb)\n");
+ goto alloc_fail6;
}
+ 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,
+ acm->rx_endpoint,
+ rb->base,
+ acm->readsize,
+ acm_read_bulk_callback, rb,
+ acm->bInterval);
+ } else {
+ usb_fill_bulk_urb(urb, acm->dev,
+ acm->rx_endpoint,
+ rb->base,
+ acm->readsize,
+ acm_read_bulk_callback, rb);
+ }
+
+ acm->read_urbs[i] = urb;
+ __set_bit(i, &acm->read_urbs_free);
}
for (i = 0; i < ACM_NW; i++) {
struct acm_wb *snd = &(acm->wb[i]);
snd->urb = usb_alloc_urb(0, GFP_KERNEL);
if (snd->urb == NULL) {
- dev_dbg(&intf->dev,
- "out of memory (write urbs usb_alloc_urb)");
- goto alloc_fail8;
+ dev_err(&intf->dev,
+ "out of memory (write urbs usb_alloc_urb)\n");
+ goto alloc_fail7;
}
if (usb_endpoint_xfer_int(epwrite))
usb_fill_int_urb(snd->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress),
+ usb_sndintpipe(usb_dev, epwrite->bEndpointAddress),
NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
else
usb_fill_bulk_urb(snd->urb, usb_dev,
@@ -1239,7 +1398,7 @@ made_compressed_probe:
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
if (i < 0)
- goto alloc_fail8;
+ goto alloc_fail7;
if (cfd) { /* export the country data */
acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
@@ -1253,6 +1412,8 @@ made_compressed_probe:
i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
if (i < 0) {
kfree(acm->country_codes);
+ acm->country_codes = NULL;
+ acm->country_code_size = 0;
goto skip_countries;
}
@@ -1261,6 +1422,8 @@ made_compressed_probe:
if (i < 0) {
device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
kfree(acm->country_codes);
+ acm->country_codes = NULL;
+ acm->country_code_size = 0;
goto skip_countries;
}
}
@@ -1270,14 +1433,12 @@ skip_countries:
usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
/* works around buggy devices */
- epctrl->bInterval ? epctrl->bInterval : 0xff);
+ epctrl->bInterval ? epctrl->bInterval : 16);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
acm->ctrlurb->transfer_dma = acm->ctrl_dma;
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
- acm_set_control(acm, acm->ctrlout);
-
acm->line.dwDTERate = cpu_to_le32(9600);
acm->line.bDataBits = 8;
acm_set_line(acm, &acm->line);
@@ -1286,44 +1447,53 @@ skip_countries:
usb_set_intfdata(data_interface, acm);
usb_get_intf(control_interface);
- tty_register_device(acm_tty_driver, minor, &control_interface->dev);
-
- acm_table[minor] = acm;
+ tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor,
+ &control_interface->dev);
+ if (IS_ERR(tty_dev)) {
+ rv = PTR_ERR(tty_dev);
+ goto alloc_fail8;
+ }
return 0;
alloc_fail8:
+ if (acm->country_codes) {
+ device_remove_file(&acm->control->dev,
+ &dev_attr_wCountryCodes);
+ device_remove_file(&acm->control->dev,
+ &dev_attr_iCountryCodeRelDate);
+ }
+ device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
+alloc_fail7:
+ usb_set_intfdata(intf, NULL);
for (i = 0; i < ACM_NW; i++)
usb_free_urb(acm->wb[i].urb);
-alloc_fail7:
- acm_read_buffers_free(acm);
alloc_fail6:
for (i = 0; i < num_rx_buf; i++)
- usb_free_urb(acm->ru[i].urb);
+ usb_free_urb(acm->read_urbs[i]);
+ acm_read_buffers_free(acm);
usb_free_urb(acm->ctrlurb);
alloc_fail5:
acm_write_buffers_free(acm);
alloc_fail4:
usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
alloc_fail2:
+ acm_release_minor(acm);
kfree(acm);
alloc_fail:
- return -ENOMEM;
+ return rv;
}
static void stop_data_traffic(struct acm *acm)
{
int i;
- dbg("Entering stop_data_traffic");
- tasklet_disable(&acm->urb_task);
+ dev_dbg(&acm->control->dev, "%s\n", __func__);
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->ru[i].urb);
-
- tasklet_enable(&acm->urb_task);
+ usb_kill_urb(acm->read_urbs[i]);
cancel_work_sync(&acm->work);
}
@@ -1333,46 +1503,52 @@ 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;
+
+ dev_dbg(&intf->dev, "%s\n", __func__);
/* sibling interface is already cleaning up */
if (!acm)
return;
- mutex_lock(&open_mutex);
+ mutex_lock(&acm->mutex);
+ acm->disconnected = true;
if (acm->country_codes) {
device_remove_file(&acm->control->dev,
&dev_attr_wCountryCodes);
device_remove_file(&acm->control->dev,
&dev_attr_iCountryCodeRelDate);
}
+ wake_up_all(&acm->wioctl);
device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
- acm->dev = NULL;
usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL);
+ mutex_unlock(&acm->mutex);
+
+ tty = tty_port_tty_get(&acm->port);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
stop_data_traffic(acm);
+ 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(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
acm_read_buffers_free(acm);
if (!acm->combined_interfaces)
usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control);
- if (acm->port.count == 0) {
- acm_tty_unregister(acm);
- mutex_unlock(&open_mutex);
- return;
- }
-
- mutex_unlock(&open_mutex);
- tty = tty_port_tty_get(&acm->port);
- if (tty) {
- tty_hangup(tty);
- tty_kref_put(tty);
- }
+ tty_port_put(&acm->port);
}
#ifdef CONFIG_PM
@@ -1381,66 +1557,44 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
struct acm *acm = usb_get_intfdata(intf);
int cnt;
- if (message.event & PM_EVENT_AUTO) {
- int b;
-
- spin_lock_irq(&acm->read_lock);
- spin_lock(&acm->write_lock);
- b = acm->processing + acm->transmitting;
- spin_unlock(&acm->write_lock);
- spin_unlock_irq(&acm->read_lock);
- if (b)
+ spin_lock_irq(&acm->write_lock);
+ if (PMSG_IS_AUTO(message)) {
+ if (acm->transmitting) {
+ spin_unlock_irq(&acm->write_lock);
return -EBUSY;
+ }
}
-
- spin_lock_irq(&acm->read_lock);
- spin_lock(&acm->write_lock);
cnt = acm->susp_count++;
- spin_unlock(&acm->write_lock);
- spin_unlock_irq(&acm->read_lock);
+ spin_unlock_irq(&acm->write_lock);
if (cnt)
return 0;
- /*
- we treat opened interfaces differently,
- we must guard against open
- */
- mutex_lock(&acm->mutex);
- if (acm->port.count)
- stop_data_traffic(acm);
+ stop_data_traffic(acm);
- mutex_unlock(&acm->mutex);
return 0;
}
static int acm_resume(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
- struct acm_wb *wb;
+ struct urb *urb;
int rv = 0;
- int cnt;
- spin_lock_irq(&acm->read_lock);
- acm->susp_count -= 1;
- cnt = acm->susp_count;
- spin_unlock_irq(&acm->read_lock);
+ spin_lock_irq(&acm->write_lock);
- if (cnt)
- return 0;
+ if (--acm->susp_count)
+ goto out;
- mutex_lock(&acm->mutex);
- if (acm->port.count) {
- rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
+ if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
+ rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
- spin_lock_irq(&acm->write_lock);
- if (acm->delayed_wb) {
- wb = acm->delayed_wb;
- acm->delayed_wb = NULL;
- spin_unlock_irq(&acm->write_lock);
- acm_start_wb(acm, wb);
- } else {
- spin_unlock_irq(&acm->write_lock);
+ for (;;) {
+ urb = usb_get_from_anchor(&acm->delayed);
+ if (!urb)
+ break;
+
+ acm_start_wb(acm, urb->context);
}
/*
@@ -1448,30 +1602,23 @@ static int acm_resume(struct usb_interface *intf)
* do the write path at all cost
*/
if (rv < 0)
- goto err_out;
+ goto out;
- tasklet_schedule(&acm->urb_task);
+ rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
}
+out:
+ spin_unlock_irq(&acm->write_lock);
-err_out:
- mutex_unlock(&acm->mutex);
return rv;
}
static int acm_reset_resume(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
- struct tty_struct *tty;
- mutex_lock(&acm->mutex);
- if (acm->port.count) {
- tty = tty_port_tty_get(&acm->port);
- if (tty) {
- tty_hangup(tty);
- tty_kref_put(tty);
- }
- }
- mutex_unlock(&acm->mutex);
+ if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
+ tty_port_tty_hangup(&acm->port, false);
+
return acm_resume(intf);
}
@@ -1493,6 +1640,8 @@ static int acm_reset_resume(struct usb_interface *intf)
static const struct usb_device_id acm_ids[] = {
/* quirky and broken devices */
+ { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
+ .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
@@ -1534,6 +1683,30 @@ static const struct usb_device_id acm_ids[] = {
},
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
+ /* Motorola H24 HSPA module: */
+ { USB_DEVICE(0x22b8, 0x2d91) }, /* modem */
+ { USB_DEVICE(0x22b8, 0x2d92), /* modem + diagnostics */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d93), /* modem + AT port */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d95), /* modem + AT port + diagnostics */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d96), /* modem + NMEA */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d97), /* modem + diagnostics + NMEA */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d99), /* modem + AT port + NMEA */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+ { USB_DEVICE(0x22b8, 0x2d9a), /* modem + AT port + diagnostics + NMEA */
+ .driver_info = NO_UNION_NORMAL, /* handle only modem interface */
+ },
+
{ USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
.driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
data interface instead of
@@ -1541,6 +1714,12 @@ static const struct usb_device_id acm_ids[] = {
Maybe we should define a new
quirk for this. */
},
+ { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */
+ .driver_info = NO_UNION_NORMAL,
+ },
+ { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */
+ .driver_info = NO_UNION_NORMAL,
+ },
{ USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
},
@@ -1607,8 +1786,14 @@ static const struct usb_device_id acm_ids[] = {
{ NOKIA_PCSUITE_ACM_INFO(0x0154), }, /* Nokia 5800 XpressMusic */
{ NOKIA_PCSUITE_ACM_INFO(0x04ce), }, /* Nokia E90 */
{ NOKIA_PCSUITE_ACM_INFO(0x01d4), }, /* Nokia E55 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0302), }, /* Nokia N8 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0335), }, /* Nokia E7 */
+ { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */
{ SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */
+ /* Support for Owen devices */
+ { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */
+
/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
/* Support Lego NXT using pbLua firmware */
@@ -1616,6 +1801,20 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = NOT_A_MODEM,
},
+ /* Support for Droids MuIn LCD */
+ { USB_DEVICE(0x04d8, 0x000b),
+ .driver_info = NO_DATA_INTERFACE,
+ },
+
+#if IS_ENABLED(CONFIG_INPUT_IMS_PCU)
+ { USB_DEVICE(0x04d8, 0x0082), /* Application mode */
+ .driver_info = IGNORE_DEVICE,
+ },
+ { USB_DEVICE(0x04d8, 0x0083), /* Bootloader mode */
+ .driver_info = IGNORE_DEVICE,
+ },
+#endif
+
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },
@@ -1652,6 +1851,7 @@ static struct usb_driver acm_driver = {
#ifdef CONFIG_PM
.supports_autosuspend = 1,
#endif
+ .disable_hub_initiated_lpm = 1,
};
/*
@@ -1659,8 +1859,10 @@ static struct usb_driver acm_driver = {
*/
static const struct tty_operations acm_ops = {
+ .install = acm_tty_install,
.open = acm_tty_open,
.close = acm_tty_close,
+ .cleanup = acm_tty_cleanup,
.hangup = acm_tty_hangup,
.write = acm_tty_write,
.write_room = acm_tty_write_room,
@@ -1684,7 +1886,6 @@ static int __init acm_init(void)
acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
if (!acm_tty_driver)
return -ENOMEM;
- acm_tty_driver->owner = THIS_MODULE,
acm_tty_driver->driver_name = "acm",
acm_tty_driver->name = "ttyACM",
acm_tty_driver->major = ACM_TTY_MAJOR,
@@ -1710,8 +1911,7 @@ static int __init acm_init(void)
return retval;
}
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
}
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 5eeb570b9a6..fc75651afe1 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -52,7 +52,7 @@
*/
/*
- * The only reason to have several buffers is to accomodate assumptions
+ * The only reason to have several buffers is to accommodate assumptions
* in line disciplines. They ask for empty space amount, receive our URB size,
* and proceed to issue several 1-character writes, assuming they will fit.
* The very first write takes a complete URB. Fortunately, this only happens
@@ -72,16 +72,10 @@ struct acm_wb {
};
struct acm_rb {
- struct list_head list;
int size;
unsigned char *base;
dma_addr_t dma;
-};
-
-struct acm_ru {
- struct list_head list;
- struct acm_rb *buffer;
- struct urb *urb;
+ int index;
struct acm *instance;
};
@@ -97,43 +91,44 @@ struct acm {
unsigned int country_code_size; /* size of this buffer */
unsigned int country_rel_date; /* release date of version */
struct acm_wb wb[ACM_NW];
- struct acm_ru ru[ACM_NR];
- struct acm_rb rb[ACM_NR];
+ unsigned long read_urbs_free;
+ struct urb *read_urbs[ACM_NR];
+ struct acm_rb read_buffers[ACM_NR];
int rx_buflimit;
int rx_endpoint;
spinlock_t read_lock;
- struct list_head spare_read_urbs;
- struct list_head spare_read_bufs;
- struct list_head filled_read_bufs;
int write_used; /* number of non-empty write buffers */
- int processing;
int transmitting;
spinlock_t write_lock;
struct mutex mutex;
+ bool disconnected;
struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
- wait_queue_head_t drain_wait; /* close processing */
- struct tasklet_struct urb_task; /* rx processing */
- spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
+ struct async_icount iocount; /* counters for control line changes */
+ struct async_icount oldcount; /* for comparison of counter */
+ wait_queue_head_t wioctl; /* for ioctl */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int minor; /* acm minor number */
- unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
unsigned int ctrl_caps; /* control capabilities from the class specific header */
unsigned int susp_count; /* number of suspended interfaces */
unsigned int combined_interfaces:1; /* control and data collapsed */
unsigned int is_int_ep:1; /* interrupt endpoints contrary to spec used */
+ unsigned int throttled:1; /* actually throttled */
+ unsigned int throttle_req:1; /* throttle requested */
u8 bInterval;
- struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
+ struct usb_anchor delayed; /* writes queued for a device about to be woken */
};
#define CDC_DATA_INTERFACE_TYPE 0x0a
/* constants describing various quirks and errors */
-#define NO_UNION_NORMAL 1
-#define SINGLE_RX_URB 2
-#define NO_CAP_LINE 4
-#define NOT_A_MODEM 8
+#define NO_UNION_NORMAL BIT(0)
+#define SINGLE_RX_URB BIT(1)
+#define NO_CAP_LINE BIT(2)
+#define NOT_A_MODEM BIT(3)
+#define NO_DATA_INTERFACE BIT(4)
+#define IGNORE_DEVICE BIT(5)
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 6ee4451bfe2..a051a7a2b1b 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -13,6 +13,7 @@
*/
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -23,6 +24,7 @@
#include <linux/usb/cdc.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
+#include <linux/usb/cdc-wdm.h>
/*
* Version Information
@@ -54,11 +56,17 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_POLL_RUNNING 6
#define WDM_RESPONDING 7
#define WDM_SUSPENDING 8
+#define WDM_RESETTING 9
+#define WDM_OVERFLOW 10
#define WDM_MAX 16
+/* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 decimal (0x100)" */
+#define WDM_DEFAULT_BUFSIZE 256
static DEFINE_MUTEX(wdm_mutex);
+static DEFINE_SPINLOCK(wdm_device_list_lock);
+static LIST_HEAD(wdm_device_list);
/* --- method tables --- */
@@ -80,7 +88,6 @@ struct wdm_device {
u16 bufsize;
u16 wMaxCommand;
u16 wMaxPacketSize;
- u16 bMaxPacketSize0;
__le16 inum;
int reslength;
int length;
@@ -88,15 +95,51 @@ struct wdm_device {
int count;
dma_addr_t shandle;
dma_addr_t ihandle;
- struct mutex lock;
+ struct mutex wlock;
+ struct mutex rlock;
wait_queue_head_t wait;
struct work_struct rxwork;
int werr;
int rerr;
+ int resp_count;
+
+ struct list_head device_list;
+ int (*manage_power)(struct usb_interface *, int);
};
static struct usb_driver wdm_driver;
+/* return intfdata if we own the interface, else look up intf in the list */
+static struct wdm_device *wdm_find_device(struct usb_interface *intf)
+{
+ struct wdm_device *desc;
+
+ spin_lock(&wdm_device_list_lock);
+ list_for_each_entry(desc, &wdm_device_list, device_list)
+ if (desc->intf == intf)
+ goto found;
+ desc = NULL;
+found:
+ spin_unlock(&wdm_device_list_lock);
+
+ return desc;
+}
+
+static struct wdm_device *wdm_find_device_by_minor(int minor)
+{
+ struct wdm_device *desc;
+
+ spin_lock(&wdm_device_list_lock);
+ list_for_each_entry(desc, &wdm_device_list, device_list)
+ if (desc->intf->minor == minor)
+ goto found;
+ desc = NULL;
+found:
+ spin_unlock(&wdm_device_list_lock);
+
+ return desc;
+}
+
/* --- callbacks --- */
static void wdm_out_callback(struct urb *urb)
{
@@ -105,8 +148,9 @@ static void wdm_out_callback(struct urb *urb)
spin_lock(&desc->iuspin);
desc->werr = urb->status;
spin_unlock(&desc->iuspin);
- clear_bit(WDM_IN_USE, &desc->flags);
kfree(desc->outbuf);
+ desc->outbuf = NULL;
+ clear_bit(WDM_IN_USE, &desc->flags);
wake_up(&desc->wait);
}
@@ -114,6 +158,7 @@ static void wdm_in_callback(struct urb *urb)
{
struct wdm_device *desc = urb->context;
int status = urb->status;
+ int length = urb->actual_length;
spin_lock(&desc->iuspin);
clear_bit(WDM_RESPONDING, &desc->flags);
@@ -144,9 +189,17 @@ static void wdm_in_callback(struct urb *urb)
}
desc->rerr = status;
- desc->reslength = urb->actual_length;
- memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
- desc->length += desc->reslength;
+ if (length + desc->length > desc->wMaxCommand) {
+ /* The buffer would overflow */
+ set_bit(WDM_OVERFLOW, &desc->flags);
+ } else {
+ /* we may already be in overflow */
+ if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
+ memmove(desc->ubuf + desc->length, desc->inbuf, length);
+ desc->length += length;
+ desc->reslength = length;
+ }
+ }
skip_error:
wake_up(&desc->wait);
@@ -157,13 +210,12 @@ skip_error:
static void wdm_int_callback(struct urb *urb)
{
int rv = 0;
+ int responding;
int status = urb->status;
struct wdm_device *desc;
- struct usb_ctrlrequest *req;
struct usb_cdc_notification *dr;
desc = urb->context;
- req = desc->irq;
dr = (struct usb_cdc_notification *)desc->sbuf;
if (status) {
@@ -202,6 +254,10 @@ static void wdm_int_callback(struct urb *urb)
"NOTIFY_NETWORK_CONNECTION %s network",
dr->wValue ? "connected to" : "disconnected from");
goto exit;
+ case USB_CDC_NOTIFY_SPEED_CHANGE:
+ dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)",
+ urb->actual_length);
+ goto exit;
default:
clear_bit(WDM_POLL_RUNNING, &desc->flags);
dev_err(&desc->intf->dev,
@@ -210,28 +266,10 @@ static void wdm_int_callback(struct urb *urb)
goto exit;
}
- req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
- req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
- req->wValue = 0;
- req->wIndex = desc->inum;
- req->wLength = cpu_to_le16(desc->wMaxCommand);
-
- usb_fill_control_urb(
- desc->response,
- interface_to_usbdev(desc->intf),
- /* using common endpoint 0 */
- usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
- (unsigned char *)req,
- desc->inbuf,
- desc->wMaxCommand,
- wdm_in_callback,
- desc
- );
- desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
spin_lock(&desc->iuspin);
- clear_bit(WDM_READ, &desc->flags);
- set_bit(WDM_RESPONDING, &desc->flags);
- if (!test_bit(WDM_DISCONNECTING, &desc->flags)
+ responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
+ if (!desc->resp_count++ && !responding
+ && !test_bit(WDM_DISCONNECTING, &desc->flags)
&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@@ -276,14 +314,8 @@ static void free_urbs(struct wdm_device *desc)
static void cleanup(struct wdm_device *desc)
{
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->wMaxPacketSize,
- desc->sbuf,
- desc->validity->transfer_dma);
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->wMaxCommand,
- desc->inbuf,
- desc->response->transfer_dma);
+ kfree(desc->sbuf);
+ kfree(desc->inbuf);
kfree(desc->orq);
kfree(desc->irq);
kfree(desc->ubuf);
@@ -309,7 +341,7 @@ static ssize_t wdm_write
if (we < 0)
return -EIO;
- desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
+ buf = kmalloc(count, GFP_KERNEL);
if (!buf) {
rv = -ENOMEM;
goto outnl;
@@ -323,7 +355,7 @@ static ssize_t wdm_write
}
/* concurrent writes and disconnect */
- r = mutex_lock_interruptible(&desc->lock);
+ r = mutex_lock_interruptible(&desc->wlock);
rv = -ERESTARTSYS;
if (r) {
kfree(buf);
@@ -339,17 +371,23 @@ static ssize_t wdm_write
r = usb_autopm_get_interface(desc->intf);
if (r < 0) {
kfree(buf);
+ rv = usb_translate_errors(r);
goto outnp;
}
- if (!file->f_flags && O_NONBLOCK)
+ if (!(file->f_flags & O_NONBLOCK))
r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
&desc->flags));
else
if (test_bit(WDM_IN_USE, &desc->flags))
r = -EAGAIN;
+
+ if (test_bit(WDM_RESETTING, &desc->flags))
+ r = -EIO;
+
if (r < 0) {
kfree(buf);
+ rv = r;
goto out;
}
@@ -373,12 +411,15 @@ static ssize_t wdm_write
req->wIndex = desc->inum;
req->wLength = cpu_to_le16(count);
set_bit(WDM_IN_USE, &desc->flags);
+ desc->outbuf = buf;
rv = usb_submit_urb(desc->command, GFP_KERNEL);
if (rv < 0) {
kfree(buf);
+ desc->outbuf = NULL;
clear_bit(WDM_IN_USE, &desc->flags);
dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);
+ rv = usb_translate_errors(rv);
} else {
dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
req->wIndex);
@@ -386,30 +427,68 @@ static ssize_t wdm_write
out:
usb_autopm_put_interface(desc->intf);
outnp:
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
outnl:
return rv < 0 ? rv : count;
}
+/*
+ * clear WDM_READ flag and possibly submit the read urb if resp_count
+ * is non-zero.
+ *
+ * Called with desc->iuspin locked
+ */
+static int clear_wdm_read_flag(struct wdm_device *desc)
+{
+ int rv = 0;
+
+ clear_bit(WDM_READ, &desc->flags);
+
+ /* submit read urb only if the device is waiting for it */
+ if (!desc->resp_count || !--desc->resp_count)
+ goto out;
+
+ set_bit(WDM_RESPONDING, &desc->flags);
+ spin_unlock_irq(&desc->iuspin);
+ rv = usb_submit_urb(desc->response, GFP_KERNEL);
+ spin_lock_irq(&desc->iuspin);
+ if (rv) {
+ dev_err(&desc->intf->dev,
+ "usb_submit_urb failed with result %d\n", rv);
+
+ /* make sure the next notification trigger a submit */
+ clear_bit(WDM_RESPONDING, &desc->flags);
+ desc->resp_count = 0;
+ }
+out:
+ return rv;
+}
+
static ssize_t wdm_read
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
- int rv, cntr = 0;
+ int rv, cntr;
int i = 0;
struct wdm_device *desc = file->private_data;
- rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
+ rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
if (rv < 0)
return -ERESTARTSYS;
- if (desc->length == 0) {
+ cntr = ACCESS_ONCE(desc->length);
+ if (cntr == 0) {
desc->read = 0;
retry:
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
rv = -ENODEV;
goto err;
}
+ if (test_bit(WDM_OVERFLOW, &desc->flags)) {
+ clear_bit(WDM_OVERFLOW, &desc->flags);
+ rv = -ENOBUFS;
+ goto err;
+ }
i++;
if (file->f_flags & O_NONBLOCK) {
if (!test_bit(WDM_READ, &desc->flags)) {
@@ -427,6 +506,10 @@ retry:
rv = -ENODEV;
goto err;
}
+ if (test_bit(WDM_RESETTING, &desc->flags)) {
+ rv = -EIO;
+ goto err;
+ }
usb_mark_last_busy(interface_to_usbdev(desc->intf));
if (rv < 0) {
rv = -ERESTARTSYS;
@@ -449,32 +532,41 @@ retry:
spin_unlock_irq(&desc->iuspin);
goto retry;
}
+
if (!desc->reslength) { /* zero length read */
+ dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
+ rv = clear_wdm_read_flag(desc);
spin_unlock_irq(&desc->iuspin);
+ if (rv < 0)
+ goto err;
goto retry;
}
- clear_bit(WDM_READ, &desc->flags);
+ cntr = desc->length;
spin_unlock_irq(&desc->iuspin);
}
- cntr = count > desc->length ? desc->length : count;
+ if (cntr > count)
+ cntr = count;
rv = copy_to_user(buffer, desc->ubuf, cntr);
if (rv > 0) {
rv = -EFAULT;
goto err;
}
+ spin_lock_irq(&desc->iuspin);
+
for (i = 0; i < desc->length - cntr; i++)
desc->ubuf[i] = desc->ubuf[i + cntr];
desc->length -= cntr;
/* in case we had outstanding data */
if (!desc->length)
- clear_bit(WDM_READ, &desc->flags);
+ clear_wdm_read_flag(desc);
+ spin_unlock_irq(&desc->iuspin);
rv = cntr;
err:
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->rlock);
return rv;
}
@@ -483,11 +575,13 @@ static int wdm_flush(struct file *file, fl_owner_t id)
struct wdm_device *desc = file->private_data;
wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
- if (desc->werr < 0)
+
+ /* cannot dereference desc->intf if WDM_DISCONNECTING */
+ if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags))
dev_err(&desc->intf->dev, "Error in flush path: %d\n",
desc->werr);
- return desc->werr;
+ return usb_translate_errors(desc->werr);
}
static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
@@ -498,7 +592,7 @@ static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
spin_lock_irqsave(&desc->iuspin, flags);
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
- mask = POLLERR;
+ mask = POLLHUP | POLLERR;
spin_unlock_irqrestore(&desc->iuspin, flags);
goto desc_out;
}
@@ -524,11 +618,11 @@ static int wdm_open(struct inode *inode, struct file *file)
struct wdm_device *desc;
mutex_lock(&wdm_mutex);
- intf = usb_find_interface(&wdm_driver, minor);
- if (!intf)
+ desc = wdm_find_device_by_minor(minor);
+ if (!desc)
goto out;
- desc = usb_get_intfdata(intf);
+ intf = desc->intf;
if (test_bit(WDM_DISCONNECTING, &desc->flags))
goto out;
file->private_data = desc;
@@ -538,20 +632,25 @@ static int wdm_open(struct inode *inode, struct file *file)
dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
goto out;
}
- intf->needs_remote_wakeup = 1;
- mutex_lock(&desc->lock);
+ /* using write lock to protect desc->count */
+ mutex_lock(&desc->wlock);
if (!desc->count++) {
+ desc->werr = 0;
+ desc->rerr = 0;
rv = usb_submit_urb(desc->validity, GFP_KERNEL);
if (rv < 0) {
desc->count--;
dev_err(&desc->intf->dev,
"Error submitting int urb - %d\n", rv);
+ rv = usb_translate_errors(rv);
}
} else {
rv = 0;
}
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
+ if (desc->count == 1)
+ desc->manage_power(intf, 1);
usb_autopm_put_interface(desc->intf);
out:
mutex_unlock(&wdm_mutex);
@@ -563,20 +662,46 @@ static int wdm_release(struct inode *inode, struct file *file)
struct wdm_device *desc = file->private_data;
mutex_lock(&wdm_mutex);
- mutex_lock(&desc->lock);
+
+ /* using write lock to protect desc->count */
+ mutex_lock(&desc->wlock);
desc->count--;
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
if (!desc->count) {
- dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
- kill_urbs(desc);
- if (!test_bit(WDM_DISCONNECTING, &desc->flags))
- desc->intf->needs_remote_wakeup = 0;
+ if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
+ dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
+ kill_urbs(desc);
+ spin_lock_irq(&desc->iuspin);
+ desc->resp_count = 0;
+ spin_unlock_irq(&desc->iuspin);
+ desc->manage_power(desc->intf, 0);
+ } else {
+ /* must avoid dev_printk here as desc->intf is invalid */
+ pr_debug(KBUILD_MODNAME " %s: device gone - cleaning up\n", __func__);
+ cleanup(desc);
+ }
}
mutex_unlock(&wdm_mutex);
return 0;
}
+static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct wdm_device *desc = file->private_data;
+ int rv = 0;
+
+ switch (cmd) {
+ case IOCTL_WDM_MAX_COMMAND:
+ if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand)))
+ rv = -EFAULT;
+ break;
+ default:
+ rv = -ENOTTY;
+ }
+ return rv;
+}
+
static const struct file_operations wdm_fops = {
.owner = THIS_MODULE,
.read = wdm_read,
@@ -585,6 +710,8 @@ static const struct file_operations wdm_fops = {
.flush = wdm_flush,
.release = wdm_release,
.poll = wdm_poll,
+ .unlocked_ioctl = wdm_ioctl,
+ .compat_ioctl = wdm_ioctl,
.llseek = noop_llseek,
};
@@ -599,16 +726,20 @@ static void wdm_rxwork(struct work_struct *work)
{
struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
unsigned long flags;
- int rv;
+ int rv = 0;
+ int responding;
spin_lock_irqsave(&desc->iuspin, flags);
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
spin_unlock_irqrestore(&desc->iuspin, flags);
} else {
+ responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
- rv = usb_submit_urb(desc->response, GFP_KERNEL);
+ if (!responding)
+ rv = usb_submit_urb(desc->response, GFP_KERNEL);
if (rv < 0 && rv != -EPERM) {
spin_lock_irqsave(&desc->iuspin, flags);
+ clear_bit(WDM_RESPONDING, &desc->flags);
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
schedule_work(&desc->rxwork);
spin_unlock_irqrestore(&desc->iuspin, flags);
@@ -618,70 +749,31 @@ static void wdm_rxwork(struct work_struct *work)
/* --- hotplug --- */
-static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
+static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
+ u16 bufsize, int (*manage_power)(struct usb_interface *, int))
{
- int rv = -EINVAL;
- struct usb_device *udev = interface_to_usbdev(intf);
+ int rv = -ENOMEM;
struct wdm_device *desc;
- struct usb_host_interface *iface;
- struct usb_endpoint_descriptor *ep;
- struct usb_cdc_dmm_desc *dmhd;
- u8 *buffer = intf->altsetting->extra;
- int buflen = intf->altsetting->extralen;
- u16 maxcom = 0;
-
- if (!buffer)
- goto out;
-
- while (buflen > 2) {
- if (buffer [1] != USB_DT_CS_INTERFACE) {
- dev_err(&intf->dev, "skipping garbage\n");
- goto next_desc;
- }
- switch (buffer [2]) {
- case USB_CDC_HEADER_TYPE:
- break;
- case USB_CDC_DMM_TYPE:
- dmhd = (struct usb_cdc_dmm_desc *)buffer;
- maxcom = le16_to_cpu(dmhd->wMaxCommand);
- dev_dbg(&intf->dev,
- "Finding maximum buffer length: %d", maxcom);
- break;
- default:
- dev_err(&intf->dev,
- "Ignoring extra header, type %d, length %d\n",
- buffer[2], buffer[0]);
- break;
- }
-next_desc:
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- rv = -ENOMEM;
desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
if (!desc)
goto out;
- mutex_init(&desc->lock);
+ INIT_LIST_HEAD(&desc->device_list);
+ mutex_init(&desc->rlock);
+ mutex_init(&desc->wlock);
spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait);
- desc->wMaxCommand = maxcom;
+ desc->wMaxCommand = bufsize;
/* this will be expanded and needed in hardware endianness */
desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
desc->intf = intf;
INIT_WORK(&desc->rxwork, wdm_rxwork);
rv = -EINVAL;
- iface = intf->cur_altsetting;
- if (iface->desc.bNumEndpoints != 1)
- goto err;
- ep = &iface->endpoint[0].desc;
- if (!ep || !usb_endpoint_is_int_in(ep))
+ if (!usb_endpoint_is_int_in(ep))
goto err;
- desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
- desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
+ desc->wMaxPacketSize = usb_endpoint_maxp(ep);
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
if (!desc->orq)
@@ -706,19 +798,13 @@ next_desc:
if (!desc->ubuf)
goto err;
- desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
- desc->wMaxPacketSize,
- GFP_KERNEL,
- &desc->validity->transfer_dma);
+ desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
if (!desc->sbuf)
goto err;
- desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
- desc->bMaxPacketSize0,
- GFP_KERNEL,
- &desc->response->transfer_dma);
+ desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
if (!desc->inbuf)
- goto err2;
+ goto err;
usb_fill_int_urb(
desc->validity,
@@ -730,45 +816,150 @@ next_desc:
desc,
ep->bInterval
);
- desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- usb_set_intfdata(intf, desc);
+ desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+ desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+ desc->irq->wValue = 0;
+ desc->irq->wIndex = desc->inum;
+ desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
+
+ usb_fill_control_urb(
+ desc->response,
+ interface_to_usbdev(intf),
+ /* using common endpoint 0 */
+ usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
+ (unsigned char *)desc->irq,
+ desc->inbuf,
+ desc->wMaxCommand,
+ wdm_in_callback,
+ desc
+ );
+
+ desc->manage_power = manage_power;
+
+ spin_lock(&wdm_device_list_lock);
+ list_add(&desc->device_list, &wdm_device_list);
+ spin_unlock(&wdm_device_list_lock);
+
rv = usb_register_dev(intf, &wdm_class);
if (rv < 0)
- goto err3;
+ goto err;
else
- dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
- intf->minor - WDM_MINOR_BASE);
+ dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
out:
return rv;
-err3:
- usb_set_intfdata(intf, NULL);
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->bMaxPacketSize0,
- desc->inbuf,
- desc->response->transfer_dma);
-err2:
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->wMaxPacketSize,
- desc->sbuf,
- desc->validity->transfer_dma);
err:
- free_urbs(desc);
- kfree(desc->ubuf);
- kfree(desc->orq);
- kfree(desc->irq);
- kfree(desc);
+ spin_lock(&wdm_device_list_lock);
+ list_del(&desc->device_list);
+ spin_unlock(&wdm_device_list_lock);
+ cleanup(desc);
return rv;
}
+static int wdm_manage_power(struct usb_interface *intf, int on)
+{
+ /* need autopm_get/put here to ensure the usbcore sees the new value */
+ int rv = usb_autopm_get_interface(intf);
+
+ intf->needs_remote_wakeup = on;
+ if (!rv)
+ usb_autopm_put_interface(intf);
+ return 0;
+}
+
+static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int rv = -EINVAL;
+ struct usb_host_interface *iface;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_cdc_dmm_desc *dmhd;
+ u8 *buffer = intf->altsetting->extra;
+ int buflen = intf->altsetting->extralen;
+ u16 maxcom = WDM_DEFAULT_BUFSIZE;
+
+ if (!buffer)
+ goto err;
+ while (buflen > 2) {
+ if (buffer[1] != USB_DT_CS_INTERFACE) {
+ dev_err(&intf->dev, "skipping garbage\n");
+ goto next_desc;
+ }
+
+ switch (buffer[2]) {
+ case USB_CDC_HEADER_TYPE:
+ break;
+ case USB_CDC_DMM_TYPE:
+ dmhd = (struct usb_cdc_dmm_desc *)buffer;
+ maxcom = le16_to_cpu(dmhd->wMaxCommand);
+ dev_dbg(&intf->dev,
+ "Finding maximum buffer length: %d", maxcom);
+ break;
+ default:
+ dev_err(&intf->dev,
+ "Ignoring extra header, type %d, length %d\n",
+ buffer[2], buffer[0]);
+ break;
+ }
+next_desc:
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ iface = intf->cur_altsetting;
+ if (iface->desc.bNumEndpoints != 1)
+ goto err;
+ ep = &iface->endpoint[0].desc;
+
+ rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
+
+err:
+ return rv;
+}
+
+/**
+ * usb_cdc_wdm_register - register a WDM subdriver
+ * @intf: usb interface the subdriver will associate with
+ * @ep: interrupt endpoint to monitor for notifications
+ * @bufsize: maximum message size to support for read/write
+ *
+ * Create WDM usb class character device and associate it with intf
+ * without binding, allowing another driver to manage the interface.
+ *
+ * The subdriver will manage the given interrupt endpoint exclusively
+ * and will issue control requests referring to the given intf. It
+ * will otherwise avoid interferring, and in particular not do
+ * usb_set_intfdata/usb_get_intfdata on intf.
+ *
+ * The return value is a pointer to the subdriver's struct usb_driver.
+ * The registering driver is responsible for calling this subdriver's
+ * disconnect, suspend, resume, pre_reset and post_reset methods from
+ * its own.
+ */
+struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
+ struct usb_endpoint_descriptor *ep,
+ int bufsize,
+ int (*manage_power)(struct usb_interface *, int))
+{
+ int rv = -EINVAL;
+
+ rv = wdm_create(intf, ep, bufsize, manage_power);
+ if (rv < 0)
+ goto err;
+
+ return &wdm_driver;
+err:
+ return ERR_PTR(rv);
+}
+EXPORT_SYMBOL(usb_cdc_wdm_register);
+
static void wdm_disconnect(struct usb_interface *intf)
{
struct wdm_device *desc;
unsigned long flags;
usb_deregister_dev(intf, &wdm_class);
+ desc = wdm_find_device(intf);
mutex_lock(&wdm_mutex);
- desc = usb_get_intfdata(intf);
/* the spinlock makes sure no new urbs are generated in the callbacks */
spin_lock_irqsave(&desc->iuspin, flags);
@@ -777,30 +968,42 @@ static void wdm_disconnect(struct usb_interface *intf)
/* to terminate pending flushes */
clear_bit(WDM_IN_USE, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
- mutex_lock(&desc->lock);
+ wake_up_all(&desc->wait);
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
- mutex_unlock(&desc->lock);
- wake_up_all(&desc->wait);
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
+
+ /* the desc->intf pointer used as list key is now invalid */
+ spin_lock(&wdm_device_list_lock);
+ list_del(&desc->device_list);
+ spin_unlock(&wdm_device_list_lock);
+
if (!desc->count)
cleanup(desc);
+ else
+ dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count);
mutex_unlock(&wdm_mutex);
}
#ifdef CONFIG_PM
static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{
- struct wdm_device *desc = usb_get_intfdata(intf);
+ struct wdm_device *desc = wdm_find_device(intf);
int rv = 0;
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
/* if this is an autosuspend the caller does the locking */
- if (!(message.event & PM_EVENT_AUTO))
- mutex_lock(&desc->lock);
+ if (!PMSG_IS_AUTO(message)) {
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
+ }
spin_lock_irq(&desc->iuspin);
- if ((message.event & PM_EVENT_AUTO) &&
+ if (PMSG_IS_AUTO(message) &&
(test_bit(WDM_IN_USE, &desc->flags)
|| test_bit(WDM_RESPONDING, &desc->flags))) {
spin_unlock_irq(&desc->iuspin);
@@ -813,8 +1016,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
}
- if (!(message.event & PM_EVENT_AUTO))
- mutex_unlock(&desc->lock);
+ if (!PMSG_IS_AUTO(message)) {
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
+ }
return rv;
}
@@ -836,7 +1041,7 @@ static int recover_from_urb_loss(struct wdm_device *desc)
#ifdef CONFIG_PM
static int wdm_resume(struct usb_interface *intf)
{
- struct wdm_device *desc = usb_get_intfdata(intf);
+ struct wdm_device *desc = wdm_find_device(intf);
int rv;
dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
@@ -850,19 +1055,38 @@ static int wdm_resume(struct usb_interface *intf)
static int wdm_pre_reset(struct usb_interface *intf)
{
- struct wdm_device *desc = usb_get_intfdata(intf);
-
- mutex_lock(&desc->lock);
+ struct wdm_device *desc = wdm_find_device(intf);
+
+ /*
+ * we notify everybody using poll of
+ * an exceptional situation
+ * must be done before recovery lest a spontaneous
+ * message from the device is lost
+ */
+ spin_lock_irq(&desc->iuspin);
+ set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */
+ set_bit(WDM_READ, &desc->flags); /* unblock read */
+ clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */
+ desc->rerr = -EINTR;
+ spin_unlock_irq(&desc->iuspin);
+ wake_up_all(&desc->wait);
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
+ kill_urbs(desc);
+ cancel_work_sync(&desc->rxwork);
return 0;
}
static int wdm_post_reset(struct usb_interface *intf)
{
- struct wdm_device *desc = usb_get_intfdata(intf);
+ struct wdm_device *desc = wdm_find_device(intf);
int rv;
+ clear_bit(WDM_OVERFLOW, &desc->flags);
+ clear_bit(WDM_RESETTING, &desc->flags);
rv = recover_from_urb_loss(desc);
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
return 0;
}
@@ -879,26 +1103,10 @@ static struct usb_driver wdm_driver = {
.post_reset = wdm_post_reset,
.id_table = wdm_ids,
.supports_autosuspend = 1,
+ .disable_hub_initiated_lpm = 1,
};
-/* --- low level module stuff --- */
-
-static int __init wdm_init(void)
-{
- int rv;
-
- rv = usb_register(&wdm_driver);
-
- return rv;
-}
-
-static void __exit wdm_exit(void)
-{
- usb_deregister(&wdm_driver);
-}
-
-module_init(wdm_init);
-module_exit(wdm_exit);
+module_usb_driver(wdm_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 9eca4053312..0924ee40a96 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -52,12 +52,12 @@
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/poll.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/lp.h>
#include <linux/mutex.h>
#undef DEBUG
#include <linux/usb.h>
+#include <linux/ratelimit.h>
/*
* Version Information
@@ -171,27 +171,31 @@ struct usblp {
#ifdef DEBUG
static void usblp_dump(struct usblp *usblp)
{
+ struct device *dev = &usblp->intf->dev;
int p;
- dbg("usblp=0x%p", usblp);
- dbg("dev=0x%p", usblp->dev);
- dbg("present=%d", usblp->present);
- dbg("readbuf=0x%p", usblp->readbuf);
- dbg("readcount=%d", usblp->readcount);
- dbg("ifnum=%d", usblp->ifnum);
- for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
- dbg("protocol[%d].alt_setting=%d", p, usblp->protocol[p].alt_setting);
- dbg("protocol[%d].epwrite=%p", p, usblp->protocol[p].epwrite);
- dbg("protocol[%d].epread=%p", p, usblp->protocol[p].epread);
- }
- dbg("current_protocol=%d", usblp->current_protocol);
- dbg("minor=%d", usblp->minor);
- dbg("wstatus=%d", usblp->wstatus);
- dbg("rstatus=%d", usblp->rstatus);
- dbg("quirks=%d", usblp->quirks);
- dbg("used=%d", usblp->used);
- dbg("bidir=%d", usblp->bidir);
- dbg("device_id_string=\"%s\"",
+ dev_dbg(dev, "usblp=0x%p\n", usblp);
+ dev_dbg(dev, "dev=0x%p\n", usblp->dev);
+ dev_dbg(dev, "present=%d\n", usblp->present);
+ dev_dbg(dev, "readbuf=0x%p\n", usblp->readbuf);
+ dev_dbg(dev, "readcount=%d\n", usblp->readcount);
+ dev_dbg(dev, "ifnum=%d\n", usblp->ifnum);
+ for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
+ dev_dbg(dev, "protocol[%d].alt_setting=%d\n", p,
+ usblp->protocol[p].alt_setting);
+ dev_dbg(dev, "protocol[%d].epwrite=%p\n", p,
+ usblp->protocol[p].epwrite);
+ dev_dbg(dev, "protocol[%d].epread=%p\n", p,
+ usblp->protocol[p].epread);
+ }
+ dev_dbg(dev, "current_protocol=%d\n", usblp->current_protocol);
+ dev_dbg(dev, "minor=%d\n", usblp->minor);
+ dev_dbg(dev, "wstatus=%d\n", usblp->wstatus);
+ dev_dbg(dev, "rstatus=%d\n", usblp->rstatus);
+ dev_dbg(dev, "quirks=%d\n", usblp->quirks);
+ dev_dbg(dev, "used=%d\n", usblp->used);
+ dev_dbg(dev, "bidir=%d\n", usblp->bidir);
+ dev_dbg(dev, "device_id_string=\"%s\"\n",
usblp->device_id_string ?
usblp->device_id_string + 2 :
(unsigned char *)"(null)");
@@ -261,7 +265,8 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
retval = usb_control_msg(usblp->dev,
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT);
- dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d",
+ dev_dbg(&usblp->intf->dev,
+ "usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d\n",
request, !!dir, recip, value, index, len, retval);
return retval < 0 ? retval : 0;
}
@@ -348,8 +353,7 @@ static int usblp_check_status(struct usblp *usblp, int err)
mutex_lock(&usblp->mut);
if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
mutex_unlock(&usblp->mut);
- if (printk_ratelimit())
- printk(KERN_ERR
+ printk_ratelimited(KERN_ERR
"usblp%d: error %d reading printer status\n",
usblp->minor, error);
return 0;
@@ -500,8 +504,9 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto done;
}
- dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
- _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
+ dev_dbg(&usblp->intf->dev,
+ "usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)\n", cmd,
+ _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
@@ -594,7 +599,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto done;
}
- dbg("usblp%d requested/got HP channel %ld/%d",
+ dev_dbg(&usblp->intf->dev,
+ "usblp%d requested/got HP channel %ld/%d\n",
usblp->minor, arg, newChannel);
break;
@@ -614,7 +620,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto done;
}
- dbg("usblp%d is bus=%d, device=%d",
+ dev_dbg(&usblp->intf->dev,
+ "usblp%d is bus=%d, device=%d\n",
usblp->minor, twoints[0], twoints[1]);
break;
@@ -634,7 +641,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto done;
}
- dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
+ dev_dbg(&usblp->intf->dev,
+ "usblp%d is VID=0x%4.4X, PID=0x%4.4X\n",
usblp->minor, twoints[0], twoints[1]);
break;
@@ -653,8 +661,7 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case LPGETSTATUS:
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
- if (printk_ratelimit())
- printk(KERN_ERR "usblp%d:"
+ printk_ratelimited(KERN_ERR "usblp%d:"
"failed reading printer status (%d)\n",
usblp->minor, retval);
retval = -EIO;
@@ -988,7 +995,7 @@ static int usblp_submit_read(struct usblp *usblp)
usblp->rcomplete = 0;
spin_unlock_irqrestore(&usblp->lock, flags);
if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
- dbg("error submitting urb (%d)", rc);
+ dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc);
spin_lock_irqsave(&usblp->lock, flags);
usblp->rstatus = rc;
usblp->rcomplete = 1;
@@ -1046,7 +1053,7 @@ static const struct file_operations usblp_fops = {
.llseek = noop_llseek,
};
-static char *usblp_devnode(struct device *dev, mode_t *mode)
+static char *usblp_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
}
@@ -1130,7 +1137,8 @@ static int usblp_probe(struct usb_interface *intf,
/* Analyze and pick initial alternate settings and endpoints. */
protocol = usblp_select_alts(usblp);
if (protocol < 0) {
- dbg("incompatible printer-class device 0x%4.4X/0x%4.4X",
+ dev_dbg(&intf->dev,
+ "incompatible printer-class device 0x%4.4X/0x%4.4X\n",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
retval = -ENODEV;
@@ -1159,14 +1167,14 @@ static int usblp_probe(struct usb_interface *intf,
retval = usb_register_dev(intf, &usblp_class);
if (retval) {
- printk(KERN_ERR "usblp: Not able to get a minor"
- " (base %u, slice default): %d\n",
- USBLP_MINOR_BASE, retval);
+ dev_err(&intf->dev,
+ "usblp: Not able to get a minor (base %u, slice default): %d\n",
+ USBLP_MINOR_BASE, retval);
goto abort_intfdata;
}
usblp->minor = intf->minor;
- printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d "
- "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
+ dev_info(&intf->dev,
+ "usblp%d: USB %sdirectional printer dev %d if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
usblp->ifnum,
usblp->protocol[usblp->current_protocol].alt_setting,
@@ -1303,7 +1311,8 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol)
usblp->bidir = (usblp->protocol[protocol].epread != NULL);
usblp->current_protocol = protocol;
- dbg("usblp%d set protocol %d", usblp->minor, protocol);
+ dev_dbg(&usblp->intf->dev, "usblp%d set protocol %d\n",
+ usblp->minor, protocol);
return 0;
}
@@ -1316,7 +1325,8 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
if (err < 0) {
- dbg("usblp%d: error = %d reading IEEE-1284 Device ID string",
+ dev_dbg(&usblp->intf->dev,
+ "usblp%d: error = %d reading IEEE-1284 Device ID string\n",
usblp->minor, err);
usblp->device_id_string[0] = usblp->device_id_string[1] = '\0';
return -EIO;
@@ -1332,7 +1342,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
length = USBLP_DEVICE_ID_SIZE - 1;
usblp->device_id_string[length] = '\0';
- dbg("usblp%d Device ID string [len=%d]=\"%s\"",
+ dev_dbg(&usblp->intf->dev, "usblp%d Device ID string [len=%d]=\"%s\"\n",
usblp->minor, length, &usblp->device_id_string[2]);
return length;
@@ -1413,18 +1423,7 @@ static struct usb_driver usblp_driver = {
.supports_autosuspend = 1,
};
-static int __init usblp_init(void)
-{
- return usb_register(&usblp_driver);
-}
-
-static void __exit usblp_exit(void)
-{
- usb_deregister(&usblp_driver);
-}
-
-module_init(usblp_init);
-module_exit(usblp_exit);
+module_usb_driver(usblp_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 6a54634ab82..103a6e9ee49 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -19,7 +19,8 @@
* http://www.gnu.org/copyleft/gpl.html.
*/
-#include <linux/init.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
@@ -31,6 +32,8 @@
#include <linux/usb/tmc.h>
+#define RIGOL 1
+#define USBTMC_HEADER_SIZE 12
#define USBTMC_MINOR_BASE 176
/*
@@ -84,6 +87,8 @@ struct usbtmc_device_data {
u8 bTag_last_write; /* needed for abort */
u8 bTag_last_read; /* needed for abort */
+ u8 rigol_quirk;
+
/* attributes from the USB TMC spec for this device */
u8 TermChar;
bool TermCharEnabled;
@@ -97,6 +102,16 @@ struct usbtmc_device_data {
};
#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
+struct usbtmc_ID_rigol_quirk {
+ __u16 idVendor;
+ __u16 idProduct;
+};
+
+static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = {
+ { 0x1ab1, 0x0588 },
+ { 0, 0 }
+};
+
/* Forward declarations */
static struct usb_driver usbtmc_driver;
@@ -105,7 +120,6 @@ static void usbtmc_delete(struct kref *kref)
struct usbtmc_device_data *data = to_usbtmc_data(kref);
usb_put_dev(data->usb_dev);
- kfree(data);
}
static int usbtmc_open(struct inode *inode, struct file *filp)
@@ -116,10 +130,8 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
intf = usb_find_interface(&usbtmc_driver, iminor(inode));
if (!intf) {
- printk(KERN_ERR KBUILD_MODNAME
- ": can not find device for minor %d", iminor(inode));
- retval = -ENODEV;
- goto exit;
+ pr_err("can not find device for minor %d", iminor(inode));
+ return -ENODEV;
}
data = usb_get_intfdata(intf);
@@ -128,7 +140,6 @@ static int usbtmc_open(struct inode *inode, struct file *filp)
/* Store pointer in file structure's private data field */
filp->private_data = data;
-exit:
return retval;
}
@@ -186,8 +197,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++)
if (current_setting->endpoint[n].desc.bEndpointAddress ==
data->bulk_in)
- max_size = le16_to_cpu(current_setting->endpoint[n].
- desc.wMaxPacketSize);
+ max_size = usb_endpoint_maxp(&current_setting->endpoint[n].desc);
if (max_size == 0) {
dev_err(dev, "Couldn't get wMaxPacketSize\n");
@@ -268,7 +278,7 @@ usbtmc_abort_bulk_in_status:
dev_err(dev, "usb_bulk_msg returned %d\n", rv);
goto exit;
}
- } while ((actual = max_size) &&
+ } while ((actual == max_size) &&
(n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
if (actual == max_size) {
@@ -362,6 +372,63 @@ exit:
return rv;
}
+/*
+ * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-IN endpoint.
+ * @transfer_size: number of bytes to request from the device.
+ *
+ * See the USBTMC specification, Table 4.
+ *
+ * Also updates bTag_last_write.
+ */
+static int send_request_dev_dep_msg_in(struct usbtmc_device_data *data, size_t transfer_size)
+{
+ int retval;
+ u8 *buffer;
+ int actual;
+
+ buffer = kmalloc(USBTMC_HEADER_SIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ /* Setup IO buffer for REQUEST_DEV_DEP_MSG_IN message
+ * Refer to class specs for details
+ */
+ buffer[0] = 2;
+ buffer[1] = data->bTag;
+ buffer[2] = ~data->bTag;
+ buffer[3] = 0; /* Reserved */
+ buffer[4] = transfer_size >> 0;
+ buffer[5] = transfer_size >> 8;
+ buffer[6] = transfer_size >> 16;
+ buffer[7] = transfer_size >> 24;
+ buffer[8] = data->TermCharEnabled * 2;
+ /* Use term character? */
+ buffer[9] = data->TermChar;
+ buffer[10] = 0; /* Reserved */
+ buffer[11] = 0; /* Reserved */
+
+ /* Send bulk URB */
+ retval = usb_bulk_msg(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev,
+ data->bulk_out),
+ buffer, USBTMC_HEADER_SIZE, &actual, USBTMC_TIMEOUT);
+
+ /* Store bTag (in case we need to abort) */
+ data->bTag_last_write = data->bTag;
+
+ /* Increment bTag -- and increment again if zero */
+ data->bTag++;
+ if (!data->bTag)
+ data->bTag++;
+
+ kfree(buffer);
+ if (retval < 0) {
+ dev_err(&data->intf->dev, "usb_bulk_msg in send_request_dev_dep_msg_in() returned %d\n", retval);
+ return retval;
+ }
+
+ return 0;
+}
+
static ssize_t usbtmc_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
@@ -389,51 +456,39 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
goto exit;
}
- remaining = count;
- done = 0;
+ if (data->rigol_quirk) {
+ dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count);
- while (remaining > 0) {
- if (remaining > USBTMC_SIZE_IOBUFFER - 12 - 3)
- this_part = USBTMC_SIZE_IOBUFFER - 12 - 3;
- else
- this_part = remaining;
+ retval = send_request_dev_dep_msg_in(data, count);
- /* Setup IO buffer for DEV_DEP_MSG_IN message
- * Refer to class specs for details
- */
- buffer[0] = 2;
- buffer[1] = data->bTag;
- buffer[2] = ~(data->bTag);
- buffer[3] = 0; /* Reserved */
- buffer[4] = (this_part) & 255;
- buffer[5] = ((this_part) >> 8) & 255;
- buffer[6] = ((this_part) >> 16) & 255;
- buffer[7] = ((this_part) >> 24) & 255;
- buffer[8] = data->TermCharEnabled * 2;
- /* Use term character? */
- buffer[9] = data->TermChar;
- buffer[10] = 0; /* Reserved */
- buffer[11] = 0; /* Reserved */
+ if (retval < 0) {
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_out(data);
+ goto exit;
+ }
+ }
- /* Send bulk URB */
- retval = usb_bulk_msg(data->usb_dev,
- usb_sndbulkpipe(data->usb_dev,
- data->bulk_out),
- buffer, 12, &actual, USBTMC_TIMEOUT);
+ /* Loop until we have fetched everything we requested */
+ remaining = count;
+ this_part = remaining;
+ done = 0;
- /* Store bTag (in case we need to abort) */
- data->bTag_last_write = data->bTag;
+ while (remaining > 0) {
+ if (!data->rigol_quirk) {
+ dev_dbg(dev, "usb_bulk_msg_in: remaining(%zu), count(%zu)\n", remaining, count);
- /* Increment bTag -- and increment again if zero */
- data->bTag++;
- if (!data->bTag)
- (data->bTag)++;
+ if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3)
+ this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE - 3;
+ else
+ this_part = remaining;
- if (retval < 0) {
+ retval = send_request_dev_dep_msg_in(data, this_part);
+ if (retval < 0) {
dev_err(dev, "usb_bulk_msg returned %d\n", retval);
- if (data->auto_abort)
- usbtmc_ioctl_abort_bulk_out(data);
- goto exit;
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_out(data);
+ goto exit;
+ }
}
/* Send bulk URB */
@@ -443,51 +498,109 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
buffer, USBTMC_SIZE_IOBUFFER, &actual,
USBTMC_TIMEOUT);
+ dev_dbg(dev, "usb_bulk_msg: retval(%u), done(%zu), remaining(%zu), actual(%d)\n", retval, done, remaining, actual);
+
/* Store bTag (in case we need to abort) */
data->bTag_last_read = data->bTag;
if (retval < 0) {
- dev_err(dev, "Unable to read data, error %d\n", retval);
+ dev_dbg(dev, "Unable to read data, error %d\n", retval);
if (data->auto_abort)
usbtmc_ioctl_abort_bulk_in(data);
goto exit;
}
- /* How many characters did the instrument send? */
- n_characters = buffer[4] +
- (buffer[5] << 8) +
- (buffer[6] << 16) +
- (buffer[7] << 24);
+ /* Parse header in first packet */
+ if ((done == 0) || !data->rigol_quirk) {
+ /* Sanity checks for the header */
+ if (actual < USBTMC_HEADER_SIZE) {
+ dev_err(dev, "Device sent too small first packet: %u < %u\n", actual, USBTMC_HEADER_SIZE);
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* Ensure the instrument doesn't lie about it */
- if(n_characters > actual - 12) {
- dev_err(dev, "Device lies about message size: %u > %d\n", n_characters, actual - 12);
- n_characters = actual - 12;
- }
+ if (buffer[0] != 2) {
+ dev_err(dev, "Device sent reply with wrong MsgID: %u != 2\n", buffer[0]);
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* Ensure the instrument doesn't send more back than requested */
- if(n_characters > this_part) {
- dev_err(dev, "Device returns more than requested: %zu > %zu\n", done + n_characters, done + this_part);
- n_characters = this_part;
- }
+ if (buffer[1] != data->bTag_last_write) {
+ dev_err(dev, "Device sent reply with wrong bTag: %u != %u\n", buffer[1], data->bTag_last_write);
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
- /* Bound amount of data received by amount of data requested */
- if (n_characters > this_part)
- n_characters = this_part;
+ /* How many characters did the instrument send? */
+ n_characters = buffer[4] +
+ (buffer[5] << 8) +
+ (buffer[6] << 16) +
+ (buffer[7] << 24);
- /* Copy buffer to user space */
- if (copy_to_user(buf + done, &buffer[12], n_characters)) {
- /* There must have been an addressing problem */
- retval = -EFAULT;
- goto exit;
+ if (n_characters > this_part) {
+ dev_err(dev, "Device wants to return more data than requested: %u > %zu\n", n_characters, count);
+ if (data->auto_abort)
+ usbtmc_ioctl_abort_bulk_in(data);
+ goto exit;
+ }
+
+ /* Remove the USBTMC header */
+ actual -= USBTMC_HEADER_SIZE;
+
+ /* Check if the message is smaller than requested */
+ if (data->rigol_quirk) {
+ if (remaining > n_characters)
+ remaining = n_characters;
+ /* Remove padding if it exists */
+ if (actual > remaining)
+ actual = remaining;
+ }
+ else {
+ if (this_part > n_characters)
+ this_part = n_characters;
+ /* Remove padding if it exists */
+ if (actual > this_part)
+ actual = this_part;
+ }
+
+ dev_dbg(dev, "Bulk-IN header: N_characters(%u), bTransAttr(%u)\n", n_characters, buffer[8]);
+
+ remaining -= actual;
+
+ /* Terminate if end-of-message bit received from device */
+ if ((buffer[8] & 0x01) && (actual >= n_characters))
+ remaining = 0;
+
+ dev_dbg(dev, "Bulk-IN header: remaining(%zu), buf(%p), buffer(%p) done(%zu)\n", remaining,buf,buffer,done);
+
+
+ /* Copy buffer to user space */
+ if (copy_to_user(buf + done, &buffer[USBTMC_HEADER_SIZE], actual)) {
+ /* There must have been an addressing problem */
+ retval = -EFAULT;
+ goto exit;
+ }
+ done += actual;
}
+ else {
+ if (actual > remaining)
+ actual = remaining;
- done += n_characters;
- /* Terminate if end-of-message bit recieved from device */
- if ((buffer[8] & 0x01) && (actual >= n_characters + 12))
- remaining = 0;
- else
- remaining -= n_characters;
+ remaining -= actual;
+
+ dev_dbg(dev, "Bulk-IN header cont: actual(%u), done(%zu), remaining(%zu), buf(%p), buffer(%p)\n", actual, done, remaining,buf,buffer);
+
+ /* Copy buffer to user space */
+ if (copy_to_user(buf + done, buffer, actual)) {
+ /* There must have been an addressing problem */
+ retval = -EFAULT;
+ goto exit;
+ }
+ done += actual;
+ }
}
/* Update file position value */
@@ -528,8 +641,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
done = 0;
while (remaining > 0) {
- if (remaining > USBTMC_SIZE_IOBUFFER - 12) {
- this_part = USBTMC_SIZE_IOBUFFER - 12;
+ if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) {
+ this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;
buffer[8] = 0;
} else {
this_part = remaining;
@@ -539,24 +652,24 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
/* Setup IO buffer for DEV_DEP_MSG_OUT message */
buffer[0] = 1;
buffer[1] = data->bTag;
- buffer[2] = ~(data->bTag);
+ buffer[2] = ~data->bTag;
buffer[3] = 0; /* Reserved */
- buffer[4] = this_part & 255;
- buffer[5] = (this_part >> 8) & 255;
- buffer[6] = (this_part >> 16) & 255;
- buffer[7] = (this_part >> 24) & 255;
+ buffer[4] = this_part >> 0;
+ buffer[5] = this_part >> 8;
+ buffer[6] = this_part >> 16;
+ buffer[7] = this_part >> 24;
/* buffer[8] is set above... */
buffer[9] = 0; /* Reserved */
buffer[10] = 0; /* Reserved */
buffer[11] = 0; /* Reserved */
- if (copy_from_user(&buffer[12], buf + done, this_part)) {
+ if (copy_from_user(&buffer[USBTMC_HEADER_SIZE], buf + done, this_part)) {
retval = -EFAULT;
goto exit;
}
- n_bytes = roundup(12 + this_part, 4);
- memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part));
+ n_bytes = roundup(USBTMC_HEADER_SIZE + this_part, 4);
+ memset(buffer + USBTMC_HEADER_SIZE + this_part, 0, n_bytes - (USBTMC_HEADER_SIZE + this_part));
do {
retval = usb_bulk_msg(data->usb_dev,
@@ -636,7 +749,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {
desc = &current_setting->endpoint[n].desc;
if (desc->bEndpointAddress == data->bulk_in)
- max_size = le16_to_cpu(desc->wMaxPacketSize);
+ max_size = usb_endpoint_maxp(desc);
}
if (max_size == 0) {
@@ -719,50 +832,32 @@ exit:
static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)
{
- u8 *buffer;
int rv;
- buffer = kmalloc(2, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
rv = usb_clear_halt(data->usb_dev,
usb_sndbulkpipe(data->usb_dev, data->bulk_out));
if (rv < 0) {
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
rv);
- goto exit;
+ return rv;
}
- rv = 0;
-
-exit:
- kfree(buffer);
- return rv;
+ return 0;
}
static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
{
- u8 *buffer;
int rv;
- buffer = kmalloc(2, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
rv = usb_clear_halt(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev, data->bulk_in));
if (rv < 0) {
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
rv);
- goto exit;
+ return rv;
}
- rv = 0;
-
-exit:
- kfree(buffer);
- return rv;
+ return 0;
}
static int get_capabilities(struct usbtmc_device_data *data)
@@ -807,7 +902,7 @@ err_out:
}
#define capability_attribute(name) \
-static ssize_t show_##name(struct device *dev, \
+static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
@@ -815,7 +910,7 @@ static ssize_t show_##name(struct device *dev, \
\
return sprintf(buf, "%d\n", data->capabilities.name); \
} \
-static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+static DEVICE_ATTR_RO(name)
capability_attribute(interface_capabilities);
capability_attribute(device_capabilities);
@@ -834,7 +929,7 @@ static struct attribute_group capability_attr_grp = {
.attrs = capability_attrs,
};
-static ssize_t show_TermChar(struct device *dev,
+static ssize_t TermChar_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
@@ -843,7 +938,7 @@ static ssize_t show_TermChar(struct device *dev,
return sprintf(buf, "%c\n", data->TermChar);
}
-static ssize_t store_TermChar(struct device *dev,
+static ssize_t TermChar_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -855,10 +950,10 @@ static ssize_t store_TermChar(struct device *dev,
data->TermChar = buf[0];
return count;
}
-static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar);
+static DEVICE_ATTR_RW(TermChar);
#define data_attribute(name) \
-static ssize_t show_##name(struct device *dev, \
+static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
@@ -866,7 +961,7 @@ static ssize_t show_##name(struct device *dev, \
\
return sprintf(buf, "%d\n", data->name); \
} \
-static ssize_t store_##name(struct device *dev, \
+static ssize_t name##_store(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
@@ -884,7 +979,7 @@ static ssize_t store_##name(struct device *dev, \
else \
return count; \
} \
-static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name)
+static DEVICE_ATTR_RW(name)
data_attribute(TermCharEnabled);
data_attribute(auto_abort);
@@ -1008,7 +1103,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL);
+ data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(&intf->dev, "Unable to allocate kernel memory\n");
return -ENOMEM;
@@ -1022,6 +1117,20 @@ static int usbtmc_probe(struct usb_interface *intf,
mutex_init(&data->io_mutex);
data->zombie = 0;
+ /* Determine if it is a Rigol or not */
+ data->rigol_quirk = 0;
+ dev_dbg(&intf->dev, "Trying to find if device Vendor 0x%04X Product 0x%04X has the RIGOL quirk\n",
+ le16_to_cpu(data->usb_dev->descriptor.idVendor),
+ le16_to_cpu(data->usb_dev->descriptor.idProduct));
+ for(n = 0; usbtmc_id_quirk[n].idVendor > 0; n++) {
+ if ((usbtmc_id_quirk[n].idVendor == le16_to_cpu(data->usb_dev->descriptor.idVendor)) &&
+ (usbtmc_id_quirk[n].idProduct == le16_to_cpu(data->usb_dev->descriptor.idProduct))) {
+ dev_dbg(&intf->dev, "Setting this device as having the RIGOL quirk\n");
+ data->rigol_quirk = 1;
+ break;
+ }
+ }
+
/* Initialize USBTMC bTag and other fields */
data->bTag = 1;
data->TermCharEnabled = 0;
@@ -1117,21 +1226,6 @@ static struct usb_driver usbtmc_driver = {
.resume = usbtmc_resume,
};
-static int __init usbtmc_init(void)
-{
- int retcode;
-
- retcode = usb_register(&usbtmc_driver);
- if (retcode)
- printk(KERN_ERR KBUILD_MODNAME": Unable to register driver\n");
- return retcode;
-}
-module_init(usbtmc_init);
-
-static void __exit usbtmc_exit(void)
-{
- usb_deregister(&usbtmc_driver);
-}
-module_exit(usbtmc_exit);
+module_usb_driver(usbtmc_driver);
MODULE_LICENSE("GPL");