summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/usb/cdc_subset.c2
-rw-r--r--drivers/usb/class/Kconfig11
-rw-r--r--drivers/usb/class/Makefile1
-rw-r--r--drivers/usb/class/cdc-wdm.c740
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c3
-rw-r--r--drivers/usb/host/ehci-orion.c2
-rw-r--r--drivers/usb/serial/ch341.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/ftdi_sio.h6
-rw-r--r--drivers/usb/serial/option.c7
-rw-r--r--drivers/usb/serial/pl2303.c1
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--include/linux/usb/cdc.h9
13 files changed, 780 insertions, 5 deletions
diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c
index 0ec7936cbe21..c66b9c324f54 100644
--- a/drivers/net/usb/cdc_subset.c
+++ b/drivers/net/usb/cdc_subset.c
@@ -218,7 +218,7 @@ static const struct driver_info	blob_info = {
 /*-------------------------------------------------------------------------*/
 
 #ifndef	HAVE_HARDWARE
-#error You need to configure some hardware for this driver
+#warning You need to configure some hardware for this driver
 #endif
 
 /*
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index 3a9102d2591b..66f17ed88cb5 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -29,3 +29,14 @@ config USB_PRINTER
 	  To compile this driver as a module, choose M here: the
 	  module will be called usblp.
 
+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
+	  AT commands over this device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cdc-wdm.
+
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
index cc391e6c2af8..535d59a30600 100644
--- a/drivers/usb/class/Makefile
+++ b/drivers/usb/class/Makefile
@@ -5,3 +5,4 @@
 
 obj-$(CONFIG_USB_ACM)		+= cdc-acm.o
 obj-$(CONFIG_USB_PRINTER)	+= usblp.o
+obj-$(CONFIG_USB_WDM)		+= cdc-wdm.o
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
new file mode 100644
index 000000000000..107666d4e2ec
--- /dev/null
+++ b/drivers/usb/class/cdc-wdm.c
@@ -0,0 +1,740 @@
+/*
+ * cdc-wdm.c
+ *
+ * This driver supports USB CDC WCM Device Management.
+ *
+ * Copyright (c) 2007-2008 Oliver Neukum
+ *
+ * Some code taken from cdc-acm.c
+ *
+ * Released under the GPLv2.
+ *
+ * Many thanks to Carl Nordbeck
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.02"
+#define DRIVER_AUTHOR "Oliver Neukum"
+
+static struct usb_device_id wdm_ids[] = {
+	{
+		.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
+				 USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+		.bInterfaceClass = USB_CLASS_COMM,
+		.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
+	},
+	{ }
+};
+
+#define WDM_MINOR_BASE	176
+
+
+#define WDM_IN_USE		1
+#define WDM_DISCONNECTING	2
+#define WDM_RESULT		3
+#define WDM_READ		4
+#define WDM_INT_STALL		5
+#define WDM_POLL_RUNNING	6
+
+
+#define WDM_MAX			16
+
+
+static DEFINE_MUTEX(wdm_mutex);
+
+/* --- method tables --- */
+
+struct wdm_device {
+	u8			*inbuf; /* buffer for response */
+	u8			*outbuf; /* buffer for command */
+	u8			*sbuf; /* buffer for status */
+	u8			*ubuf; /* buffer for copy to user space */
+
+	struct urb		*command;
+	struct urb		*response;
+	struct urb		*validity;
+	struct usb_interface	*intf;
+	struct usb_ctrlrequest	*orq;
+	struct usb_ctrlrequest	*irq;
+	spinlock_t		iuspin;
+
+	unsigned long		flags;
+	u16			bufsize;
+	u16			wMaxCommand;
+	u16			wMaxPacketSize;
+	u16			bMaxPacketSize0;
+	__le16			inum;
+	int			reslength;
+	int			length;
+	int			read;
+	int			count;
+	dma_addr_t		shandle;
+	dma_addr_t		ihandle;
+	struct mutex		wlock;
+	struct mutex		rlock;
+	wait_queue_head_t	wait;
+	struct work_struct	rxwork;
+	int			werr;
+	int			rerr;
+};
+
+static struct usb_driver wdm_driver;
+
+/* --- callbacks --- */
+static void wdm_out_callback(struct urb *urb)
+{
+	struct wdm_device *desc;
+	desc = urb->context;
+	spin_lock(&desc->iuspin);
+	desc->werr = urb->status;
+	spin_unlock(&desc->iuspin);
+	clear_bit(WDM_IN_USE, &desc->flags);
+	kfree(desc->outbuf);
+	wake_up(&desc->wait);
+}
+
+static void wdm_in_callback(struct urb *urb)
+{
+	struct wdm_device *desc = urb->context;
+	int status = urb->status;
+
+	spin_lock(&desc->iuspin);
+
+	if (status) {
+		switch (status) {
+		case -ENOENT:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ENOENT");
+			break;
+		case -ECONNRESET:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ECONNRESET");
+			break;
+		case -ESHUTDOWN:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ESHUTDOWN");
+			break;
+		case -EPIPE:
+			err("nonzero urb status received: -EPIPE");
+			break;
+		default:
+			err("Unexpected error %d", status);
+			break;
+		}
+	}
+
+	desc->rerr = status;
+	desc->reslength = urb->actual_length;
+	memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
+	desc->length += desc->reslength;
+	wake_up(&desc->wait);
+
+	set_bit(WDM_READ, &desc->flags);
+	spin_unlock(&desc->iuspin);
+}
+
+static void wdm_int_callback(struct urb *urb)
+{
+	int rv = 0;
+	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) {
+		switch (status) {
+		case -ESHUTDOWN:
+		case -ENOENT:
+		case -ECONNRESET:
+			return; /* unplug */
+		case -EPIPE:
+			set_bit(WDM_INT_STALL, &desc->flags);
+			err("Stall on int endpoint");
+			goto sw; /* halt is cleared in work */
+		default:
+			err("nonzero urb status received: %d", status);
+			break;
+		}
+	}
+
+	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
+		err("wdm_int_callback - %d bytes", urb->actual_length);
+		goto exit;
+	}
+
+	switch (dr->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		dev_dbg(&desc->intf->dev,
+			"NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
+			dr->wIndex, dr->wLength);
+		break;
+
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+
+		dev_dbg(&desc->intf->dev,
+			"NOTIFY_NETWORK_CONNECTION %s network",
+			dr->wValue ? "connected to" : "disconnected from");
+		goto exit;
+	default:
+		clear_bit(WDM_POLL_RUNNING, &desc->flags);
+		err("unknown notification %d received: index %d len %d",
+			dr->bNotificationType, dr->wIndex, dr->wLength);
+		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->bMaxPacketSize0);
+
+	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->bMaxPacketSize0,
+		wdm_in_callback,
+		desc
+	);
+	desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	spin_lock(&desc->iuspin);
+	clear_bit(WDM_READ, &desc->flags);
+	if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		rv = usb_submit_urb(desc->response, GFP_ATOMIC);
+		dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
+			__func__, rv);
+	}
+	spin_unlock(&desc->iuspin);
+	if (rv < 0) {
+		if (rv == -EPERM)
+			return;
+		if (rv == -ENOMEM) {
+sw:
+			rv = schedule_work(&desc->rxwork);
+			if (rv)
+				err("Cannot schedule work");
+		}
+	}
+exit:
+	rv = usb_submit_urb(urb, GFP_ATOMIC);
+	if (rv)
+		err("%s - usb_submit_urb failed with result %d",
+		     __func__, rv);
+
+}
+
+static void kill_urbs(struct wdm_device *desc)
+{
+	usb_kill_urb(desc->command);
+	usb_kill_urb(desc->validity);
+	usb_kill_urb(desc->response);
+}
+
+static void free_urbs(struct wdm_device *desc)
+{
+	usb_free_urb(desc->validity);
+	usb_free_urb(desc->response);
+	usb_free_urb(desc->command);
+}
+
+static void cleanup(struct wdm_device *desc)
+{
+	usb_buffer_free(interface_to_usbdev(desc->intf),
+			desc->wMaxPacketSize,
+			desc->sbuf,
+			desc->validity->transfer_dma);
+	usb_buffer_free(interface_to_usbdev(desc->intf),
+			desc->wMaxPacketSize,
+			desc->inbuf,
+			desc->response->transfer_dma);
+	kfree(desc->orq);
+	kfree(desc->irq);
+	kfree(desc->ubuf);
+	free_urbs(desc);
+	kfree(desc);
+}
+
+static ssize_t wdm_write
+(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+	u8 *buf;
+	int rv = -EMSGSIZE, r, we;
+	struct wdm_device *desc = file->private_data;
+	struct usb_ctrlrequest *req;
+
+	if (count > desc->wMaxCommand)
+		count = desc->wMaxCommand;
+
+	spin_lock_irq(&desc->iuspin);
+	we = desc->werr;
+	desc->werr = 0;
+	spin_unlock_irq(&desc->iuspin);
+	if (we < 0)
+		return -EIO;
+
+	r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */
+	rv = -ERESTARTSYS;
+	if (r)
+		goto outnl;
+
+	r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
+							   &desc->flags));
+	if (r < 0)
+		goto out;
+
+	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		rv = -ENODEV;
+		goto out;
+	}
+
+	desc->outbuf = buf = kmalloc(count, GFP_KERNEL);
+	if (!buf) {
+		rv = -ENOMEM;
+		goto out;
+	}
+
+	r = copy_from_user(buf, buffer, count);
+	if (r > 0) {
+		kfree(buf);
+		rv = -EFAULT;
+		goto out;
+	}
+
+	req = desc->orq;
+	usb_fill_control_urb(
+		desc->command,
+		interface_to_usbdev(desc->intf),
+		/* using common endpoint 0 */
+		usb_sndctrlpipe(interface_to_usbdev(desc->intf), 0),
+		(unsigned char *)req,
+		buf,
+		count,
+		wdm_out_callback,
+		desc
+	);
+
+	req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+			     USB_RECIP_INTERFACE);
+	req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+	req->wValue = 0;
+	req->wIndex = desc->inum;
+	req->wLength = cpu_to_le16(count);
+	set_bit(WDM_IN_USE, &desc->flags);
+
+	rv = usb_submit_urb(desc->command, GFP_KERNEL);
+	if (rv < 0) {
+		kfree(buf);
+		clear_bit(WDM_IN_USE, &desc->flags);
+	} else {
+		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
+			req->wIndex);
+	}
+out:
+	mutex_unlock(&desc->wlock);
+outnl:
+	return rv < 0 ? rv : count;
+}
+
+static ssize_t wdm_read
+(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	int rv, cntr;
+	int i = 0;
+	struct wdm_device *desc = file->private_data;
+
+
+	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
+	if (rv < 0)
+		return -ERESTARTSYS;
+
+	if (desc->length == 0) {
+		desc->read = 0;
+retry:
+		i++;
+		rv = wait_event_interruptible(desc->wait,
+					      test_bit(WDM_READ, &desc->flags));
+
+		if (rv < 0) {
+			rv = -ERESTARTSYS;
+			goto err;
+		}
+
+		spin_lock_irq(&desc->iuspin);
+
+		if (desc->rerr) { /* read completed, error happened */
+			int t = desc->rerr;
+			desc->rerr = 0;
+			spin_unlock_irq(&desc->iuspin);
+			err("reading had resulted in %d", t);
+			rv = -EIO;
+			goto err;
+		}
+		/*
+		 * recheck whether we've lost the race
+		 * against the completion handler
+		 */
+		if (!test_bit(WDM_READ, &desc->flags)) { /* lost race */
+			spin_unlock_irq(&desc->iuspin);
+			goto retry;
+		}
+		if (!desc->reslength) { /* zero length read */
+			spin_unlock_irq(&desc->iuspin);
+			goto retry;
+		}
+		clear_bit(WDM_READ, &desc->flags);
+		spin_unlock_irq(&desc->iuspin);
+	}
+
+	cntr = count > desc->length ? desc->length : count;
+	rv = copy_to_user(buffer, desc->ubuf, cntr);
+	if (rv > 0) {
+		rv = -EFAULT;
+		goto err;
+	}
+
+	for (i = 0; i < desc->length - cntr; i++)
+		desc->ubuf[i] = desc->ubuf[i + cntr];
+
+	desc->length -= cntr;
+	rv = cntr;
+
+err:
+	mutex_unlock(&desc->rlock);
+	if (rv < 0)
+		err("wdm_read: exit error");
+	return rv;
+}
+
+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)
+		err("Error in flush path: %d", desc->werr);
+
+	return desc->werr;
+}
+
+static unsigned int wdm_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct wdm_device *desc = file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	spin_lock_irqsave(&desc->iuspin, flags);
+	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		mask = POLLERR;
+		spin_unlock_irqrestore(&desc->iuspin, flags);
+		goto desc_out;
+	}
+	if (test_bit(WDM_READ, &desc->flags))
+		mask = POLLIN | POLLRDNORM;
+	if (desc->rerr || desc->werr)
+		mask |= POLLERR;
+	if (!test_bit(WDM_IN_USE, &desc->flags))
+		mask |= POLLOUT | POLLWRNORM;
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+
+	poll_wait(file, &desc->wait, wait);
+
+desc_out:
+	return mask;
+}
+
+static int wdm_open(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	int rv = -ENODEV;
+	struct usb_interface *intf;
+	struct wdm_device *desc;
+
+	mutex_lock(&wdm_mutex);
+	intf = usb_find_interface(&wdm_driver, minor);
+	if (!intf)
+		goto out;
+
+	desc = usb_get_intfdata(intf);
+	if (test_bit(WDM_DISCONNECTING, &desc->flags))
+		goto out;
+
+	desc->count++;
+	file->private_data = desc;
+
+	rv = usb_submit_urb(desc->validity, GFP_KERNEL);
+
+	if (rv < 0) {
+		desc->count--;
+		err("Error submitting int urb - %d", rv);
+		goto out;
+	}
+	rv = 0;
+
+out:
+	mutex_unlock(&wdm_mutex);
+	return rv;
+}
+
+static int wdm_release(struct inode *inode, struct file *file)
+{
+	struct wdm_device *desc = file->private_data;
+
+	mutex_lock(&wdm_mutex);
+	desc->count--;
+	if (!desc->count) {
+		dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
+		kill_urbs(desc);
+	}
+	mutex_unlock(&wdm_mutex);
+	return 0;
+}
+
+static const struct file_operations wdm_fops = {
+	.owner =	THIS_MODULE,
+	.read =		wdm_read,
+	.write =	wdm_write,
+	.open =		wdm_open,
+	.flush =	wdm_flush,
+	.release =	wdm_release,
+	.poll =		wdm_poll
+};
+
+static struct usb_class_driver wdm_class = {
+	.name =		"cdc-wdm%d",
+	.fops =		&wdm_fops,
+	.minor_base =	WDM_MINOR_BASE,
+};
+
+/* --- error handling --- */
+static void wdm_rxwork(struct work_struct *work)
+{
+	struct wdm_device *desc = container_of(work, struct wdm_device, rxwork);
+	unsigned long flags;
+	int rv;
+
+	spin_lock_irqsave(&desc->iuspin, flags);
+	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		spin_unlock_irqrestore(&desc->iuspin, flags);
+	} else {
+		spin_unlock_irqrestore(&desc->iuspin, flags);
+		rv = usb_submit_urb(desc->response, GFP_KERNEL);
+		if (rv < 0 && rv != -EPERM) {
+			spin_lock_irqsave(&desc->iuspin, flags);
+			if (!test_bit(WDM_DISCONNECTING, &desc->flags))
+				schedule_work(&desc->rxwork);
+			spin_unlock_irqrestore(&desc->iuspin, flags);
+		}
+	}
+}
+
+/* --- hotplug --- */
+
+static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	int rv = -EINVAL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	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 > 0) {
+		if (buffer [1] != USB_DT_CS_INTERFACE) {
+			err("skipping garbage");
+			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:
+			err("Ignoring extra header, type %d, length %d",
+				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->wlock);
+	mutex_init(&desc->rlock);
+	spin_lock_init(&desc->iuspin);
+	init_waitqueue_head(&desc->wait);
+	desc->wMaxCommand = maxcom;
+	desc->inum = cpu_to_le16((u16)intf->cur_altsetting->desc.bInterfaceNumber);
+	desc->intf = intf;
+	INIT_WORK(&desc->rxwork, wdm_rxwork);
+
+	iface = &intf->altsetting[0];
+	ep = &iface->endpoint[0].desc;
+	if (!usb_endpoint_is_int_in(ep)) {
+		rv = -EINVAL;
+		goto err;
+	}
+
+	desc->wMaxPacketSize = ep->wMaxPacketSize;
+	desc->bMaxPacketSize0 = cpu_to_le16(udev->descriptor.bMaxPacketSize0);
+
+	desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+	if (!desc->orq)
+		goto err;
+	desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+	if (!desc->irq)
+		goto err;
+
+	desc->validity = usb_alloc_urb(0, GFP_KERNEL);
+	if (!desc->validity)
+		goto err;
+
+	desc->response = usb_alloc_urb(0, GFP_KERNEL);
+	if (!desc->response)
+		goto err;
+
+	desc->command = usb_alloc_urb(0, GFP_KERNEL);
+	if (!desc->command)
+		goto err;
+
+	desc->ubuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
+	if (!desc->ubuf)
+		goto err;
+
+	desc->sbuf = usb_buffer_alloc(interface_to_usbdev(intf),
+					desc->wMaxPacketSize,
+					GFP_KERNEL,
+					&desc->validity->transfer_dma);
+	if (!desc->sbuf)
+		goto err;
+
+	desc->inbuf = usb_buffer_alloc(interface_to_usbdev(intf),
+					desc->bMaxPacketSize0,
+					GFP_KERNEL,
+					&desc->response->transfer_dma);
+	if (!desc->inbuf)
+		goto err2;
+
+	usb_fill_int_urb(
+		desc->validity,
+		interface_to_usbdev(intf),
+		usb_rcvintpipe(interface_to_usbdev(intf), ep->bEndpointAddress),
+		desc->sbuf,
+		desc->wMaxPacketSize,
+		wdm_int_callback,
+		desc,
+		ep->bInterval
+	);
+	desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_set_intfdata(intf, desc);
+	rv = usb_register_dev(intf, &wdm_class);
+	dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
+		 intf->minor - WDM_MINOR_BASE);
+	if (rv < 0)
+		goto err;
+out:
+	return rv;
+err2:
+	usb_buffer_free(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);
+	return rv;
+}
+
+static void wdm_disconnect(struct usb_interface *intf)
+{
+	struct wdm_device *desc;
+	unsigned long flags;
+
+	usb_deregister_dev(intf, &wdm_class);
+	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);
+	set_bit(WDM_DISCONNECTING, &desc->flags);
+	set_bit(WDM_READ, &desc->flags);
+	clear_bit(WDM_IN_USE, &desc->flags);
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+	cancel_work_sync(&desc->rxwork);
+	kill_urbs(desc);
+	wake_up_all(&desc->wait);
+	if (!desc->count)
+		cleanup(desc);
+	mutex_unlock(&wdm_mutex);
+}
+
+static struct usb_driver wdm_driver = {
+	.name =		"cdc_wdm",
+	.probe =	wdm_probe,
+	.disconnect =	wdm_disconnect,
+	.id_table =	wdm_ids,
+};
+
+/* --- 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_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION("USB Abstract Control Model driver for "
+		   "USB WCM Device Management");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 499b7a23f351..e02bfd4df3a6 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1526,7 +1526,8 @@ static void udc_disable(struct pxa_udc *udc)
 
 	ep0_idle(udc);
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
-	udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+	if (udc->mach->udc_command)
+		udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
 }
 
 /**
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index d187d0313742..3adfda813a7b 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -115,6 +115,8 @@ static int ehci_orion_setup(struct usb_hcd *hcd)
 	if (retval)
 		return retval;
 
+	hcd->has_tt = 1;
+
 	ehci_reset(ehci);
 	ehci_port_power(ehci, 0);
 
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index ba28fdc9ccd2..1f7c86bd8297 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -28,6 +28,7 @@ static int debug;
 
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(0x4348, 0x5523) },
+	{ USB_DEVICE(0x1a86, 0x7523) },
 	{ },
 };
 MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 5b349ece7247..3cee6feac174 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -374,6 +374,7 @@ static struct usb_device_id id_table_combined [] = {
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
 	{ USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
 	{ },					/* Optional parameter entry */
 	{ }					/* Terminating entry */
 };
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 504edf8c3a3f..a72f2c81d664 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -592,6 +592,12 @@
 #define	FIC_NEO1973_DEBUG_PID	0x5118
 
 /*
+ * RATOC REX-USB60F
+ */
+#define RATOC_VENDOR_ID		0x0584
+#define RATOC_PRODUCT_ID_USB60F	0xb020
+
+/*
  *   BmRequestType:  1100 0000b
  *   bRequest:       FTDI_E2_READ
  *   wValue:         0
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e7e016e60333..6cecd2c12b1d 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -183,6 +183,7 @@ static int  option_send_setup(struct usb_serial_port *port);
 #define AXESSTEL_PRODUCT_MV110H			0x1000
 
 #define ONDA_VENDOR_ID				0x19d2
+#define ONDA_PRODUCT_MSA501HS			0x0001
 #define ONDA_PRODUCT_ET502HS			0x0002
 
 #define BANDRICH_VENDOR_ID			0x1A8D
@@ -196,6 +197,9 @@ static int  option_send_setup(struct usb_serial_port *port);
 
 #define MAXON_VENDOR_ID				0x16d8
 
+#define TELIT_VENDOR_ID				0x1bc7
+#define TELIT_PRODUCT_UC864E			0x1003
+
 static struct usb_device_id option_ids[] = {
 	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
 	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -297,13 +301,14 @@ static struct usb_device_id option_ids[] = {
 	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) },
 	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
 	{ USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) },
+	{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MSA501HS) },
 	{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) },
 	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
 	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
 	{ USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
 	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
 	{ USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
-	{ USB_DEVICE(0x19d2, 0x0001) }, 	/* Telstra NextG CDMA */
+	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
 	{ } /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index c605fb68f807..234c5eea95a2 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -66,7 +66,6 @@ static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
 	{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
 	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
-	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
 	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
 	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
 	{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 10cf872e5ecb..3bdefe020501 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -36,7 +36,6 @@
 
 #define RATOC_VENDOR_ID		0x0584
 #define RATOC_PRODUCT_ID	0xb000
-#define RATOC_PRODUCT_ID_USB60F	0xb020
 
 #define TRIPP_VENDOR_ID		0x2478
 #define TRIPP_PRODUCT_ID	0x2008
diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h
index 71e52f2f6a38..ca228bb94218 100644
--- a/include/linux/usb/cdc.h
+++ b/include/linux/usb/cdc.h
@@ -130,6 +130,15 @@ struct usb_cdc_ether_desc {
 	__u8	bNumberPowerFilters;
 } __attribute__ ((packed));
 
+/* "Telephone Control Model Functional Descriptor" from CDC WMC spec 6.3..3 */
+struct usb_cdc_dmm_desc {
+	__u8	bFunctionLength;
+	__u8	bDescriptorType;
+	__u8	bDescriptorSubtype;
+	__u16	bcdVersion;
+	__le16	wMaxCommand;
+} __attribute__ ((packed));
+
 /* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */
 struct usb_cdc_mdlm_desc {
 	__u8	bLength;