v4.19.13 snapshot.
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
new file mode 100644
index 0000000..971385f
--- /dev/null
+++ b/drivers/usb/class/Kconfig
@@ -0,0 +1,48 @@
+#
+# USB Class driver configuration
+#
+comment "USB Device Class drivers"
+
+config USB_ACM
+	tristate "USB Modem (CDC ACM) support"
+	depends on TTY
+	---help---
+	  This driver supports USB modems and ISDN adapters which support the
+	  Communication Device Class Abstract Control Model interface.
+	  Please read <file:Documentation/usb/acm.txt> for details.
+
+	  If your modem only reports "Cls=ff(vend.)" in the descriptors in
+	  /sys/kernel/debug/usb/devices, then your modem will not work with this
+	  driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cdc-acm.
+
+config USB_PRINTER
+	tristate "USB Printer support"
+	help
+	  Say Y here if you want to connect a USB printer to your computer's
+	  USB port.
+
+	  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"
+	---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.
+
+config USB_TMC
+	tristate "USB Test and Measurement Class support"
+	help
+	  Say Y here if you want to connect a USB device that follows
+	  the USB.org specification for USB Test and Measurement devices
+	  to your computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbtmc.
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
new file mode 100644
index 0000000..5d393a2
--- /dev/null
+++ b/drivers/usb/class/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for USB Class drivers
+# (one step up from the misc category)
+#
+
+obj-$(CONFIG_USB_ACM)		+= cdc-acm.o
+obj-$(CONFIG_USB_PRINTER)	+= usblp.o
+obj-$(CONFIG_USB_WDM)		+= cdc-wdm.o
+obj-$(CONFIG_USB_TMC)		+= usbtmc.o
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
new file mode 100644
index 0000000..40c64c7
--- /dev/null
+++ b/drivers/usb/class/cdc-acm.c
@@ -0,0 +1,1994 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * cdc-acm.c
+ *
+ * Copyright (c) 1999 Armin Fuerst	<fuerst@in.tum.de>
+ * Copyright (c) 1999 Pavel Machek	<pavel@ucw.cz>
+ * Copyright (c) 1999 Johannes Erdfelt	<johannes@erdfelt.com>
+ * 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
+ */
+
+#undef DEBUG
+#undef VERBOSE_DEBUG
+
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+
+#include "cdc-acm.h"
+
+
+#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 DEFINE_IDR(acm_minors);
+static DEFINE_MUTEX(acm_minors_lock);
+
+static void acm_tty_set_termios(struct tty_struct *tty,
+				struct ktermios *termios_old);
+
+/*
+ * acm_minors accessors
+ */
+
+/*
+ * Look up an ACM structure by minor. If found and not disconnected, increment
+ * its refcount and return it with its mutex held.
+ */
+static struct acm *acm_get_by_minor(unsigned int minor)
+{
+	struct acm *acm;
+
+	mutex_lock(&acm_minors_lock);
+	acm = idr_find(&acm_minors, minor);
+	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_minors_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_minors_lock);
+	minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL);
+	mutex_unlock(&acm_minors_lock);
+
+	return minor;
+}
+
+/* Release the minor number associated with 'acm'.  */
+static void acm_release_minor(struct acm *acm)
+{
+	mutex_lock(&acm_minors_lock);
+	idr_remove(&acm_minors, acm->minor);
+	mutex_unlock(&acm_minors_lock);
+}
+
+/*
+ * Functions for ACM control messages.
+ */
+
+static int acm_ctrl_msg(struct acm *acm, int request, int value,
+							void *buf, int len)
+{
+	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);
+
+	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;
+}
+
+/* devices aren't required to support these requests.
+ * the cdc acm descriptor tells whether they do...
+ */
+static inline int acm_set_control(struct acm *acm, int control)
+{
+	if (acm->quirks & QUIRK_CONTROL_LINE_STATE)
+		return -EOPNOTSUPP;
+
+	return acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+			control, NULL, 0);
+}
+
+#define acm_set_line(acm, line) \
+	acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
+#define acm_send_break(acm, ms) \
+	acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)
+
+static void acm_kill_urbs(struct acm *acm)
+{
+	int i;
+
+	usb_kill_urb(acm->ctrlurb);
+	for (i = 0; i < ACM_NW; i++)
+		usb_kill_urb(acm->wb[i].urb);
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_kill_urb(acm->read_urbs[i]);
+}
+
+/*
+ * Write buffer management.
+ * All of these assume proper locks taken by the caller.
+ */
+
+static int acm_wb_alloc(struct acm *acm)
+{
+	int i, wbn;
+	struct acm_wb *wb;
+
+	wbn = 0;
+	i = 0;
+	for (;;) {
+		wb = &acm->wb[wbn];
+		if (!wb->use) {
+			wb->use = 1;
+			wb->len = 0;
+			return wbn;
+		}
+		wbn = (wbn + 1) % ACM_NW;
+		if (++i >= ACM_NW)
+			return -1;
+	}
+}
+
+static int acm_wb_is_avail(struct acm *acm)
+{
+	int i, n;
+	unsigned long flags;
+
+	n = ACM_NW;
+	spin_lock_irqsave(&acm->write_lock, flags);
+	for (i = 0; i < ACM_NW; i++)
+		n -= acm->wb[i].use;
+	spin_unlock_irqrestore(&acm->write_lock, flags);
+	return n;
+}
+
+/*
+ * Finish write. Caller must hold acm->write_lock
+ */
+static void acm_write_done(struct acm *acm, struct acm_wb *wb)
+{
+	wb->use = 0;
+	acm->transmitting--;
+	usb_autopm_put_interface_async(acm->control);
+}
+
+/*
+ * Poke write.
+ *
+ * the caller is responsible for locking
+ */
+
+static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
+{
+	int rc;
+
+	acm->transmitting++;
+
+	wb->urb->transfer_buffer = wb->buf;
+	wb->urb->transfer_dma = wb->dmah;
+	wb->urb->transfer_buffer_length = wb->len;
+	wb->urb->dev = acm->dev;
+
+	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
+	if (rc < 0) {
+		dev_err(&acm->data->dev,
+			"%s - usb_submit_urb(write bulk) failed: %d\n",
+			__func__, rc);
+		acm_write_done(acm, wb);
+	}
+	return rc;
+}
+
+/*
+ * attributes exported through sysfs
+ */
+static ssize_t bmCapabilities_show
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d", acm->ctrl_caps);
+}
+static DEVICE_ATTR_RO(bmCapabilities);
+
+static ssize_t wCountryCodes_show
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
+
+	memcpy(buf, acm->country_codes, acm->country_code_size);
+	return acm->country_code_size;
+}
+
+static DEVICE_ATTR_RO(wCountryCodes);
+
+static ssize_t iCountryCodeRelDate_show
+(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct acm *acm = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d", acm->country_rel_date);
+}
+
+static DEVICE_ATTR_RO(iCountryCodeRelDate);
+/*
+ * Interrupt handlers for various ACM device responses
+ */
+
+static void acm_process_notification(struct acm *acm, unsigned char *buf)
+{
+	int newctrl;
+	int difference;
+	unsigned long flags;
+	struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf;
+	unsigned char *data = buf + sizeof(struct usb_cdc_notification);
+
+	switch (dr->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		dev_dbg(&acm->control->dev,
+			"%s - network connection: %d\n", __func__, dr->wValue);
+		break;
+
+	case USB_CDC_NOTIFY_SERIAL_STATE:
+		if (le16_to_cpu(dr->wLength) != 2) {
+			dev_dbg(&acm->control->dev,
+				"%s - malformed serial state\n", __func__);
+			break;
+		}
+
+		newctrl = get_unaligned_le16(data);
+		dev_dbg(&acm->control->dev,
+			"%s - serial state: 0x%x\n", __func__, newctrl);
+
+		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_irqsave(&acm->read_lock, flags);
+		acm->ctrlin = newctrl;
+		acm->oldcount = acm->iocount;
+
+		if (difference & ACM_CTRL_DSR)
+			acm->iocount.dsr++;
+		if (difference & ACM_CTRL_DCD)
+			acm->iocount.dcd++;
+		if (newctrl & ACM_CTRL_BRK)
+			acm->iocount.brk++;
+		if (newctrl & ACM_CTRL_RI)
+			acm->iocount.rng++;
+		if (newctrl & ACM_CTRL_FRAMING)
+			acm->iocount.frame++;
+		if (newctrl & ACM_CTRL_PARITY)
+			acm->iocount.parity++;
+		if (newctrl & ACM_CTRL_OVERRUN)
+			acm->iocount.overrun++;
+		spin_unlock_irqrestore(&acm->read_lock, flags);
+
+		if (difference)
+			wake_up_all(&acm->wioctl);
+
+		break;
+
+	default:
+		dev_dbg(&acm->control->dev,
+			"%s - unknown notification %d received: index %d len %d\n",
+			__func__,
+			dr->bNotificationType, dr->wIndex, dr->wLength);
+	}
+}
+
+/* control interface reports status changes with "interrupt" transfers */
+static void acm_ctrl_irq(struct urb *urb)
+{
+	struct acm *acm = urb->context;
+	struct usb_cdc_notification *dr = urb->transfer_buffer;
+	unsigned int current_size = urb->actual_length;
+	unsigned int expected_size, copy_size, alloc_size;
+	int retval;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dev_dbg(&acm->control->dev,
+			"%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&acm->control->dev,
+			"%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	usb_mark_last_busy(acm->dev);
+
+	if (acm->nb_index)
+		dr = (struct usb_cdc_notification *)acm->notification_buffer;
+
+	/* size = notification-header + (optional) data */
+	expected_size = sizeof(struct usb_cdc_notification) +
+					le16_to_cpu(dr->wLength);
+
+	if (current_size < expected_size) {
+		/* notification is transmitted fragmented, reassemble */
+		if (acm->nb_size < expected_size) {
+			if (acm->nb_size) {
+				kfree(acm->notification_buffer);
+				acm->nb_size = 0;
+			}
+			alloc_size = roundup_pow_of_two(expected_size);
+			/*
+			 * kmalloc ensures a valid notification_buffer after a
+			 * use of kfree in case the previous allocation was too
+			 * small. Final freeing is done on disconnect.
+			 */
+			acm->notification_buffer =
+				kmalloc(alloc_size, GFP_ATOMIC);
+			if (!acm->notification_buffer)
+				goto exit;
+			acm->nb_size = alloc_size;
+		}
+
+		copy_size = min(current_size,
+				expected_size - acm->nb_index);
+
+		memcpy(&acm->notification_buffer[acm->nb_index],
+		       urb->transfer_buffer, copy_size);
+		acm->nb_index += copy_size;
+		current_size = acm->nb_index;
+	}
+
+	if (current_size >= expected_size) {
+		/* notification complete */
+		acm_process_notification(acm, (unsigned char *)dr);
+		acm->nb_index = 0;
+	}
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval && retval != -EPERM)
+		dev_err(&acm->control->dev,
+			"%s - usb_submit_urb failed: %d\n", __func__, retval);
+}
+
+static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
+{
+	int res;
+
+	if (!test_and_clear_bit(index, &acm->read_urbs_free))
+		return 0;
+
+	res = usb_submit_urb(acm->read_urbs[index], mem_flags);
+	if (res) {
+		if (res != -EPERM && res != -ENODEV) {
+			dev_err(&acm->data->dev,
+				"urb %d failed submission with %d\n",
+				index, res);
+		}
+		set_bit(index, &acm->read_urbs_free);
+		return res;
+	} else {
+		dev_vdbg(&acm->data->dev, "submitted urb %d\n", index);
+	}
+
+	return 0;
+}
+
+static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
+{
+	int res;
+	int i;
+
+	for (i = 0; i < acm->rx_buflimit; ++i) {
+		res = acm_submit_read_urb(acm, i, mem_flags);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static void acm_process_read_urb(struct acm *acm, struct urb *urb)
+{
+	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;
+	int status = urb->status;
+
+	dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
+		rb->index, urb->actual_length, status);
+
+	set_bit(rb->index, &acm->read_urbs_free);
+
+	if (!acm->dev) {
+		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		usb_mark_last_busy(acm->dev);
+		acm_process_read_urb(acm, urb);
+		break;
+	case -EPIPE:
+		set_bit(EVENT_RX_STALL, &acm->flags);
+		schedule_work(&acm->work);
+		return;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		dev_dbg(&acm->data->dev,
+			"%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&acm->data->dev,
+			"%s - nonzero urb status received: %d\n",
+			__func__, status);
+		break;
+	}
+
+	/*
+	 * Unthrottle may run on another CPU which needs to see events
+	 * in the same order. Submission has an implict barrier
+	 */
+	smp_mb__before_atomic();
+
+	/* throttle device if requested by tty */
+	spin_lock_irqsave(&acm->read_lock, flags);
+	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);
+	}
+}
+
+/* data interface wrote those outgoing bytes */
+static void acm_write_bulk(struct urb *urb)
+{
+	struct acm_wb *wb = urb->context;
+	struct acm *acm = wb->instance;
+	unsigned long flags;
+	int status = urb->status;
+
+	if (status || (urb->actual_length != urb->transfer_buffer_length))
+		dev_vdbg(&acm->data->dev, "wrote len %d/%d, status %d\n",
+			urb->actual_length,
+			urb->transfer_buffer_length,
+			status);
+
+	spin_lock_irqsave(&acm->write_lock, flags);
+	acm_write_done(acm, wb);
+	spin_unlock_irqrestore(&acm->write_lock, flags);
+	set_bit(EVENT_TTY_WAKEUP, &acm->flags);
+	schedule_work(&acm->work);
+}
+
+static void acm_softint(struct work_struct *work)
+{
+	int i;
+	struct acm *acm = container_of(work, struct acm, work);
+
+	if (test_bit(EVENT_RX_STALL, &acm->flags)) {
+		if (!(usb_autopm_get_interface(acm->data))) {
+			for (i = 0; i < acm->rx_buflimit; i++)
+				usb_kill_urb(acm->read_urbs[i]);
+			usb_clear_halt(acm->dev, acm->in);
+			acm_submit_read_urbs(acm, GFP_KERNEL);
+			usb_autopm_put_interface(acm->data);
+		}
+		clear_bit(EVENT_RX_STALL, &acm->flags);
+	}
+
+	if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) {
+		tty_port_tty_wakeup(&acm->port);
+		clear_bit(EVENT_TTY_WAKEUP, &acm->flags);
+	}
+}
+
+/*
+ * TTY handlers
+ */
+
+static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct acm *acm;
+	int retval;
+
+	acm = acm_get_by_minor(tty->index);
+	if (!acm)
+		return -ENODEV;
+
+	retval = tty_standard_install(driver, tty);
+	if (retval)
+		goto error_init_termios;
+
+	tty->driver_data = acm;
+
+	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;
+
+	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
+		val = 0;
+
+	/* FIXME: add missing ctrlout locking throughout driver */
+	acm->ctrlout = val;
+
+	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");
+}
+
+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;
+
+	mutex_lock(&acm->mutex);
+	if (acm->disconnected)
+		goto disconnected;
+
+	retval = usb_autopm_get_interface(acm->control);
+	if (retval)
+		goto error_get_interface;
+
+	/*
+	 * 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;
+
+	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;
+	}
+
+	acm_tty_set_termios(tty, NULL);
+
+	/*
+	 * 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);
+
+	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);
+error_submit_urb:
+	usb_autopm_put_interface(acm->control);
+error_get_interface:
+disconnected:
+	mutex_unlock(&acm->mutex);
+
+	return usb_translate_errors(retval);
+}
+
+static void acm_port_destruct(struct tty_port *port)
+{
+	struct acm *acm = container_of(port, struct acm, port);
+
+	acm_release_minor(acm);
+	usb_put_intf(acm->control);
+	kfree(acm->country_codes);
+	kfree(acm);
+}
+
+static void acm_port_shutdown(struct tty_port *port)
+{
+	struct acm *acm = container_of(port, struct acm, port);
+	struct urb *urb;
+	struct acm_wb *wb;
+
+	/*
+	 * 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);
+	}
+
+	acm_kill_urbs(acm);
+}
+
+static void acm_tty_cleanup(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+
+	tty_port_put(&acm->port);
+}
+
+static void acm_tty_hangup(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+
+	tty_port_hangup(&acm->port);
+}
+
+static void acm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	struct acm *acm = tty->driver_data;
+
+	tty_port_close(&acm->port, tty, filp);
+}
+
+static int acm_tty_write(struct tty_struct *tty,
+					const unsigned char *buf, int count)
+{
+	struct acm *acm = tty->driver_data;
+	int stat;
+	unsigned long flags;
+	int wbn;
+	struct acm_wb *wb;
+
+	if (!count)
+		return 0;
+
+	dev_vdbg(&acm->data->dev, "%d bytes from tty layer\n", count);
+
+	spin_lock_irqsave(&acm->write_lock, flags);
+	wbn = acm_wb_alloc(acm);
+	if (wbn < 0) {
+		spin_unlock_irqrestore(&acm->write_lock, flags);
+		return 0;
+	}
+	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;
+	dev_vdbg(&acm->data->dev, "writing %d bytes\n", 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);
+
+	if (stat < 0)
+		return stat;
+	return count;
+}
+
+static int acm_tty_write_room(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	/*
+	 * Do not let the line discipline to know that we have a reserve,
+	 * or it might get too enthusiastic.
+	 */
+	return acm_wb_is_avail(acm) ? acm->writesize : 0;
+}
+
+static int acm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+	/*
+	 * 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.
+	 */
+	return (ACM_NW - acm_wb_is_avail(acm)) * acm->writesize;
+}
+
+static void acm_tty_throttle(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+
+	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;
+	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;
+
+	retval = acm_send_break(acm, state ? 0xffff : 0);
+	if (retval < 0)
+		dev_dbg(&acm->control->dev,
+			"%s - send break failed\n", __func__);
+	return retval;
+}
+
+static int acm_tty_tiocmget(struct tty_struct *tty)
+{
+	struct acm *acm = tty->driver_data;
+
+	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) |
+	       (acm->ctrlin  & ACM_CTRL_RI  ? TIOCM_RI  : 0) |
+	       (acm->ctrlin  & ACM_CTRL_DCD ? TIOCM_CD  : 0) |
+	       TIOCM_CTS;
+}
+
+static int acm_tty_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct acm *acm = tty->driver_data;
+	unsigned int newctrl;
+
+	newctrl = acm->ctrlout;
+	set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
+					(set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+	clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
+					(clear & TIOCM_RTS ? ACM_CTRL_RTS : 0);
+
+	newctrl = (newctrl & ~clear) | set;
+
+	if (acm->ctrlout == newctrl)
+		return 0;
+	return acm_set_control(acm, acm->ctrlout = newctrl);
+}
+
+static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
+{
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+	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 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;
+
+	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;
+
+	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 acm_tty_get_icount(struct tty_struct *tty,
+					struct serial_icounter_struct *icount)
+{
+	struct acm *acm = tty->driver_data;
+
+	icount->dsr = acm->iocount.dsr;
+	icount->rng = acm->iocount.rng;
+	icount->dcd = acm->iocount.dcd;
+	icount->frame = acm->iocount.frame;
+	icount->overrun = acm->iocount.overrun;
+	icount->parity = acm->iocount.parity;
+	icount->brk = acm->iocount.brk;
+
+	return 0;
+}
+
+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;
+	}
+
+	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 usb_cdc_line_coding newline;
+	int newctrl = acm->ctrlout;
+
+	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;
+	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);
+
+	if (C_BAUD(tty) == B0) {
+		newline.dwDTERate = acm->line.dwDTERate;
+		newctrl &= ~ACM_CTRL_DTR;
+	} else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
+		newctrl |=  ACM_CTRL_DTR;
+	}
+
+	if (newctrl != acm->ctrlout)
+		acm_set_control(acm, acm->ctrlout = newctrl);
+
+	if (memcmp(&acm->line, &newline, sizeof newline)) {
+		memcpy(&acm->line, &newline, sizeof newline);
+		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.
+ */
+
+/* Little helpers: write/read buffers free */
+static void acm_write_buffers_free(struct acm *acm)
+{
+	int i;
+	struct acm_wb *wb;
+
+	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++)
+		usb_free_coherent(acm->dev, acm->writesize, wb->buf, wb->dmah);
+}
+
+static void acm_read_buffers_free(struct acm *acm)
+{
+	int i;
+
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_free_coherent(acm->dev, acm->readsize,
+			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
+}
+
+/* Little helper: write buffers allocate */
+static int acm_write_buffers_alloc(struct acm *acm)
+{
+	int i;
+	struct acm_wb *wb;
+
+	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) {
+		wb->buf = usb_alloc_coherent(acm->dev, acm->writesize, GFP_KERNEL,
+		    &wb->dmah);
+		if (!wb->buf) {
+			while (i != 0) {
+				--i;
+				--wb;
+				usb_free_coherent(acm->dev, acm->writesize,
+				    wb->buf, wb->dmah);
+			}
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+static int acm_probe(struct usb_interface *intf,
+		     const struct usb_device_id *id)
+{
+	struct usb_cdc_union_desc *union_header = NULL;
+	struct usb_cdc_call_mgmt_descriptor *cmgmd = NULL;
+	unsigned char *buffer = intf->altsetting->extra;
+	int buflen = intf->altsetting->extralen;
+	struct usb_interface *control_interface;
+	struct usb_interface *data_interface;
+	struct usb_endpoint_descriptor *epctrl = NULL;
+	struct usb_endpoint_descriptor *epread = NULL;
+	struct usb_endpoint_descriptor *epwrite = NULL;
+	struct usb_device *usb_dev = interface_to_usbdev(intf);
+	struct usb_cdc_parsed_header h;
+	struct acm *acm;
+	int minor;
+	int ctrlsize, readsize;
+	u8 *buf;
+	int call_intf_num = -1;
+	int data_intf_num = -1;
+	unsigned long quirks;
+	int num_rx_buf;
+	int i;
+	int combined_interfaces = 0;
+	struct device *tty_dev;
+	int rv = -ENOMEM;
+	int res;
+
+	/* normal quirks */
+	quirks = (unsigned long)id->driver_info;
+
+	if (quirks == IGNORE_DEVICE)
+		return -ENODEV;
+
+	memset(&h, 0x00, sizeof(struct usb_cdc_parsed_header));
+
+	num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : ACM_NR;
+
+	/* handle quirks deadly to normal probing*/
+	if (quirks == NO_UNION_NORMAL) {
+		data_interface = usb_ifnum_to_if(usb_dev, 1);
+		control_interface = usb_ifnum_to_if(usb_dev, 0);
+		/* we would crash */
+		if (!data_interface || !control_interface)
+			return -ENODEV;
+		goto skip_normal_probe;
+	}
+
+	/* normal probing*/
+	if (!buffer) {
+		dev_err(&intf->dev, "Weird descriptor references\n");
+		return -EINVAL;
+	}
+
+	if (!intf->cur_altsetting)
+		return -EINVAL;
+
+	if (!buflen) {
+		if (intf->cur_altsetting->endpoint &&
+				intf->cur_altsetting->endpoint->extralen &&
+				intf->cur_altsetting->endpoint->extra) {
+			dev_dbg(&intf->dev,
+				"Seeking extra descriptors on endpoint\n");
+			buflen = intf->cur_altsetting->endpoint->extralen;
+			buffer = intf->cur_altsetting->endpoint->extra;
+		} else {
+			dev_err(&intf->dev,
+				"Zero length descriptor references\n");
+			return -EINVAL;
+		}
+	}
+
+	cdc_parse_cdc_header(&h, intf, buffer, buflen);
+	union_header = h.usb_cdc_union_desc;
+	cmgmd = h.usb_cdc_call_mgmt_descriptor;
+	if (cmgmd)
+		call_intf_num = cmgmd->bDataInterface;
+
+	if (!union_header) {
+		if (call_intf_num > 0) {
+			dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
+			/* quirks for Droids MuIn LCD */
+			if (quirks & NO_DATA_INTERFACE) {
+				data_interface = usb_ifnum_to_if(usb_dev, 0);
+			} else {
+				data_intf_num = call_intf_num;
+				data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
+			}
+			control_interface = intf;
+		} else {
+			if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
+				dev_dbg(&intf->dev,"No union descriptor, giving up\n");
+				return -ENODEV;
+			} else {
+				dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
+				combined_interfaces = 1;
+				control_interface = data_interface = intf;
+				goto look_for_collapsed_interface;
+			}
+		}
+	} else {
+		data_intf_num = union_header->bSlaveInterface0;
+		control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
+		data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
+	}
+
+	if (!control_interface || !data_interface) {
+		dev_dbg(&intf->dev, "no interfaces\n");
+		return -ENODEV;
+	}
+	if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
+		return -ENODEV;
+
+	if (data_intf_num != call_intf_num)
+		dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
+
+	if (control_interface == data_interface) {
+		/* some broken devices designed for windows work this way */
+		dev_warn(&intf->dev,"Control and data interfaces are not separated!\n");
+		combined_interfaces = 1;
+		/* a popular other OS doesn't use it */
+		quirks |= NO_CAP_LINE;
+		if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) {
+			dev_err(&intf->dev, "This needs exactly 3 endpoints\n");
+			return -EINVAL;
+		}
+look_for_collapsed_interface:
+		res = usb_find_common_endpoints(data_interface->cur_altsetting,
+				&epread, &epwrite, &epctrl, NULL);
+		if (res)
+			return res;
+
+		goto made_compressed_probe;
+	}
+
+skip_normal_probe:
+
+	/*workaround for switched interfaces */
+	if (data_interface->cur_altsetting->desc.bInterfaceClass
+						!= CDC_DATA_INTERFACE_TYPE) {
+		if (control_interface->cur_altsetting->desc.bInterfaceClass
+						== CDC_DATA_INTERFACE_TYPE) {
+			dev_dbg(&intf->dev,
+				"Your device has switched interfaces.\n");
+			swap(control_interface, data_interface);
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	/* Accept probe requests only for the control interface */
+	if (!combined_interfaces && intf != control_interface)
+		return -ENODEV;
+
+	if (!combined_interfaces && usb_interface_claimed(data_interface)) {
+		/* valid in this context */
+		dev_dbg(&intf->dev, "The data interface isn't available\n");
+		return -EBUSY;
+	}
+
+
+	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;
+	epread = &data_interface->cur_altsetting->endpoint[0].desc;
+	epwrite = &data_interface->cur_altsetting->endpoint[1].desc;
+
+
+	/* workaround for switched endpoints */
+	if (!usb_endpoint_dir_in(epread)) {
+		/* descriptors are swapped */
+		dev_dbg(&intf->dev,
+			"The data interface has switched endpoints\n");
+		swap(epread, epwrite);
+	}
+made_compressed_probe:
+	dev_dbg(&intf->dev, "interfaces are valid\n");
+
+	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
+	if (acm == NULL)
+		goto alloc_fail;
+
+	tty_port_init(&acm->port);
+	acm->port.ops = &acm_port_ops;
+
+	minor = acm_alloc_minor(acm);
+	if (minor < 0)
+		goto alloc_fail1;
+
+	ctrlsize = usb_endpoint_maxp(epctrl);
+	readsize = usb_endpoint_maxp(epread) *
+				(quirks == SINGLE_RX_URB ? 1 : 2);
+	acm->combined_interfaces = combined_interfaces;
+	acm->writesize = usb_endpoint_maxp(epwrite) * 20;
+	acm->control = control_interface;
+	acm->data = data_interface;
+	acm->minor = minor;
+	acm->dev = usb_dev;
+	if (h.usb_cdc_acm_descriptor)
+		acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
+	if (quirks & NO_CAP_LINE)
+		acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
+	acm->ctrlsize = ctrlsize;
+	acm->readsize = readsize;
+	acm->rx_buflimit = num_rx_buf;
+	INIT_WORK(&acm->work, acm_softint);
+	init_waitqueue_head(&acm->wioctl);
+	spin_lock_init(&acm->write_lock);
+	spin_lock_init(&acm->read_lock);
+	mutex_init(&acm->mutex);
+	if (usb_endpoint_xfer_int(epread)) {
+		acm->bInterval = epread->bInterval;
+		acm->in = usb_rcvintpipe(usb_dev, epread->bEndpointAddress);
+	} else {
+		acm->in = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
+	}
+	if (usb_endpoint_xfer_int(epwrite))
+		acm->out = usb_sndintpipe(usb_dev, epwrite->bEndpointAddress);
+	else
+		acm->out = usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress);
+	init_usb_anchor(&acm->delayed);
+	acm->quirks = quirks;
+
+	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
+	if (!buf)
+		goto alloc_fail1;
+	acm->ctrl_buffer = buf;
+
+	if (acm_write_buffers_alloc(acm) < 0)
+		goto alloc_fail2;
+
+	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!acm->ctrlurb)
+		goto alloc_fail3;
+
+	for (i = 0; i < num_rx_buf; i++) {
+		struct acm_rb *rb = &(acm->read_buffers[i]);
+		struct urb *urb;
+
+		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
+								&rb->dma);
+		if (!rb->base)
+			goto alloc_fail4;
+		rb->index = i;
+		rb->instance = acm;
+
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb)
+			goto alloc_fail4;
+
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		urb->transfer_dma = rb->dma;
+		if (usb_endpoint_xfer_int(epread))
+			usb_fill_int_urb(urb, acm->dev, acm->in, rb->base,
+					 acm->readsize,
+					 acm_read_bulk_callback, rb,
+					 acm->bInterval);
+		else
+			usb_fill_bulk_urb(urb, acm->dev, acm->in, 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)
+			goto alloc_fail5;
+
+		if (usb_endpoint_xfer_int(epwrite))
+			usb_fill_int_urb(snd->urb, usb_dev, acm->out,
+				NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval);
+		else
+			usb_fill_bulk_urb(snd->urb, usb_dev, acm->out,
+				NULL, acm->writesize, acm_write_bulk, snd);
+		snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		if (quirks & SEND_ZERO_PACKET)
+			snd->urb->transfer_flags |= URB_ZERO_PACKET;
+		snd->instance = acm;
+	}
+
+	usb_set_intfdata(intf, acm);
+
+	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
+	if (i < 0)
+		goto alloc_fail5;
+
+	if (h.usb_cdc_country_functional_desc) { /* export the country data */
+		struct usb_cdc_country_functional_desc * cfd =
+					h.usb_cdc_country_functional_desc;
+
+		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
+		if (!acm->country_codes)
+			goto skip_countries;
+		acm->country_code_size = cfd->bLength - 4;
+		memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0,
+							cfd->bLength - 4);
+		acm->country_rel_date = cfd->iCountryCodeRelDate;
+
+		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;
+		}
+
+		i = device_create_file(&intf->dev,
+						&dev_attr_iCountryCodeRelDate);
+		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;
+		}
+	}
+
+skip_countries:
+	usb_fill_int_urb(acm->ctrlurb, usb_dev,
+			 usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
+			 acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm,
+			 /* works around buggy devices */
+			 epctrl->bInterval ? epctrl->bInterval : 16);
+	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
+	acm->notification_buffer = NULL;
+	acm->nb_index = 0;
+	acm->nb_size = 0;
+
+	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
+
+	acm->line.dwDTERate = cpu_to_le32(9600);
+	acm->line.bDataBits = 8;
+	acm_set_line(acm, &acm->line);
+
+	usb_driver_claim_interface(&acm_driver, data_interface, acm);
+	usb_set_intfdata(data_interface, acm);
+
+	usb_get_intf(control_interface);
+	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_fail6;
+	}
+
+	if (quirks & CLEAR_HALT_CONDITIONS) {
+		usb_clear_halt(usb_dev, acm->in);
+		usb_clear_halt(usb_dev, acm->out);
+	}
+
+	return 0;
+alloc_fail6:
+	if (acm->country_codes) {
+		device_remove_file(&acm->control->dev,
+				&dev_attr_wCountryCodes);
+		device_remove_file(&acm->control->dev,
+				&dev_attr_iCountryCodeRelDate);
+		kfree(acm->country_codes);
+	}
+	device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
+alloc_fail5:
+	usb_set_intfdata(intf, NULL);
+	for (i = 0; i < ACM_NW; i++)
+		usb_free_urb(acm->wb[i].urb);
+alloc_fail4:
+	for (i = 0; i < num_rx_buf; i++)
+		usb_free_urb(acm->read_urbs[i]);
+	acm_read_buffers_free(acm);
+	usb_free_urb(acm->ctrlurb);
+alloc_fail3:
+	acm_write_buffers_free(acm);
+alloc_fail2:
+	usb_free_coherent(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+alloc_fail1:
+	tty_port_put(&acm->port);
+alloc_fail:
+	return rv;
+}
+
+static void acm_disconnect(struct usb_interface *intf)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+	struct tty_struct *tty;
+	int i;
+
+	/* sibling interface is already cleaning up */
+	if (!acm)
+		return;
+
+	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);
+	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);
+	}
+
+	acm_kill_urbs(acm);
+	cancel_work_sync(&acm->work);
+
+	tty_unregister_device(acm_tty_driver, acm->minor);
+
+	usb_free_urb(acm->ctrlurb);
+	for (i = 0; i < ACM_NW; i++)
+		usb_free_urb(acm->wb[i].urb);
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_free_urb(acm->read_urbs[i]);
+	acm_write_buffers_free(acm);
+	usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
+	acm_read_buffers_free(acm);
+
+	kfree(acm->notification_buffer);
+
+	if (!acm->combined_interfaces)
+		usb_driver_release_interface(&acm_driver, intf == acm->control ?
+					acm->data : acm->control);
+
+	tty_port_put(&acm->port);
+}
+
+#ifdef CONFIG_PM
+static int acm_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+	int cnt;
+
+	spin_lock_irq(&acm->write_lock);
+	if (PMSG_IS_AUTO(message)) {
+		if (acm->transmitting) {
+			spin_unlock_irq(&acm->write_lock);
+			return -EBUSY;
+		}
+	}
+	cnt = acm->susp_count++;
+	spin_unlock_irq(&acm->write_lock);
+
+	if (cnt)
+		return 0;
+
+	acm_kill_urbs(acm);
+	cancel_work_sync(&acm->work);
+
+	return 0;
+}
+
+static int acm_resume(struct usb_interface *intf)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+	struct urb *urb;
+	int rv = 0;
+
+	spin_lock_irq(&acm->write_lock);
+
+	if (--acm->susp_count)
+		goto out;
+
+	if (tty_port_initialized(&acm->port)) {
+		rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
+
+		for (;;) {
+			urb = usb_get_from_anchor(&acm->delayed);
+			if (!urb)
+				break;
+
+			acm_start_wb(acm, urb->context);
+		}
+
+		/*
+		 * delayed error checking because we must
+		 * do the write path at all cost
+		 */
+		if (rv < 0)
+			goto out;
+
+		rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
+	}
+out:
+	spin_unlock_irq(&acm->write_lock);
+
+	return rv;
+}
+
+static int acm_reset_resume(struct usb_interface *intf)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+
+	if (tty_port_initialized(&acm->port))
+		tty_port_tty_hangup(&acm->port, false);
+
+	return acm_resume(intf);
+}
+
+#endif /* CONFIG_PM */
+
+static int acm_pre_reset(struct usb_interface *intf)
+{
+	struct acm *acm = usb_get_intfdata(intf);
+
+	clear_bit(EVENT_RX_STALL, &acm->flags);
+	acm->nb_index = 0; /* pending control transfers are lost */
+
+	return 0;
+}
+
+#define NOKIA_PCSUITE_ACM_INFO(x) \
+		USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
+		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
+		USB_CDC_ACM_PROTO_VENDOR)
+
+#define SAMSUNG_PCSUITE_ACM_INFO(x) \
+		USB_DEVICE_AND_INTERFACE_INFO(0x04e7, x, \
+		USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
+		USB_CDC_ACM_PROTO_VENDOR)
+
+/*
+ * USB driver structure.
+ */
+
+static const struct usb_device_id acm_ids[] = {
+	/* quirky and broken devices */
+	{ USB_DEVICE(0x076d, 0x0006), /* Denso Cradle CU-321 */
+	.driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
+	{ 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 */
+	},
+	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x079b, 0x000f), /* BT On-Air USB MODEM */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0ace, 0x1602), /* ZyDAS 56K USB MODEM */
+	.driver_info = SINGLE_RX_URB,
+	},
+	{ USB_DEVICE(0x0ace, 0x1608), /* ZyDAS 56K USB MODEM */
+	.driver_info = SINGLE_RX_URB, /* firmware bug */
+	},
+	{ USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */
+	.driver_info = SINGLE_RX_URB, /* firmware bug */
+	},
+	{ USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */
+	.driver_info = SINGLE_RX_URB,
+	},
+	{ USB_DEVICE(0x1965, 0x0018), /* Uniden UBC125XLT */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0803, 0x3095), /* Zoom Telephonics Model 3095F USB MODEM */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
+	{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
+	.driver_info = QUIRK_CONTROL_LINE_STATE, },
+	{ USB_DEVICE(0x2184, 0x001c) },	/* GW Instek AFG-2225 */
+	{ USB_DEVICE(0x2184, 0x0036) },	/* GW Instek AFG-125 */
+	{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
+	},
+	/* Motorola H24 HSPA module: */
+	{ 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
+					   communications interface.
+					   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 */
+	},
+	{ USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */
+	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+	},
+	{ USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */
+	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+	},
+	{ USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */
+	.driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */
+	},
+	{ USB_DEVICE(0x0ca6, 0xa050), /* Castles VEGA3000 */
+	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
+	},
+
+	{ USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
+	.driver_info = CLEAR_HALT_CONDITIONS,
+	},
+
+	/* Nokia S60 phones expose two ACM channels. The first is
+	 * a modem and is picked up by the standard AT-command
+	 * information below. The second is 'vendor-specific' but
+	 * is treated as a serial device at the S60 end, so we want
+	 * to expose it on Linux too. */
+	{ NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
+	{ NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i  */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
+	{ NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
+	{ NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic &  */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB  */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3  */
+	{ NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
+	{ NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x02e3), }, /* Nokia 5230, RM-588 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0178), }, /* Nokia E63 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x010e), }, /* Nokia E75 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x02d9), }, /* Nokia 6760 Slide */
+	{ NOKIA_PCSUITE_ACM_INFO(0x01d0), }, /* Nokia E52 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0223), }, /* Nokia E72 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x0275), }, /* Nokia X6 */
+	{ NOKIA_PCSUITE_ACM_INFO(0x026c), }, /* Nokia N97 Mini */
+	{ 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 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
+
+	/*Samsung phone in firmware update mode */
+	{ USB_DEVICE(0x04e8, 0x685d),
+	.driver_info = IGNORE_DEVICE,
+	},
+
+	/* Exclude Infineon Flash Loader utility */
+	{ USB_DEVICE(0x058b, 0x0041),
+	.driver_info = IGNORE_DEVICE,
+	},
+
+	/* control interfaces without any protocol set */
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_PROTO_NONE) },
+
+	/* control interfaces with various AT-command sets */
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_V25TER) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_PCCA101) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_GSM) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_3G) },
+	{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
+		USB_CDC_ACM_PROTO_AT_CDMA) },
+
+	{ USB_DEVICE(0x1519, 0x0452), /* Intel 7260 modem */
+	.driver_info = SEND_ZERO_PACKET,
+	},
+
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, acm_ids);
+
+static struct usb_driver acm_driver = {
+	.name =		"cdc_acm",
+	.probe =	acm_probe,
+	.disconnect =	acm_disconnect,
+#ifdef CONFIG_PM
+	.suspend =	acm_suspend,
+	.resume =	acm_resume,
+	.reset_resume =	acm_reset_resume,
+#endif
+	.pre_reset =	acm_pre_reset,
+	.id_table =	acm_ids,
+#ifdef CONFIG_PM
+	.supports_autosuspend = 1,
+#endif
+	.disable_hub_initiated_lpm = 1,
+};
+
+/*
+ * TTY driver structures.
+ */
+
+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,
+	.ioctl =		acm_tty_ioctl,
+	.throttle =		acm_tty_throttle,
+	.unthrottle =		acm_tty_unthrottle,
+	.chars_in_buffer =	acm_tty_chars_in_buffer,
+	.break_ctl =		acm_tty_break_ctl,
+	.set_termios =		acm_tty_set_termios,
+	.tiocmget =		acm_tty_tiocmget,
+	.tiocmset =		acm_tty_tiocmset,
+	.get_icount =		acm_tty_get_icount,
+};
+
+/*
+ * Init / exit.
+ */
+
+static int __init acm_init(void)
+{
+	int retval;
+	acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
+	if (!acm_tty_driver)
+		return -ENOMEM;
+	acm_tty_driver->driver_name = "acm",
+	acm_tty_driver->name = "ttyACM",
+	acm_tty_driver->major = ACM_TTY_MAJOR,
+	acm_tty_driver->minor_start = 0,
+	acm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
+	acm_tty_driver->subtype = SERIAL_TYPE_NORMAL,
+	acm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	acm_tty_driver->init_termios = tty_std_termios;
+	acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
+								HUPCL | CLOCAL;
+	tty_set_operations(acm_tty_driver, &acm_ops);
+
+	retval = tty_register_driver(acm_tty_driver);
+	if (retval) {
+		put_tty_driver(acm_tty_driver);
+		return retval;
+	}
+
+	retval = usb_register(&acm_driver);
+	if (retval) {
+		tty_unregister_driver(acm_tty_driver);
+		put_tty_driver(acm_tty_driver);
+		return retval;
+	}
+
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
+
+	return 0;
+}
+
+static void __exit acm_exit(void)
+{
+	usb_deregister(&acm_driver);
+	tty_unregister_driver(acm_tty_driver);
+	put_tty_driver(acm_tty_driver);
+	idr_destroy(&acm_minors);
+}
+
+module_init(acm_init);
+module_exit(acm_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(ACM_TTY_MAJOR);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
new file mode 100644
index 0000000..ca06b20
--- /dev/null
+++ b/drivers/usb/class/cdc-acm.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *
+ * Includes for cdc-acm.c
+ *
+ * Mainly take from usbnet's cdc-ether part
+ *
+ */
+
+/*
+ * CMSPAR, some architectures can't have space and mark parity.
+ */
+
+#ifndef CMSPAR
+#define CMSPAR			0
+#endif
+
+/*
+ * Major and minor numbers.
+ */
+
+#define ACM_TTY_MAJOR		166
+#define ACM_TTY_MINORS		256
+
+/*
+ * Requests.
+ */
+
+#define USB_RT_ACM		(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+/*
+ * Output control lines.
+ */
+
+#define ACM_CTRL_DTR		0x01
+#define ACM_CTRL_RTS		0x02
+
+/*
+ * Input control lines and line errors.
+ */
+
+#define ACM_CTRL_DCD		0x01
+#define ACM_CTRL_DSR		0x02
+#define ACM_CTRL_BRK		0x04
+#define ACM_CTRL_RI		0x08
+
+#define ACM_CTRL_FRAMING	0x10
+#define ACM_CTRL_PARITY		0x20
+#define ACM_CTRL_OVERRUN	0x40
+
+/*
+ * Internal driver structures.
+ */
+
+/*
+ * 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
+ * when processing onlcr, so we only need 2 buffers. These values must be
+ * powers of 2.
+ */
+#define ACM_NW  16
+#define ACM_NR  16
+
+struct acm_wb {
+	unsigned char *buf;
+	dma_addr_t dmah;
+	int len;
+	int use;
+	struct urb		*urb;
+	struct acm		*instance;
+};
+
+struct acm_rb {
+	int			size;
+	unsigned char		*base;
+	dma_addr_t		dma;
+	int			index;
+	struct acm		*instance;
+};
+
+struct acm {
+	struct usb_device *dev;				/* the corresponding usb device */
+	struct usb_interface *control;			/* control interface */
+	struct usb_interface *data;			/* data interface */
+	unsigned in, out;				/* i/o pipes */
+	struct tty_port port;			 	/* our tty port data */
+	struct urb *ctrlurb;				/* urbs */
+	u8 *ctrl_buffer;				/* buffers of urbs */
+	dma_addr_t ctrl_dma;				/* dma handles of buffers */
+	u8 *country_codes;				/* country codes from device */
+	unsigned int country_code_size;			/* size of this buffer */
+	unsigned int country_rel_date;			/* release date of version */
+	struct acm_wb wb[ACM_NW];
+	unsigned long read_urbs_free;
+	struct urb *read_urbs[ACM_NR];
+	struct acm_rb read_buffers[ACM_NR];
+	int rx_buflimit;
+	spinlock_t read_lock;
+	u8 *notification_buffer;			/* to reassemble fragmented notifications */
+	unsigned int nb_index;
+	unsigned int nb_size;
+	int transmitting;
+	spinlock_t write_lock;
+	struct mutex mutex;
+	bool disconnected;
+	unsigned long flags;
+#		define EVENT_TTY_WAKEUP	0
+#		define EVENT_RX_STALL	1
+	struct usb_cdc_line_coding line;		/* bits, stop, parity */
+	struct work_struct work;			/* work queue entry for line discipline waking up */
+	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 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 throttled:1;			/* actually throttled */
+	unsigned int throttle_req:1;			/* throttle requested */
+	u8 bInterval;
+	struct usb_anchor delayed;			/* writes queued for a device about to be woken */
+	unsigned long quirks;
+};
+
+#define CDC_DATA_INTERFACE_TYPE	0x0a
+
+/* constants describing various quirks and errors */
+#define NO_UNION_NORMAL			BIT(0)
+#define SINGLE_RX_URB			BIT(1)
+#define NO_CAP_LINE			BIT(2)
+#define NO_DATA_INTERFACE		BIT(4)
+#define IGNORE_DEVICE			BIT(5)
+#define QUIRK_CONTROL_LINE_STATE	BIT(6)
+#define CLEAR_HALT_CONDITIONS		BIT(7)
+#define SEND_ZERO_PACKET		BIT(8)
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
new file mode 100644
index 0000000..bec581f
--- /dev/null
+++ b/drivers/usb/class/cdc-wdm.c
@@ -0,0 +1,1125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cdc-wdm.c
+ *
+ * This driver supports USB CDC WCM Device Management.
+ *
+ * Copyright (c) 2007-2009 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/ioctl.h>
+#include <linux/slab.h>
+#include <linux/module.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>
+#include <linux/usb/cdc-wdm.h>
+
+#define DRIVER_AUTHOR "Oliver Neukum"
+#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
+
+static const 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
+	},
+	{ }
+};
+
+MODULE_DEVICE_TABLE (usb, wdm_ids);
+
+#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_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 --- */
+
+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;
+	__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;
+	struct work_struct	service_outs_intr;
+	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)
+{
+	struct wdm_device *desc;
+	unsigned long flags;
+
+	desc = urb->context;
+	spin_lock_irqsave(&desc->iuspin, flags);
+	desc->werr = urb->status;
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+	kfree(desc->outbuf);
+	desc->outbuf = NULL;
+	clear_bit(WDM_IN_USE, &desc->flags);
+	wake_up(&desc->wait);
+}
+
+static void wdm_in_callback(struct urb *urb)
+{
+	unsigned long flags;
+	struct wdm_device *desc = urb->context;
+	int status = urb->status;
+	int length = urb->actual_length;
+
+	spin_lock_irqsave(&desc->iuspin, flags);
+	clear_bit(WDM_RESPONDING, &desc->flags);
+
+	if (status) {
+		switch (status) {
+		case -ENOENT:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ENOENT\n");
+			goto skip_error;
+		case -ECONNRESET:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ECONNRESET\n");
+			goto skip_error;
+		case -ESHUTDOWN:
+			dev_dbg(&desc->intf->dev,
+				"nonzero urb status received: -ESHUTDOWN\n");
+			goto skip_error;
+		case -EPIPE:
+			dev_err(&desc->intf->dev,
+				"nonzero urb status received: -EPIPE\n");
+			break;
+		default:
+			dev_err(&desc->intf->dev,
+				"Unexpected error %d\n", status);
+			break;
+		}
+	}
+
+	/*
+	 * only set a new error if there is no previous error.
+	 * Errors are only cleared during read/open
+	 * Avoid propagating -EPIPE (stall) to userspace since it is
+	 * better handled as an empty read
+	 */
+	if (desc->rerr == 0 && status != -EPIPE)
+		desc->rerr = status;
+
+	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:
+
+	if (desc->rerr) {
+		/*
+		 * Since there was an error, userspace may decide to not read
+		 * any data after poll'ing.
+		 * We should respond to further attempts from the device to send
+		 * data, so that we can get unstuck.
+		 */
+		schedule_work(&desc->service_outs_intr);
+	} else {
+		set_bit(WDM_READ, &desc->flags);
+		wake_up(&desc->wait);
+	}
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+}
+
+static void wdm_int_callback(struct urb *urb)
+{
+	unsigned long flags;
+	int rv = 0;
+	int responding;
+	int status = urb->status;
+	struct wdm_device *desc;
+	struct usb_cdc_notification *dr;
+
+	desc = urb->context;
+	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);
+			dev_err(&desc->intf->dev, "Stall on int endpoint\n");
+			goto sw; /* halt is cleared in work */
+		default:
+			dev_err(&desc->intf->dev,
+				"nonzero urb status received: %d\n", status);
+			break;
+		}
+	}
+
+	if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
+		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
+			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\n",
+			le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength));
+		break;
+
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+
+		dev_dbg(&desc->intf->dev,
+			"NOTIFY_NETWORK_CONNECTION %s network\n",
+			dr->wValue ? "connected to" : "disconnected from");
+		goto exit;
+	case USB_CDC_NOTIFY_SPEED_CHANGE:
+		dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n",
+			urb->actual_length);
+		goto exit;
+	default:
+		clear_bit(WDM_POLL_RUNNING, &desc->flags);
+		dev_err(&desc->intf->dev,
+			"unknown notification %d received: index %d len %d\n",
+			dr->bNotificationType,
+			le16_to_cpu(dr->wIndex),
+			le16_to_cpu(dr->wLength));
+		goto exit;
+	}
+
+	spin_lock_irqsave(&desc->iuspin, 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, "submit response URB %d\n", rv);
+	}
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+	if (rv < 0) {
+		clear_bit(WDM_RESPONDING, &desc->flags);
+		if (rv == -EPERM)
+			return;
+		if (rv == -ENOMEM) {
+sw:
+			rv = schedule_work(&desc->rxwork);
+			if (rv)
+				dev_err(&desc->intf->dev,
+					"Cannot schedule work\n");
+		}
+	}
+exit:
+	rv = usb_submit_urb(urb, GFP_ATOMIC);
+	if (rv)
+		dev_err(&desc->intf->dev,
+			"%s - usb_submit_urb failed with result %d\n",
+			__func__, rv);
+
+}
+
+static void kill_urbs(struct wdm_device *desc)
+{
+	/* the order here is essential */
+	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)
+{
+	kfree(desc->sbuf);
+	kfree(desc->inbuf);
+	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 usb_translate_errors(we);
+
+	buf = memdup_user(buffer, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	/* concurrent writes and disconnect */
+	r = mutex_lock_interruptible(&desc->wlock);
+	rv = -ERESTARTSYS;
+	if (r)
+		goto out_free_mem;
+
+	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		rv = -ENODEV;
+		goto out_free_mem_lock;
+	}
+
+	r = usb_autopm_get_interface(desc->intf);
+	if (r < 0) {
+		rv = usb_translate_errors(r);
+		goto out_free_mem_lock;
+	}
+
+	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) {
+		rv = r;
+		goto out_free_mem_pm;
+	}
+
+	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; /* already converted */
+	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) {
+		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);
+		goto out_free_mem_pm;
+	} else {
+		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n",
+			le16_to_cpu(req->wIndex));
+	}
+
+	usb_autopm_put_interface(desc->intf);
+	mutex_unlock(&desc->wlock);
+	return count;
+
+out_free_mem_pm:
+	usb_autopm_put_interface(desc->intf);
+out_free_mem_lock:
+	mutex_unlock(&desc->wlock);
+out_free_mem:
+	kfree(buf);
+	return rv;
+}
+
+/*
+ * Submit the read urb if resp_count is non-zero.
+ *
+ * Called with desc->iuspin locked
+ */
+static int service_outstanding_interrupt(struct wdm_device *desc)
+{
+	int rv = 0;
+
+	/* 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;
+	int i = 0;
+	struct wdm_device *desc = file->private_data;
+
+
+	rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
+	if (rv < 0)
+		return -ERESTARTSYS;
+
+	cntr = READ_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)) {
+				rv = -EAGAIN;
+				goto err;
+			}
+			rv = 0;
+		} else {
+			rv = wait_event_interruptible(desc->wait,
+				test_bit(WDM_READ, &desc->flags));
+		}
+
+		/* may have happened while we slept */
+		if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+			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;
+			goto err;
+		}
+
+		spin_lock_irq(&desc->iuspin);
+
+		if (desc->rerr) { /* read completed, error happened */
+			rv = usb_translate_errors(desc->rerr);
+			desc->rerr = 0;
+			spin_unlock_irq(&desc->iuspin);
+			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 */
+			dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n");
+			clear_bit(WDM_READ, &desc->flags);
+			rv = service_outstanding_interrupt(desc);
+			spin_unlock_irq(&desc->iuspin);
+			if (rv < 0)
+				goto err;
+			goto retry;
+		}
+		cntr = desc->length;
+		spin_unlock_irq(&desc->iuspin);
+	}
+
+	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);
+		service_outstanding_interrupt(desc);
+	}
+	spin_unlock_irq(&desc->iuspin);
+	rv = cntr;
+
+err:
+	mutex_unlock(&desc->rlock);
+	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));
+
+	/* 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 usb_translate_errors(desc->werr);
+}
+
+static __poll_t wdm_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct wdm_device *desc = file->private_data;
+	unsigned long flags;
+	__poll_t mask = 0;
+
+	spin_lock_irqsave(&desc->iuspin, flags);
+	if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+		mask = EPOLLHUP | EPOLLERR;
+		spin_unlock_irqrestore(&desc->iuspin, flags);
+		goto desc_out;
+	}
+	if (test_bit(WDM_READ, &desc->flags))
+		mask = EPOLLIN | EPOLLRDNORM;
+	if (desc->rerr || desc->werr)
+		mask |= EPOLLERR;
+	if (!test_bit(WDM_IN_USE, &desc->flags))
+		mask |= EPOLLOUT | EPOLLWRNORM;
+	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);
+	desc = wdm_find_device_by_minor(minor);
+	if (!desc)
+		goto out;
+
+	intf = desc->intf;
+	if (test_bit(WDM_DISCONNECTING, &desc->flags))
+		goto out;
+	file->private_data = desc;
+
+	rv = usb_autopm_get_interface(desc->intf);
+	if (rv < 0) {
+		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
+		goto out;
+	}
+
+	/* 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->wlock);
+	if (desc->count == 1)
+		desc->manage_power(intf, 1);
+	usb_autopm_put_interface(desc->intf);
+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);
+
+	/* using write lock to protect desc->count */
+	mutex_lock(&desc->wlock);
+	desc->count--;
+	mutex_unlock(&desc->wlock);
+
+	if (!desc->count) {
+		if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
+			dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
+			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,
+	.write =	wdm_write,
+	.open =		wdm_open,
+	.flush =	wdm_flush,
+	.release =	wdm_release,
+	.poll =		wdm_poll,
+	.unlocked_ioctl = wdm_ioctl,
+	.compat_ioctl = wdm_ioctl,
+	.llseek =	noop_llseek,
+};
+
+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 = 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);
+		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);
+		}
+	}
+}
+
+static void service_interrupt_work(struct work_struct *work)
+{
+	struct wdm_device *desc;
+
+	desc = container_of(work, struct wdm_device, service_outs_intr);
+
+	spin_lock_irq(&desc->iuspin);
+	service_outstanding_interrupt(desc);
+	if (!desc->resp_count) {
+		set_bit(WDM_READ, &desc->flags);
+		wake_up(&desc->wait);
+	}
+	spin_unlock_irq(&desc->iuspin);
+}
+
+/* --- hotplug --- */
+
+static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
+		u16 bufsize, int (*manage_power)(struct usb_interface *, int))
+{
+	int rv = -ENOMEM;
+	struct wdm_device *desc;
+
+	desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
+	if (!desc)
+		goto out;
+	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 = 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);
+	INIT_WORK(&desc->service_outs_intr, service_interrupt_work);
+
+	rv = -EINVAL;
+	if (!usb_endpoint_is_int_in(ep))
+		goto err;
+
+	desc->wMaxPacketSize = usb_endpoint_maxp(ep);
+
+	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 = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
+	if (!desc->sbuf)
+		goto err;
+
+	desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
+	if (!desc->inbuf)
+		goto err;
+
+	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->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; /* already converted */
+	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 err;
+	else
+		dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
+out:
+	return rv;
+err:
+	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_parsed_header hdr;
+	u8 *buffer = intf->altsetting->extra;
+	int buflen = intf->altsetting->extralen;
+	u16 maxcom = WDM_DEFAULT_BUFSIZE;
+
+	if (!buffer)
+		goto err;
+
+	cdc_parse_cdc_header(&hdr, intf, buffer, buflen);
+
+	if (hdr.usb_cdc_dmm_desc)
+		maxcom = le16_to_cpu(hdr.usb_cdc_dmm_desc->wMaxCommand);
+
+	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);
+
+	/* 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);
+	/* to terminate pending flushes */
+	clear_bit(WDM_IN_USE, &desc->flags);
+	spin_unlock_irqrestore(&desc->iuspin, flags);
+	wake_up_all(&desc->wait);
+	mutex_lock(&desc->rlock);
+	mutex_lock(&desc->wlock);
+	kill_urbs(desc);
+	cancel_work_sync(&desc->rxwork);
+	cancel_work_sync(&desc->service_outs_intr);
+	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, "%d open files - postponing cleanup\n", 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 = 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 (!PMSG_IS_AUTO(message)) {
+		mutex_lock(&desc->rlock);
+		mutex_lock(&desc->wlock);
+	}
+	spin_lock_irq(&desc->iuspin);
+
+	if (PMSG_IS_AUTO(message) &&
+			(test_bit(WDM_IN_USE, &desc->flags)
+			|| test_bit(WDM_RESPONDING, &desc->flags))) {
+		spin_unlock_irq(&desc->iuspin);
+		rv = -EBUSY;
+	} else {
+
+		set_bit(WDM_SUSPENDING, &desc->flags);
+		spin_unlock_irq(&desc->iuspin);
+		/* callback submits work - order is essential */
+		kill_urbs(desc);
+		cancel_work_sync(&desc->rxwork);
+		cancel_work_sync(&desc->service_outs_intr);
+	}
+	if (!PMSG_IS_AUTO(message)) {
+		mutex_unlock(&desc->wlock);
+		mutex_unlock(&desc->rlock);
+	}
+
+	return rv;
+}
+#endif
+
+static int recover_from_urb_loss(struct wdm_device *desc)
+{
+	int rv = 0;
+
+	if (desc->count) {
+		rv = usb_submit_urb(desc->validity, GFP_NOIO);
+		if (rv < 0)
+			dev_err(&desc->intf->dev,
+				"Error resume submitting int urb - %d\n", rv);
+	}
+	return rv;
+}
+
+#ifdef CONFIG_PM
+static int wdm_resume(struct usb_interface *intf)
+{
+	struct wdm_device *desc = wdm_find_device(intf);
+	int rv;
+
+	dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
+
+	clear_bit(WDM_SUSPENDING, &desc->flags);
+	rv = recover_from_urb_loss(desc);
+
+	return rv;
+}
+#endif
+
+static int wdm_pre_reset(struct usb_interface *intf)
+{
+	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);
+	cancel_work_sync(&desc->service_outs_intr);
+	return 0;
+}
+
+static int wdm_post_reset(struct usb_interface *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->wlock);
+	mutex_unlock(&desc->rlock);
+	return 0;
+}
+
+static struct usb_driver wdm_driver = {
+	.name =		"cdc_wdm",
+	.probe =	wdm_probe,
+	.disconnect =	wdm_disconnect,
+#ifdef CONFIG_PM
+	.suspend =	wdm_suspend,
+	.resume =	wdm_resume,
+	.reset_resume =	wdm_resume,
+#endif
+	.pre_reset =	wdm_pre_reset,
+	.post_reset =	wdm_post_reset,
+	.id_table =	wdm_ids,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(wdm_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
new file mode 100644
index 0000000..407a7a6
--- /dev/null
+++ b/drivers/usb/class/usblp.c
@@ -0,0 +1,1433 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * usblp.c
+ *
+ * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
+ * Copyright (c) 1999 Pavel Machek	<pavel@ucw.cz>
+ * Copyright (c) 2000 Randy Dunlap	<rdunlap@xenotime.net>
+ * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
+ # Copyright (c) 2001 Pete Zaitcev	<zaitcev@redhat.com>
+ # Copyright (c) 2001 David Paschal	<paschal@rcsis.com>
+ * Copyright (c) 2006 Oliver Neukum	<oliver@neukum.name>
+ *
+ * USB Printer Device Class driver for USB printers and printer cables
+ *
+ * Sponsored by SuSE
+ *
+ * ChangeLog:
+ *	v0.1 - thorough cleaning, URBification, almost a rewrite
+ *	v0.2 - some more cleanups
+ *	v0.3 - cleaner again, waitqueue fixes
+ *	v0.4 - fixes in unidirectional mode
+ *	v0.5 - add DEVICE_ID string support
+ *	v0.6 - never time out
+ *	v0.7 - fixed bulk-IN read and poll (David Paschal)
+ *	v0.8 - add devfs support
+ *	v0.9 - fix unplug-while-open paths
+ *	v0.10- remove sleep_on, fix error on oom (oliver@neukum.org)
+ *	v0.11 - add proto_bias option (Pete Zaitcev)
+ *	v0.12 - add hpoj.sourceforge.net ioctls (David Paschal)
+ *	v0.13 - alloc space for statusbuf (<status> not on stack);
+ *		use usb_alloc_coherent() for read buf & write buf;
+ *      none  - Maintained in Linux kernel after v0.13
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/signal.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/lp.h>
+#include <linux/mutex.h>
+#undef DEBUG
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/ratelimit.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal"
+#define DRIVER_DESC "USB Printer Device Class driver"
+
+#define USBLP_BUF_SIZE		8192
+#define USBLP_BUF_SIZE_IN	1024
+#define USBLP_DEVICE_ID_SIZE	1024
+
+/* ioctls: */
+#define IOCNR_GET_DEVICE_ID		1
+#define IOCNR_GET_PROTOCOLS		2
+#define IOCNR_SET_PROTOCOL		3
+#define IOCNR_HP_SET_CHANNEL		4
+#define IOCNR_GET_BUS_ADDRESS		5
+#define IOCNR_GET_VID_PID		6
+#define IOCNR_SOFT_RESET		7
+/* Get device_id string: */
+#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
+/* The following ioctls were added for http://hpoj.sourceforge.net:
+ * Get two-int array:
+ * [0]=current protocol
+ *     (1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2,
+ *         3=USB_CLASS_PRINTER/1/3),
+ * [1]=supported protocol mask (mask&(1<<n)!=0 means
+ *     USB_CLASS_PRINTER/1/n supported):
+ */
+#define LPIOC_GET_PROTOCOLS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_PROTOCOLS, len)
+/*
+ * Set protocol
+ *     (arg: 1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2,
+ *         3=USB_CLASS_PRINTER/1/3):
+ */
+#define LPIOC_SET_PROTOCOL _IOC(_IOC_WRITE, 'P', IOCNR_SET_PROTOCOL, 0)
+/* Set channel number (HP Vendor-specific command): */
+#define LPIOC_HP_SET_CHANNEL _IOC(_IOC_WRITE, 'P', IOCNR_HP_SET_CHANNEL, 0)
+/* Get two-int array: [0]=bus number, [1]=device address: */
+#define LPIOC_GET_BUS_ADDRESS(len) _IOC(_IOC_READ, 'P', IOCNR_GET_BUS_ADDRESS, len)
+/* Get two-int array: [0]=vendor ID, [1]=product ID: */
+#define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)
+/* Perform class specific soft reset */
+#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0);
+
+/*
+ * A DEVICE_ID string may include the printer's serial number.
+ * It should end with a semi-colon (';').
+ * An example from an HP 970C DeskJet printer is (this is one long string,
+ * with the serial number changed):
+MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ:                    ;
+ */
+
+/*
+ * USB Printer Requests
+ */
+
+#define USBLP_REQ_GET_ID			0x00
+#define USBLP_REQ_GET_STATUS			0x01
+#define USBLP_REQ_RESET				0x02
+#define USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST	0x00	/* HP Vendor-specific */
+
+#define USBLP_MINORS		16
+#define USBLP_MINOR_BASE	0
+
+#define USBLP_CTL_TIMEOUT	5000			/* 5 seconds */
+
+#define USBLP_FIRST_PROTOCOL	1
+#define USBLP_LAST_PROTOCOL	3
+#define USBLP_MAX_PROTOCOLS	(USBLP_LAST_PROTOCOL+1)
+
+/*
+ * some arbitrary status buffer size;
+ * need a status buffer that is allocated via kmalloc(), not on stack
+ */
+#define STATUS_BUF_SIZE		8
+
+/*
+ * Locks down the locking order:
+ * ->wmut locks wstatus.
+ * ->mut locks the whole usblp, except [rw]complete, and thus, by indirection,
+ * [rw]status. We only touch status when we know the side idle.
+ * ->lock locks what interrupt accesses.
+ */
+struct usblp {
+	struct usb_device	*dev;			/* USB device */
+	struct mutex		wmut;
+	struct mutex		mut;
+	spinlock_t		lock;		/* locks rcomplete, wcomplete */
+	char			*readbuf;		/* read transfer_buffer */
+	char			*statusbuf;		/* status transfer_buffer */
+	struct usb_anchor	urbs;
+	wait_queue_head_t	rwait, wwait;
+	int			readcount;		/* Counter for reads */
+	int			ifnum;			/* Interface number */
+	struct usb_interface	*intf;			/* The interface */
+	/*
+	 * Alternate-setting numbers and endpoints for each protocol
+	 * (USB_CLASS_PRINTER/1/{index=1,2,3}) that the device supports:
+	 */
+	struct {
+		int				alt_setting;
+		struct usb_endpoint_descriptor	*epwrite;
+		struct usb_endpoint_descriptor	*epread;
+	}			protocol[USBLP_MAX_PROTOCOLS];
+	int			current_protocol;
+	int			minor;			/* minor number of device */
+	int			wcomplete, rcomplete;
+	int			wstatus;	/* bytes written or error */
+	int			rstatus;	/* bytes ready or error */
+	unsigned int		quirks;			/* quirks flags */
+	unsigned int		flags;			/* mode flags */
+	unsigned char		used;			/* True if open */
+	unsigned char		present;		/* True if not disconnected */
+	unsigned char		bidir;			/* interface is bidirectional */
+	unsigned char		no_paper;		/* Paper Out happened */
+	unsigned char		*device_id_string;	/* IEEE 1284 DEVICE ID string (ptr) */
+							/* first 2 bytes are (big-endian) length */
+};
+
+#ifdef DEBUG
+static void usblp_dump(struct usblp *usblp)
+{
+	struct device *dev = &usblp->intf->dev;
+	int p;
+
+	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)");
+}
+#endif
+
+/* Quirks: various printer quirks are handled by this table & its flags. */
+
+struct quirk_printer_struct {
+	__u16 vendorId;
+	__u16 productId;
+	unsigned int quirks;
+};
+
+#define USBLP_QUIRK_BIDIR	0x1	/* reports bidir but requires unidirectional mode (no INs/reads) */
+#define USBLP_QUIRK_USB_INIT	0x2	/* needs vendor USB init string */
+#define USBLP_QUIRK_BAD_CLASS	0x4	/* descriptor uses vendor-specific Class or SubClass */
+
+static const struct quirk_printer_struct quirk_printers[] = {
+	{ 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */
+	{ 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */
+	{ 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */
+	{ 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
+	{ 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
+	{ 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
+	{ 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
+	{ 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
+	{ 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
+	{ 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
+	{ 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
+	{ 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */
+	{ 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */
+	{ 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut <kernel@zut.de> */
+	{ 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd HL-1440 Laser Printer */
+	{ 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */
+	{ 0, 0 }
+};
+
+static int usblp_wwait(struct usblp *usblp, int nonblock);
+static int usblp_wtest(struct usblp *usblp, int nonblock);
+static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock);
+static int usblp_rtest(struct usblp *usblp, int nonblock);
+static int usblp_submit_read(struct usblp *usblp);
+static int usblp_select_alts(struct usblp *usblp);
+static int usblp_set_protocol(struct usblp *usblp, int protocol);
+static int usblp_cache_device_id_string(struct usblp *usblp);
+
+/* forward reference to make our lives easier */
+static struct usb_driver usblp_driver;
+static DEFINE_MUTEX(usblp_mutex);	/* locks the existence of usblp's */
+
+/*
+ * Functions for usblp control messages.
+ */
+
+static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, int recip, int value, void *buf, int len)
+{
+	int retval;
+	int index = usblp->ifnum;
+
+	/* High byte has the interface index.
+	   Low byte has the alternate setting.
+	 */
+	if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS))
+		index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
+
+	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);
+	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;
+}
+
+#define usblp_read_status(usblp, status)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1)
+#define usblp_get_id(usblp, config, id, maxlen)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_TYPE_CLASS, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen)
+#define usblp_reset(usblp)\
+	usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_TYPE_CLASS, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)
+
+#define usblp_hp_channel_change_request(usblp, channel, buffer) \
+	usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST, USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE, channel, buffer, 1)
+
+/*
+ * See the description for usblp_select_alts() below for the usage
+ * explanation.  Look into your /sys/kernel/debug/usb/devices and dmesg in
+ * case of any trouble.
+ */
+static int proto_bias = -1;
+
+/*
+ * URB callback.
+ */
+
+static void usblp_bulk_read(struct urb *urb)
+{
+	struct usblp *usblp = urb->context;
+	int status = urb->status;
+	unsigned long flags;
+
+	if (usblp->present && usblp->used) {
+		if (status)
+			printk(KERN_WARNING "usblp%d: "
+			    "nonzero read bulk status received: %d\n",
+			    usblp->minor, status);
+	}
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (status < 0)
+		usblp->rstatus = status;
+	else
+		usblp->rstatus = urb->actual_length;
+	usblp->rcomplete = 1;
+	wake_up(&usblp->rwait);
+	spin_unlock_irqrestore(&usblp->lock, flags);
+
+	usb_free_urb(urb);
+}
+
+static void usblp_bulk_write(struct urb *urb)
+{
+	struct usblp *usblp = urb->context;
+	int status = urb->status;
+	unsigned long flags;
+
+	if (usblp->present && usblp->used) {
+		if (status)
+			printk(KERN_WARNING "usblp%d: "
+			    "nonzero write bulk status received: %d\n",
+			    usblp->minor, status);
+	}
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (status < 0)
+		usblp->wstatus = status;
+	else
+		usblp->wstatus = urb->actual_length;
+	usblp->no_paper = 0;
+	usblp->wcomplete = 1;
+	wake_up(&usblp->wwait);
+	spin_unlock_irqrestore(&usblp->lock, flags);
+
+	usb_free_urb(urb);
+}
+
+/*
+ * Get and print printer errors.
+ */
+
+static const char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" };
+
+static int usblp_check_status(struct usblp *usblp, int err)
+{
+	unsigned char status, newerr = 0;
+	int error;
+
+	mutex_lock(&usblp->mut);
+	if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) {
+		mutex_unlock(&usblp->mut);
+		printk_ratelimited(KERN_ERR
+				"usblp%d: error %d reading printer status\n",
+				usblp->minor, error);
+		return 0;
+	}
+	status = *usblp->statusbuf;
+	mutex_unlock(&usblp->mut);
+
+	if (~status & LP_PERRORP)
+		newerr = 3;
+	if (status & LP_POUTPA)
+		newerr = 1;
+	if (~status & LP_PSELECD)
+		newerr = 2;
+
+	if (newerr != err) {
+		printk(KERN_INFO "usblp%d: %s\n",
+		   usblp->minor, usblp_messages[newerr]);
+	}
+
+	return newerr;
+}
+
+static int handle_bidir(struct usblp *usblp)
+{
+	if (usblp->bidir && usblp->used) {
+		if (usblp_submit_read(usblp) < 0)
+			return -EIO;
+	}
+	return 0;
+}
+
+/*
+ * File op functions.
+ */
+
+static int usblp_open(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	struct usblp *usblp;
+	struct usb_interface *intf;
+	int retval;
+
+	if (minor < 0)
+		return -ENODEV;
+
+	mutex_lock(&usblp_mutex);
+
+	retval = -ENODEV;
+	intf = usb_find_interface(&usblp_driver, minor);
+	if (!intf)
+		goto out;
+	usblp = usb_get_intfdata(intf);
+	if (!usblp || !usblp->dev || !usblp->present)
+		goto out;
+
+	retval = -EBUSY;
+	if (usblp->used)
+		goto out;
+
+	/*
+	 * We do not implement LP_ABORTOPEN/LPABORTOPEN for two reasons:
+	 *  - We do not want persistent state which close(2) does not clear
+	 *  - It is not used anyway, according to CUPS people
+	 */
+
+	retval = usb_autopm_get_interface(intf);
+	if (retval < 0)
+		goto out;
+	usblp->used = 1;
+	file->private_data = usblp;
+
+	usblp->wcomplete = 1; /* we begin writeable */
+	usblp->wstatus = 0;
+	usblp->rcomplete = 0;
+
+	if (handle_bidir(usblp) < 0) {
+		usb_autopm_put_interface(intf);
+		usblp->used = 0;
+		file->private_data = NULL;
+		retval = -EIO;
+	}
+out:
+	mutex_unlock(&usblp_mutex);
+	return retval;
+}
+
+static void usblp_cleanup(struct usblp *usblp)
+{
+	printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
+
+	kfree(usblp->readbuf);
+	kfree(usblp->device_id_string);
+	kfree(usblp->statusbuf);
+	kfree(usblp);
+}
+
+static void usblp_unlink_urbs(struct usblp *usblp)
+{
+	usb_kill_anchored_urbs(&usblp->urbs);
+}
+
+static int usblp_release(struct inode *inode, struct file *file)
+{
+	struct usblp *usblp = file->private_data;
+
+	usblp->flags &= ~LP_ABORT;
+
+	mutex_lock(&usblp_mutex);
+	usblp->used = 0;
+	if (usblp->present) {
+		usblp_unlink_urbs(usblp);
+		usb_autopm_put_interface(usblp->intf);
+	} else		/* finish cleanup from disconnect */
+		usblp_cleanup(usblp);
+	mutex_unlock(&usblp_mutex);
+	return 0;
+}
+
+/* No kernel lock - fine */
+static __poll_t usblp_poll(struct file *file, struct poll_table_struct *wait)
+{
+	__poll_t ret;
+	unsigned long flags;
+
+	struct usblp *usblp = file->private_data;
+	/* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */
+	poll_wait(file, &usblp->rwait, wait);
+	poll_wait(file, &usblp->wwait, wait);
+	spin_lock_irqsave(&usblp->lock, flags);
+	ret = ((usblp->bidir && usblp->rcomplete) ? EPOLLIN  | EPOLLRDNORM : 0) |
+	   ((usblp->no_paper || usblp->wcomplete) ? EPOLLOUT | EPOLLWRNORM : 0);
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	return ret;
+}
+
+static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usblp *usblp = file->private_data;
+	int length, err, i;
+	unsigned char newChannel;
+	int status;
+	int twoints[2];
+	int retval = 0;
+
+	mutex_lock(&usblp->mut);
+	if (!usblp->present) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	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 */
+
+		switch (_IOC_NR(cmd)) {
+
+		case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+			if (_IOC_DIR(cmd) != _IOC_READ) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+			length = usblp_cache_device_id_string(usblp);
+			if (length < 0) {
+				retval = length;
+				goto done;
+			}
+			if (length > _IOC_SIZE(cmd))
+				length = _IOC_SIZE(cmd); /* truncate */
+
+			if (copy_to_user((void __user *) arg,
+					usblp->device_id_string,
+					(unsigned long) length)) {
+				retval = -EFAULT;
+				goto done;
+			}
+
+			break;
+
+		case IOCNR_GET_PROTOCOLS:
+			if (_IOC_DIR(cmd) != _IOC_READ ||
+			    _IOC_SIZE(cmd) < sizeof(twoints)) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+			twoints[0] = usblp->current_protocol;
+			twoints[1] = 0;
+			for (i = USBLP_FIRST_PROTOCOL;
+			     i <= USBLP_LAST_PROTOCOL; i++) {
+				if (usblp->protocol[i].alt_setting >= 0)
+					twoints[1] |= (1<<i);
+			}
+
+			if (copy_to_user((void __user *)arg,
+					(unsigned char *)twoints,
+					sizeof(twoints))) {
+				retval = -EFAULT;
+				goto done;
+			}
+
+			break;
+
+		case IOCNR_SET_PROTOCOL:
+			if (_IOC_DIR(cmd) != _IOC_WRITE) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+#ifdef DEBUG
+			if (arg == -10) {
+				usblp_dump(usblp);
+				break;
+			}
+#endif
+
+			usblp_unlink_urbs(usblp);
+			retval = usblp_set_protocol(usblp, arg);
+			if (retval < 0) {
+				usblp_set_protocol(usblp,
+					usblp->current_protocol);
+			}
+			break;
+
+		case IOCNR_HP_SET_CHANNEL:
+			if (_IOC_DIR(cmd) != _IOC_WRITE ||
+			    le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
+			    usblp->quirks & USBLP_QUIRK_BIDIR) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+			err = usblp_hp_channel_change_request(usblp,
+				arg, &newChannel);
+			if (err < 0) {
+				dev_err(&usblp->dev->dev,
+					"usblp%d: error = %d setting "
+					"HP channel\n",
+					usblp->minor, err);
+				retval = -EIO;
+				goto done;
+			}
+
+			dev_dbg(&usblp->intf->dev,
+				"usblp%d requested/got HP channel %ld/%d\n",
+				usblp->minor, arg, newChannel);
+			break;
+
+		case IOCNR_GET_BUS_ADDRESS:
+			if (_IOC_DIR(cmd) != _IOC_READ ||
+			    _IOC_SIZE(cmd) < sizeof(twoints)) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+			twoints[0] = usblp->dev->bus->busnum;
+			twoints[1] = usblp->dev->devnum;
+			if (copy_to_user((void __user *)arg,
+					(unsigned char *)twoints,
+					sizeof(twoints))) {
+				retval = -EFAULT;
+				goto done;
+			}
+
+			dev_dbg(&usblp->intf->dev,
+				"usblp%d is bus=%d, device=%d\n",
+				usblp->minor, twoints[0], twoints[1]);
+			break;
+
+		case IOCNR_GET_VID_PID:
+			if (_IOC_DIR(cmd) != _IOC_READ ||
+			    _IOC_SIZE(cmd) < sizeof(twoints)) {
+				retval = -EINVAL;
+				goto done;
+			}
+
+			twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
+			twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
+			if (copy_to_user((void __user *)arg,
+					(unsigned char *)twoints,
+					sizeof(twoints))) {
+				retval = -EFAULT;
+				goto done;
+			}
+
+			dev_dbg(&usblp->intf->dev,
+				"usblp%d is VID=0x%4.4X, PID=0x%4.4X\n",
+				usblp->minor, twoints[0], twoints[1]);
+			break;
+
+		case IOCNR_SOFT_RESET:
+			if (_IOC_DIR(cmd) != _IOC_NONE) {
+				retval = -EINVAL;
+				goto done;
+			}
+			retval = usblp_reset(usblp);
+			break;
+		default:
+			retval = -ENOTTY;
+		}
+	else	/* old-style ioctl value */
+		switch (cmd) {
+
+		case LPGETSTATUS:
+			retval = usblp_read_status(usblp, usblp->statusbuf);
+			if (retval) {
+				printk_ratelimited(KERN_ERR "usblp%d:"
+					    "failed reading printer status (%d)\n",
+					    usblp->minor, retval);
+				retval = -EIO;
+				goto done;
+			}
+			status = *usblp->statusbuf;
+			if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+				retval = -EFAULT;
+			break;
+
+		case LPABORT:
+			if (arg)
+				usblp->flags |= LP_ABORT;
+			else
+				usblp->flags &= ~LP_ABORT;
+			break;
+
+		default:
+			retval = -ENOTTY;
+		}
+
+done:
+	mutex_unlock(&usblp->mut);
+	return retval;
+}
+
+static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length)
+{
+	struct urb *urb;
+	char *writebuf;
+
+	writebuf = kmalloc(transfer_length, GFP_KERNEL);
+	if (writebuf == NULL)
+		return NULL;
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (urb == NULL) {
+		kfree(writebuf);
+		return NULL;
+	}
+
+	usb_fill_bulk_urb(urb, usblp->dev,
+		usb_sndbulkpipe(usblp->dev,
+		 usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
+		writebuf, transfer_length, usblp_bulk_write, usblp);
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	return urb;
+}
+
+static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct usblp *usblp = file->private_data;
+	struct urb *writeurb;
+	int rv;
+	int transfer_length;
+	ssize_t writecount = 0;
+
+	if (mutex_lock_interruptible(&usblp->wmut)) {
+		rv = -EINTR;
+		goto raise_biglock;
+	}
+	if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)
+		goto raise_wait;
+
+	while (writecount < count) {
+		/*
+		 * Step 1: Submit next block.
+		 */
+		if ((transfer_length = count - writecount) > USBLP_BUF_SIZE)
+			transfer_length = USBLP_BUF_SIZE;
+
+		rv = -ENOMEM;
+		writeurb = usblp_new_writeurb(usblp, transfer_length);
+		if (writeurb == NULL)
+			goto raise_urb;
+		usb_anchor_urb(writeurb, &usblp->urbs);
+
+		if (copy_from_user(writeurb->transfer_buffer,
+				   buffer + writecount, transfer_length)) {
+			rv = -EFAULT;
+			goto raise_badaddr;
+		}
+
+		spin_lock_irq(&usblp->lock);
+		usblp->wcomplete = 0;
+		spin_unlock_irq(&usblp->lock);
+		if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
+			usblp->wstatus = 0;
+			spin_lock_irq(&usblp->lock);
+			usblp->no_paper = 0;
+			usblp->wcomplete = 1;
+			wake_up(&usblp->wwait);
+			spin_unlock_irq(&usblp->lock);
+			if (rv != -ENOMEM)
+				rv = -EIO;
+			goto raise_submit;
+		}
+
+		/*
+		 * Step 2: Wait for transfer to end, collect results.
+		 */
+		rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK));
+		if (rv < 0) {
+			if (rv == -EAGAIN) {
+				/* Presume that it's going to complete well. */
+				writecount += transfer_length;
+			}
+			if (rv == -ENOSPC) {
+				spin_lock_irq(&usblp->lock);
+				usblp->no_paper = 1;	/* Mark for poll(2) */
+				spin_unlock_irq(&usblp->lock);
+				writecount += transfer_length;
+			}
+			/* Leave URB dangling, to be cleaned on close. */
+			goto collect_error;
+		}
+
+		if (usblp->wstatus < 0) {
+			rv = -EIO;
+			goto collect_error;
+		}
+		/*
+		 * This is critical: it must be our URB, not other writer's.
+		 * The wmut exists mainly to cover us here.
+		 */
+		writecount += usblp->wstatus;
+	}
+
+	mutex_unlock(&usblp->wmut);
+	return writecount;
+
+raise_submit:
+raise_badaddr:
+	usb_unanchor_urb(writeurb);
+	usb_free_urb(writeurb);
+raise_urb:
+raise_wait:
+collect_error:		/* Out of raise sequence */
+	mutex_unlock(&usblp->wmut);
+raise_biglock:
+	return writecount ? writecount : rv;
+}
+
+/*
+ * Notice that we fail to restart in a few cases: on EFAULT, on restart
+ * error, etc. This is the historical behaviour. In all such cases we return
+ * EIO, and applications loop in order to get the new read going.
+ */
+static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, loff_t *ppos)
+{
+	struct usblp *usblp = file->private_data;
+	ssize_t count;
+	ssize_t avail;
+	int rv;
+
+	if (!usblp->bidir)
+		return -EINVAL;
+
+	rv = usblp_rwait_and_lock(usblp, !!(file->f_flags & O_NONBLOCK));
+	if (rv < 0)
+		return rv;
+
+	if ((avail = usblp->rstatus) < 0) {
+		printk(KERN_ERR "usblp%d: error %d reading from printer\n",
+		    usblp->minor, (int)avail);
+		usblp_submit_read(usblp);
+		count = -EIO;
+		goto done;
+	}
+
+	count = len < avail - usblp->readcount ? len : avail - usblp->readcount;
+	if (count != 0 &&
+	    copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) {
+		count = -EFAULT;
+		goto done;
+	}
+
+	if ((usblp->readcount += count) == avail) {
+		if (usblp_submit_read(usblp) < 0) {
+			/* We don't want to leak USB return codes into errno. */
+			if (count == 0)
+				count = -EIO;
+			goto done;
+		}
+	}
+
+done:
+	mutex_unlock(&usblp->mut);
+	return count;
+}
+
+/*
+ * Wait for the write path to come idle.
+ * This is called under the ->wmut, so the idle path stays idle.
+ *
+ * Our write path has a peculiar property: it does not buffer like a tty,
+ * but waits for the write to succeed. This allows our ->release to bug out
+ * without waiting for writes to drain. But it obviously does not work
+ * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use
+ * select(2) or poll(2) to wait for the buffer to drain before closing.
+ * Alternatively, set blocking mode with fcntl and issue a zero-size write.
+ */
+static int usblp_wwait(struct usblp *usblp, int nonblock)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	int rc;
+	int err = 0;
+
+	add_wait_queue(&usblp->wwait, &waita);
+	for (;;) {
+		if (mutex_lock_interruptible(&usblp->mut)) {
+			rc = -EINTR;
+			break;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		rc = usblp_wtest(usblp, nonblock);
+		mutex_unlock(&usblp->mut);
+		if (rc <= 0)
+			break;
+
+		if (schedule_timeout(msecs_to_jiffies(1500)) == 0) {
+			if (usblp->flags & LP_ABORT) {
+				err = usblp_check_status(usblp, err);
+				if (err == 1) {	/* Paper out */
+					rc = -ENOSPC;
+					break;
+				}
+			} else {
+				/* Prod the printer, Gentoo#251237. */
+				mutex_lock(&usblp->mut);
+				usblp_read_status(usblp, usblp->statusbuf);
+				mutex_unlock(&usblp->mut);
+			}
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&usblp->wwait, &waita);
+	return rc;
+}
+
+static int usblp_wtest(struct usblp *usblp, int nonblock)
+{
+	unsigned long flags;
+
+	if (!usblp->present)
+		return -ENODEV;
+	if (signal_pending(current))
+		return -EINTR;
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (usblp->wcomplete) {
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if (nonblock)
+		return -EAGAIN;
+	return 1;
+}
+
+/*
+ * Wait for read bytes to become available. This probably should have been
+ * called usblp_r_lock_and_wait(), because we lock first. But it's a traditional
+ * name for functions which lock and return.
+ *
+ * We do not use wait_event_interruptible because it makes locking iffy.
+ */
+static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	int rc;
+
+	add_wait_queue(&usblp->rwait, &waita);
+	for (;;) {
+		if (mutex_lock_interruptible(&usblp->mut)) {
+			rc = -EINTR;
+			break;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		if ((rc = usblp_rtest(usblp, nonblock)) < 0) {
+			mutex_unlock(&usblp->mut);
+			break;
+		}
+		if (rc == 0)	/* Keep it locked */
+			break;
+		mutex_unlock(&usblp->mut);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&usblp->rwait, &waita);
+	return rc;
+}
+
+static int usblp_rtest(struct usblp *usblp, int nonblock)
+{
+	unsigned long flags;
+
+	if (!usblp->present)
+		return -ENODEV;
+	if (signal_pending(current))
+		return -EINTR;
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (usblp->rcomplete) {
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if (nonblock)
+		return -EAGAIN;
+	return 1;
+}
+
+/*
+ * Please check ->bidir and other such things outside for now.
+ */
+static int usblp_submit_read(struct usblp *usblp)
+{
+	struct urb *urb;
+	unsigned long flags;
+	int rc;
+
+	rc = -ENOMEM;
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (urb == NULL)
+		goto raise_urb;
+
+	usb_fill_bulk_urb(urb, usblp->dev,
+		usb_rcvbulkpipe(usblp->dev,
+		  usblp->protocol[usblp->current_protocol].epread->bEndpointAddress),
+		usblp->readbuf, USBLP_BUF_SIZE_IN,
+		usblp_bulk_read, usblp);
+	usb_anchor_urb(urb, &usblp->urbs);
+
+	spin_lock_irqsave(&usblp->lock, flags);
+	usblp->readcount = 0; /* XXX Why here? */
+	usblp->rcomplete = 0;
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
+		dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc);
+		spin_lock_irqsave(&usblp->lock, flags);
+		usblp->rstatus = rc;
+		usblp->rcomplete = 1;
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		goto raise_submit;
+	}
+
+	return 0;
+
+raise_submit:
+	usb_unanchor_urb(urb);
+	usb_free_urb(urb);
+raise_urb:
+	return rc;
+}
+
+/*
+ * Checks for printers that have quirks, such as requiring unidirectional
+ * communication but reporting bidirectional; currently some HP printers
+ * have this flaw (HP 810, 880, 895, etc.), or needing an init string
+ * sent at each open (like some Epsons).
+ * Returns 1 if found, 0 if not found.
+ *
+ * HP recommended that we use the bidirectional interface but
+ * don't attempt any bulk IN transfers from the IN endpoint.
+ * Here's some more detail on the problem:
+ * The problem is not that it isn't bidirectional though. The problem
+ * is that if you request a device ID, or status information, while
+ * the buffers are full, the return data will end up in the print data
+ * buffer. For example if you make sure you never request the device ID
+ * while you are sending print data, and you don't try to query the
+ * printer status every couple of milliseconds, you will probably be OK.
+ */
+static unsigned int usblp_quirks(__u16 vendor, __u16 product)
+{
+	int i;
+
+	for (i = 0; quirk_printers[i].vendorId; i++) {
+		if (vendor == quirk_printers[i].vendorId &&
+		    product == quirk_printers[i].productId)
+			return quirk_printers[i].quirks;
+	}
+	return 0;
+}
+
+static const struct file_operations usblp_fops = {
+	.owner =	THIS_MODULE,
+	.read =		usblp_read,
+	.write =	usblp_write,
+	.poll =		usblp_poll,
+	.unlocked_ioctl =	usblp_ioctl,
+	.compat_ioctl =		usblp_ioctl,
+	.open =		usblp_open,
+	.release =	usblp_release,
+	.llseek =	noop_llseek,
+};
+
+static char *usblp_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
+}
+
+static struct usb_class_driver usblp_class = {
+	.name =		"lp%d",
+	.devnode =	usblp_devnode,
+	.fops =		&usblp_fops,
+	.minor_base =	USBLP_MINOR_BASE,
+};
+
+static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usblp *usblp = usb_get_intfdata(intf);
+
+	if (usblp->device_id_string[0] == 0 &&
+	    usblp->device_id_string[1] == 0)
+		return 0;
+
+	return sprintf(buf, "%s", usblp->device_id_string+2);
+}
+
+static DEVICE_ATTR_RO(ieee1284_id);
+
+static int usblp_probe(struct usb_interface *intf,
+		       const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usblp *usblp;
+	int protocol;
+	int retval;
+
+	/* Malloc and start initializing usblp structure so we can use it
+	 * directly. */
+	usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL);
+	if (!usblp) {
+		retval = -ENOMEM;
+		goto abort_ret;
+	}
+	usblp->dev = dev;
+	mutex_init(&usblp->wmut);
+	mutex_init(&usblp->mut);
+	spin_lock_init(&usblp->lock);
+	init_waitqueue_head(&usblp->rwait);
+	init_waitqueue_head(&usblp->wwait);
+	init_usb_anchor(&usblp->urbs);
+	usblp->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+	usblp->intf = intf;
+
+	/* Malloc device ID string buffer to the largest expected length,
+	 * since we can re-query it on an ioctl and a dynamic string
+	 * could change in length. */
+	if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) {
+		retval = -ENOMEM;
+		goto abort;
+	}
+
+	/*
+	 * Allocate read buffer. We somewhat wastefully
+	 * malloc both regardless of bidirectionality, because the
+	 * alternate setting can be changed later via an ioctl.
+	 */
+	if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) {
+		retval = -ENOMEM;
+		goto abort;
+	}
+
+	/* Allocate buffer for printer status */
+	usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
+	if (!usblp->statusbuf) {
+		retval = -ENOMEM;
+		goto abort;
+	}
+
+	/* Lookup quirks for this printer. */
+	usblp->quirks = usblp_quirks(
+		le16_to_cpu(dev->descriptor.idVendor),
+		le16_to_cpu(dev->descriptor.idProduct));
+
+	/* Analyze and pick initial alternate settings and endpoints. */
+	protocol = usblp_select_alts(usblp);
+	if (protocol < 0) {
+		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;
+		goto abort;
+	}
+
+	/* Setup the selected alternate setting and endpoints. */
+	if (usblp_set_protocol(usblp, protocol) < 0) {
+		retval = -ENODEV;	/* ->probe isn't ->ioctl */
+		goto abort;
+	}
+
+	/* Retrieve and store the device ID string. */
+	usblp_cache_device_id_string(usblp);
+	retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+	if (retval)
+		goto abort_intfdata;
+
+#ifdef DEBUG
+	usblp_check_status(usblp, 0);
+#endif
+
+	usb_set_intfdata(intf, usblp);
+
+	usblp->present = 1;
+
+	retval = usb_register_dev(intf, &usblp_class);
+	if (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;
+	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,
+		usblp->current_protocol,
+		le16_to_cpu(usblp->dev->descriptor.idVendor),
+		le16_to_cpu(usblp->dev->descriptor.idProduct));
+
+	return 0;
+
+abort_intfdata:
+	usb_set_intfdata(intf, NULL);
+	device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
+abort:
+	kfree(usblp->readbuf);
+	kfree(usblp->statusbuf);
+	kfree(usblp->device_id_string);
+	kfree(usblp);
+abort_ret:
+	return retval;
+}
+
+/*
+ * We are a "new" style driver with usb_device_id table,
+ * but our requirements are too intricate for simple match to handle.
+ *
+ * The "proto_bias" option may be used to specify the preferred protocol
+ * for all USB printers (1=USB_CLASS_PRINTER/1/1, 2=USB_CLASS_PRINTER/1/2,
+ * 3=USB_CLASS_PRINTER/1/3).  If the device supports the preferred protocol,
+ * then we bind to it.
+ *
+ * The best interface for us is USB_CLASS_PRINTER/1/2, because it
+ * is compatible with a stream of characters. If we find it, we bind to it.
+ *
+ * Note that the people from hpoj.sourceforge.net need to be able to
+ * bind to USB_CLASS_PRINTER/1/3 (MLC/1284.4), so we provide them ioctls
+ * for this purpose.
+ *
+ * Failing USB_CLASS_PRINTER/1/2, we look for USB_CLASS_PRINTER/1/3,
+ * even though it's probably not stream-compatible, because this matches
+ * the behaviour of the old code.
+ *
+ * If nothing else, we bind to USB_CLASS_PRINTER/1/1
+ * - the unidirectional interface.
+ */
+static int usblp_select_alts(struct usblp *usblp)
+{
+	struct usb_interface *if_alt;
+	struct usb_host_interface *ifd;
+	struct usb_endpoint_descriptor *epwrite, *epread;
+	int p, i;
+	int res;
+
+	if_alt = usblp->intf;
+
+	for (p = 0; p < USBLP_MAX_PROTOCOLS; p++)
+		usblp->protocol[p].alt_setting = -1;
+
+	/* Find out what we have. */
+	for (i = 0; i < if_alt->num_altsetting; i++) {
+		ifd = &if_alt->altsetting[i];
+
+		if (ifd->desc.bInterfaceClass != USB_CLASS_PRINTER ||
+		    ifd->desc.bInterfaceSubClass != 1)
+			if (!(usblp->quirks & USBLP_QUIRK_BAD_CLASS))
+				continue;
+
+		if (ifd->desc.bInterfaceProtocol < USBLP_FIRST_PROTOCOL ||
+		    ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL)
+			continue;
+
+		/* Look for the expected bulk endpoints. */
+		if (ifd->desc.bInterfaceProtocol > 1) {
+			res = usb_find_common_endpoints(ifd,
+					&epread, &epwrite, NULL, NULL);
+		} else {
+			epread = NULL;
+			res = usb_find_bulk_out_endpoint(ifd, &epwrite);
+		}
+
+		/* Ignore buggy hardware without the right endpoints. */
+		if (res)
+			continue;
+
+		/* Turn off reads for buggy bidirectional printers. */
+		if (usblp->quirks & USBLP_QUIRK_BIDIR) {
+			printk(KERN_INFO "usblp%d: Disabling reads from "
+			    "problematic bidirectional printer\n",
+			    usblp->minor);
+			epread = NULL;
+		}
+
+		usblp->protocol[ifd->desc.bInterfaceProtocol].alt_setting =
+				ifd->desc.bAlternateSetting;
+		usblp->protocol[ifd->desc.bInterfaceProtocol].epwrite = epwrite;
+		usblp->protocol[ifd->desc.bInterfaceProtocol].epread = epread;
+	}
+
+	/* If our requested protocol is supported, then use it. */
+	if (proto_bias >= USBLP_FIRST_PROTOCOL &&
+	    proto_bias <= USBLP_LAST_PROTOCOL &&
+	    usblp->protocol[proto_bias].alt_setting != -1)
+		return proto_bias;
+
+	/* Ordering is important here. */
+	if (usblp->protocol[2].alt_setting != -1)
+		return 2;
+	if (usblp->protocol[1].alt_setting != -1)
+		return 1;
+	if (usblp->protocol[3].alt_setting != -1)
+		return 3;
+
+	/* If nothing is available, then don't bind to this device. */
+	return -1;
+}
+
+static int usblp_set_protocol(struct usblp *usblp, int protocol)
+{
+	int r, alts;
+
+	if (protocol < USBLP_FIRST_PROTOCOL || protocol > USBLP_LAST_PROTOCOL)
+		return -EINVAL;
+
+	alts = usblp->protocol[protocol].alt_setting;
+	if (alts < 0)
+		return -EINVAL;
+	r = usb_set_interface(usblp->dev, usblp->ifnum, alts);
+	if (r < 0) {
+		printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n",
+			alts, usblp->ifnum);
+		return r;
+	}
+
+	usblp->bidir = (usblp->protocol[protocol].epread != NULL);
+	usblp->current_protocol = protocol;
+	dev_dbg(&usblp->intf->dev, "usblp%d set protocol %d\n",
+		usblp->minor, protocol);
+	return 0;
+}
+
+/* Retrieves and caches device ID string.
+ * Returns length, including length bytes but not null terminator.
+ * On error, returns a negative errno value. */
+static int usblp_cache_device_id_string(struct usblp *usblp)
+{
+	int err, length;
+
+	err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
+	if (err < 0) {
+		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;
+	}
+
+	/* First two bytes are length in big-endian.
+	 * They count themselves, and we copy them into
+	 * the user's buffer. */
+	length = be16_to_cpu(*((__be16 *)usblp->device_id_string));
+	if (length < 2)
+		length = 2;
+	else if (length >= USBLP_DEVICE_ID_SIZE)
+		length = USBLP_DEVICE_ID_SIZE - 1;
+	usblp->device_id_string[length] = '\0';
+
+	dev_dbg(&usblp->intf->dev, "usblp%d Device ID string [len=%d]=\"%s\"\n",
+		usblp->minor, length, &usblp->device_id_string[2]);
+
+	return length;
+}
+
+static void usblp_disconnect(struct usb_interface *intf)
+{
+	struct usblp *usblp = usb_get_intfdata(intf);
+
+	usb_deregister_dev(intf, &usblp_class);
+
+	if (!usblp || !usblp->dev) {
+		dev_err(&intf->dev, "bogus disconnect\n");
+		BUG();
+	}
+
+	device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
+
+	mutex_lock(&usblp_mutex);
+	mutex_lock(&usblp->mut);
+	usblp->present = 0;
+	wake_up(&usblp->wwait);
+	wake_up(&usblp->rwait);
+	usb_set_intfdata(intf, NULL);
+
+	usblp_unlink_urbs(usblp);
+	mutex_unlock(&usblp->mut);
+
+	if (!usblp->used)
+		usblp_cleanup(usblp);
+	mutex_unlock(&usblp_mutex);
+}
+
+static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usblp *usblp = usb_get_intfdata(intf);
+
+	usblp_unlink_urbs(usblp);
+#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
+	/* not strictly necessary, but just in case */
+	wake_up(&usblp->wwait);
+	wake_up(&usblp->rwait);
+#endif
+
+	return 0;
+}
+
+static int usblp_resume(struct usb_interface *intf)
+{
+	struct usblp *usblp = usb_get_intfdata(intf);
+	int r;
+
+	r = handle_bidir(usblp);
+
+	return r;
+}
+
+static const struct usb_device_id usblp_ids[] = {
+	{ USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 1) },
+	{ USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 2) },
+	{ USB_DEVICE_INFO(USB_CLASS_PRINTER, 1, 3) },
+	{ USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 1) },
+	{ USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 2) },
+	{ USB_INTERFACE_INFO(USB_CLASS_PRINTER, 1, 3) },
+	{ USB_DEVICE(0x04b8, 0x0202) },	/* Seiko Epson Receipt Printer M129C */
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usblp_ids);
+
+static struct usb_driver usblp_driver = {
+	.name =		"usblp",
+	.probe =	usblp_probe,
+	.disconnect =	usblp_disconnect,
+	.suspend =	usblp_suspend,
+	.resume =	usblp_resume,
+	.id_table =	usblp_ids,
+	.supports_autosuspend =	1,
+};
+
+module_usb_driver(usblp_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+module_param(proto_bias, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(proto_bias, "Favourite protocol number");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
new file mode 100644
index 0000000..83ffa5a
--- /dev/null
+++ b/drivers/usb/class/usbtmc.c
@@ -0,0 +1,1721 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * drivers/usb/class/usbtmc.c - USB Test & Measurement class driver
+ *
+ * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
+ * Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/compat.h>
+#include <linux/usb/tmc.h>
+
+
+#define USBTMC_HEADER_SIZE	12
+#define USBTMC_MINOR_BASE	176
+
+/*
+ * Size of driver internal IO buffer. Must be multiple of 4 and at least as
+ * large as wMaxPacketSize (which is usually 512 bytes).
+ */
+#define USBTMC_SIZE_IOBUFFER	2048
+
+/* Minimum USB timeout (in milliseconds) */
+#define USBTMC_MIN_TIMEOUT	100
+/* Default USB timeout (in milliseconds) */
+#define USBTMC_TIMEOUT		5000
+
+/*
+ * Maximum number of read cycles to empty bulk in endpoint during CLEAR and
+ * ABORT_BULK_IN requests. Ends the loop if (for whatever reason) a short
+ * packet is never read.
+ */
+#define USBTMC_MAX_READS_TO_CLEAR_BULK_IN	100
+
+static const struct usb_device_id usbtmc_devices[] = {
+	{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 0), },
+	{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 1), },
+	{ 0, } /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, usbtmc_devices);
+
+/*
+ * This structure is the capabilities for the device
+ * See section 4.2.1.8 of the USBTMC specification,
+ * and section 4.2.2 of the USBTMC usb488 subclass
+ * specification for details.
+ */
+struct usbtmc_dev_capabilities {
+	__u8 interface_capabilities;
+	__u8 device_capabilities;
+	__u8 usb488_interface_capabilities;
+	__u8 usb488_device_capabilities;
+};
+
+/* This structure holds private data for each USBTMC device. One copy is
+ * allocated for each USBTMC device in the driver's probe function.
+ */
+struct usbtmc_device_data {
+	const struct usb_device_id *id;
+	struct usb_device *usb_dev;
+	struct usb_interface *intf;
+	struct list_head file_list;
+
+	unsigned int bulk_in;
+	unsigned int bulk_out;
+
+	u8 bTag;
+	u8 bTag_last_write;	/* needed for abort */
+	u8 bTag_last_read;	/* needed for abort */
+
+	/* data for interrupt in endpoint handling */
+	u8             bNotify1;
+	u8             bNotify2;
+	u16            ifnum;
+	u8             iin_bTag;
+	u8            *iin_buffer;
+	atomic_t       iin_data_valid;
+	unsigned int   iin_ep;
+	int            iin_ep_present;
+	int            iin_interval;
+	struct urb    *iin_urb;
+	u16            iin_wMaxPacketSize;
+
+	/* coalesced usb488_caps from usbtmc_dev_capabilities */
+	__u8 usb488_caps;
+
+	/* attributes from the USB TMC spec for this device */
+	u8 TermChar;
+	bool TermCharEnabled;
+	bool auto_abort;
+
+	bool zombie; /* fd of disconnected device */
+
+	struct usbtmc_dev_capabilities	capabilities;
+	struct kref kref;
+	struct mutex io_mutex;	/* only one i/o function running at a time */
+	wait_queue_head_t waitq;
+	struct fasync_struct *fasync;
+	spinlock_t dev_lock; /* lock for file_list */
+};
+#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref)
+
+/*
+ * This structure holds private data for each USBTMC file handle.
+ */
+struct usbtmc_file_data {
+	struct usbtmc_device_data *data;
+	struct list_head file_elem;
+
+	u32            timeout;
+	u8             srq_byte;
+	atomic_t       srq_asserted;
+	u8             eom_val;
+	u8             term_char;
+	bool           term_char_enabled;
+};
+
+/* Forward declarations */
+static struct usb_driver usbtmc_driver;
+
+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)
+{
+	struct usb_interface *intf;
+	struct usbtmc_device_data *data;
+	struct usbtmc_file_data *file_data;
+
+	intf = usb_find_interface(&usbtmc_driver, iminor(inode));
+	if (!intf) {
+		pr_err("can not find device for minor %d", iminor(inode));
+		return -ENODEV;
+	}
+
+	file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
+	if (!file_data)
+		return -ENOMEM;
+
+	data = usb_get_intfdata(intf);
+	/* Protect reference to data from file structure until release */
+	kref_get(&data->kref);
+
+	mutex_lock(&data->io_mutex);
+	file_data->data = data;
+
+	/* copy default values from device settings */
+	file_data->timeout = USBTMC_TIMEOUT;
+	file_data->term_char = data->TermChar;
+	file_data->term_char_enabled = data->TermCharEnabled;
+	file_data->eom_val = 1;
+
+	INIT_LIST_HEAD(&file_data->file_elem);
+	spin_lock_irq(&data->dev_lock);
+	list_add_tail(&file_data->file_elem, &data->file_list);
+	spin_unlock_irq(&data->dev_lock);
+	mutex_unlock(&data->io_mutex);
+
+	/* Store pointer in file structure's private data field */
+	filp->private_data = file_data;
+
+	return 0;
+}
+
+static int usbtmc_release(struct inode *inode, struct file *file)
+{
+	struct usbtmc_file_data *file_data = file->private_data;
+
+	/* prevent IO _AND_ usbtmc_interrupt */
+	mutex_lock(&file_data->data->io_mutex);
+	spin_lock_irq(&file_data->data->dev_lock);
+
+	list_del(&file_data->file_elem);
+
+	spin_unlock_irq(&file_data->data->dev_lock);
+	mutex_unlock(&file_data->data->io_mutex);
+
+	kref_put(&file_data->data->kref, usbtmc_delete);
+	file_data->data = NULL;
+	kfree(file_data);
+	return 0;
+}
+
+static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
+{
+	u8 *buffer;
+	struct device *dev;
+	int rv;
+	int n;
+	int actual;
+	struct usb_host_interface *current_setting;
+	int max_size;
+
+	dev = &data->intf->dev;
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_INITIATE_ABORT_BULK_IN,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+			     data->bTag_last_read, data->bulk_in,
+			     buffer, 2, USBTMC_TIMEOUT);
+
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+
+	if (buffer[0] == USBTMC_STATUS_FAILED) {
+		rv = 0;
+		goto exit;
+	}
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n",
+			buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	max_size = 0;
+	current_setting = data->intf->cur_altsetting;
+	for (n = 0; n < current_setting->desc.bNumEndpoints; n++)
+		if (current_setting->endpoint[n].desc.bEndpointAddress ==
+			data->bulk_in)
+			max_size = usb_endpoint_maxp(&current_setting->endpoint[n].desc);
+
+	if (max_size == 0) {
+		dev_err(dev, "Couldn't get wMaxPacketSize\n");
+		rv = -EPERM;
+		goto exit;
+	}
+
+	dev_dbg(&data->intf->dev, "wMaxPacketSize is %d\n", max_size);
+
+	n = 0;
+
+	do {
+		dev_dbg(dev, "Reading from bulk in EP\n");
+
+		rv = usb_bulk_msg(data->usb_dev,
+				  usb_rcvbulkpipe(data->usb_dev,
+						  data->bulk_in),
+				  buffer, USBTMC_SIZE_IOBUFFER,
+				  &actual, USBTMC_TIMEOUT);
+
+		n++;
+
+		if (rv < 0) {
+			dev_err(dev, "usb_bulk_msg returned %d\n", rv);
+			goto exit;
+		}
+	} while ((actual == max_size) &&
+		 (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
+
+	if (actual == max_size) {
+		dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
+			USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	n = 0;
+
+usbtmc_abort_bulk_in_status:
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+			     0, data->bulk_in, buffer, 0x08,
+			     USBTMC_TIMEOUT);
+
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+
+	if (buffer[0] == USBTMC_STATUS_SUCCESS) {
+		rv = 0;
+		goto exit;
+	}
+
+	if (buffer[0] != USBTMC_STATUS_PENDING) {
+		dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	if (buffer[1] == 1)
+		do {
+			dev_dbg(dev, "Reading from bulk in EP\n");
+
+			rv = usb_bulk_msg(data->usb_dev,
+					  usb_rcvbulkpipe(data->usb_dev,
+							  data->bulk_in),
+					  buffer, USBTMC_SIZE_IOBUFFER,
+					  &actual, USBTMC_TIMEOUT);
+
+			n++;
+
+			if (rv < 0) {
+				dev_err(dev, "usb_bulk_msg returned %d\n", rv);
+				goto exit;
+			}
+		} while ((actual == max_size) &&
+			 (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
+
+	if (actual == max_size) {
+		dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
+			USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	goto usbtmc_abort_bulk_in_status;
+
+exit:
+	kfree(buffer);
+	return rv;
+
+}
+
+static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data)
+{
+	struct device *dev;
+	u8 *buffer;
+	int rv;
+	int n;
+
+	dev = &data->intf->dev;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+			     data->bTag_last_write, data->bulk_out,
+			     buffer, 2, USBTMC_TIMEOUT);
+
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", buffer[0]);
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "INITIATE_ABORT_BULK_OUT returned %x\n",
+			buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	n = 0;
+
+usbtmc_abort_bulk_out_check_status:
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+			     0, data->bulk_out, buffer, 0x08,
+			     USBTMC_TIMEOUT);
+	n++;
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "CHECK_ABORT_BULK_OUT returned %x\n", buffer[0]);
+
+	if (buffer[0] == USBTMC_STATUS_SUCCESS)
+		goto usbtmc_abort_bulk_out_clear_halt;
+
+	if ((buffer[0] == USBTMC_STATUS_PENDING) &&
+	    (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN))
+		goto usbtmc_abort_bulk_out_check_status;
+
+	rv = -EPERM;
+	goto exit;
+
+usbtmc_abort_bulk_out_clear_halt:
+	rv = usb_clear_halt(data->usb_dev,
+			    usb_sndbulkpipe(data->usb_dev, data->bulk_out));
+
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+	rv = 0;
+
+exit:
+	kfree(buffer);
+	return rv;
+}
+
+static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
+				void __user *arg)
+{
+	struct usbtmc_device_data *data = file_data->data;
+	struct device *dev = &data->intf->dev;
+	int srq_asserted = 0;
+	u8 *buffer;
+	u8 tag;
+	__u8 stb;
+	int rv;
+
+	dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
+		data->iin_ep_present);
+
+	spin_lock_irq(&data->dev_lock);
+	srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
+	if (srq_asserted) {
+		/* a STB with SRQ is already received */
+		stb = file_data->srq_byte;
+		spin_unlock_irq(&data->dev_lock);
+		rv = put_user(stb, (__u8 __user *)arg);
+		dev_dbg(dev, "stb:0x%02x with srq received %d\n",
+			(unsigned int)stb, rv);
+		return rv;
+	}
+	spin_unlock_irq(&data->dev_lock);
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	atomic_set(&data->iin_data_valid, 0);
+
+	rv = usb_control_msg(data->usb_dev,
+			usb_rcvctrlpipe(data->usb_dev, 0),
+			USBTMC488_REQUEST_READ_STATUS_BYTE,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			data->iin_bTag,
+			data->ifnum,
+			buffer, 0x03, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "stb usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "control status returned %x\n", buffer[0]);
+		rv = -EIO;
+		goto exit;
+	}
+
+	if (data->iin_ep_present) {
+		rv = wait_event_interruptible_timeout(
+			data->waitq,
+			atomic_read(&data->iin_data_valid) != 0,
+			file_data->timeout);
+		if (rv < 0) {
+			dev_dbg(dev, "wait interrupted %d\n", rv);
+			goto exit;
+		}
+
+		if (rv == 0) {
+			dev_dbg(dev, "wait timed out\n");
+			rv = -ETIMEDOUT;
+			goto exit;
+		}
+
+		tag = data->bNotify1 & 0x7f;
+		if (tag != data->iin_bTag) {
+			dev_err(dev, "expected bTag %x got %x\n",
+				data->iin_bTag, tag);
+		}
+
+		stb = data->bNotify2;
+	} else {
+		stb = buffer[2];
+	}
+
+	rv = put_user(stb, (__u8 __user *)arg);
+	dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)stb, rv);
+
+ exit:
+	/* bump interrupt bTag */
+	data->iin_bTag += 1;
+	if (data->iin_bTag > 127)
+		/* 1 is for SRQ see USBTMC-USB488 subclass spec section 4.3.1 */
+		data->iin_bTag = 2;
+
+	kfree(buffer);
+	return rv;
+}
+
+static int usbtmc488_ioctl_simple(struct usbtmc_device_data *data,
+				void __user *arg, unsigned int cmd)
+{
+	struct device *dev = &data->intf->dev;
+	__u8 val;
+	u8 *buffer;
+	u16 wValue;
+	int rv;
+
+	if (!(data->usb488_caps & USBTMC488_CAPABILITY_SIMPLE))
+		return -EINVAL;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	if (cmd == USBTMC488_REQUEST_REN_CONTROL) {
+		rv = copy_from_user(&val, arg, sizeof(val));
+		if (rv) {
+			rv = -EFAULT;
+			goto exit;
+		}
+		wValue = val ? 1 : 0;
+	} else {
+		wValue = 0;
+	}
+
+	rv = usb_control_msg(data->usb_dev,
+			usb_rcvctrlpipe(data->usb_dev, 0),
+			cmd,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			wValue,
+			data->ifnum,
+			buffer, 0x01, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "simple usb_control_msg failed %d\n", rv);
+		goto exit;
+	} else if (rv != 1) {
+		dev_warn(dev, "simple usb_control_msg returned %d\n", rv);
+		rv = -EIO;
+		goto exit;
+	}
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "simple control status returned %x\n", buffer[0]);
+		rv = -EIO;
+		goto exit;
+	}
+	rv = 0;
+
+ exit:
+	kfree(buffer);
+	return rv;
+}
+
+/*
+ * Sends a TRIGGER Bulk-OUT command message
+ * See the USBTMC-USB488 specification, Table 2.
+ *
+ * Also updates bTag_last_write.
+ */
+static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data)
+{
+	struct usbtmc_device_data *data = file_data->data;
+	int retval;
+	u8 *buffer;
+	int actual;
+
+	buffer = kzalloc(USBTMC_HEADER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = 128;
+	buffer[1] = data->bTag;
+	buffer[2] = ~data->bTag;
+
+	retval = usb_bulk_msg(data->usb_dev,
+			      usb_sndbulkpipe(data->usb_dev,
+					      data->bulk_out),
+			      buffer, USBTMC_HEADER_SIZE,
+			      &actual, file_data->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, "%s returned %d\n",
+			__func__, retval);
+		return retval;
+	}
+
+	return 0;
+}
+
+/*
+ * Sends a REQUEST_DEV_DEP_MSG_IN message on the Bulk-OUT 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_file_data *file_data,
+				       size_t transfer_size)
+{
+	struct usbtmc_device_data *data = file_data->data;
+	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] = file_data->term_char_enabled * 2;
+	/* Use term character? */
+	buffer[9] = file_data->term_char;
+	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, file_data->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)
+{
+	struct usbtmc_file_data *file_data;
+	struct usbtmc_device_data *data;
+	struct device *dev;
+	u32 n_characters;
+	u8 *buffer;
+	int actual;
+	size_t done;
+	size_t remaining;
+	int retval;
+	size_t this_part;
+
+	/* Get pointer to private data structure */
+	file_data = filp->private_data;
+	data = file_data->data;
+	dev = &data->intf->dev;
+
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	mutex_lock(&data->io_mutex);
+	if (data->zombie) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev_dbg(dev, "usb_bulk_msg_in: count(%zu)\n", count);
+
+	retval = send_request_dev_dep_msg_in(file_data, count);
+
+	if (retval < 0) {
+		if (data->auto_abort)
+			usbtmc_ioctl_abort_bulk_out(data);
+		goto exit;
+	}
+
+	/* Loop until we have fetched everything we requested */
+	remaining = count;
+	this_part = remaining;
+	done = 0;
+
+	while (remaining > 0) {
+		/* Send bulk URB */
+		retval = usb_bulk_msg(data->usb_dev,
+				      usb_rcvbulkpipe(data->usb_dev,
+						      data->bulk_in),
+				      buffer, USBTMC_SIZE_IOBUFFER, &actual,
+				      file_data->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_dbg(dev, "Unable to read data, error %d\n", retval);
+			if (data->auto_abort)
+				usbtmc_ioctl_abort_bulk_in(data);
+			goto exit;
+		}
+
+		/* Parse header in first packet */
+		if (done == 0) {
+			/* 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;
+			}
+
+			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;
+			}
+
+			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;
+			}
+
+			/* How many characters did the instrument send? */
+			n_characters = buffer[4] +
+				       (buffer[5] << 8) +
+				       (buffer[6] << 16) +
+				       (buffer[7] << 24);
+
+			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 (remaining > n_characters)
+				remaining = n_characters;
+			/* Remove padding if it exists */
+			if (actual > remaining)
+				actual = remaining;
+
+			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;
+
+			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 */
+	*f_pos = *f_pos + done;
+	retval = done;
+
+exit:
+	mutex_unlock(&data->io_mutex);
+	kfree(buffer);
+	return retval;
+}
+
+static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
+			    size_t count, loff_t *f_pos)
+{
+	struct usbtmc_file_data *file_data;
+	struct usbtmc_device_data *data;
+	u8 *buffer;
+	int retval;
+	int actual;
+	unsigned long int n_bytes;
+	int remaining;
+	int done;
+	int this_part;
+
+	file_data = filp->private_data;
+	data = file_data->data;
+
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	mutex_lock(&data->io_mutex);
+	if (data->zombie) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	remaining = count;
+	done = 0;
+
+	while (remaining > 0) {
+		if (remaining > USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE) {
+			this_part = USBTMC_SIZE_IOBUFFER - USBTMC_HEADER_SIZE;
+			buffer[8] = 0;
+		} else {
+			this_part = remaining;
+			buffer[8] = file_data->eom_val;
+		}
+
+		/* Setup IO buffer for DEV_DEP_MSG_OUT message */
+		buffer[0] = 1;
+		buffer[1] = data->bTag;
+		buffer[2] = ~data->bTag;
+		buffer[3] = 0; /* Reserved */
+		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[USBTMC_HEADER_SIZE], buf + done, this_part)) {
+			retval = -EFAULT;
+			goto exit;
+		}
+
+		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,
+					      usb_sndbulkpipe(data->usb_dev,
+							      data->bulk_out),
+					      buffer, n_bytes,
+					      &actual, file_data->timeout);
+			if (retval != 0)
+				break;
+			n_bytes -= actual;
+		} while (n_bytes);
+
+		data->bTag_last_write = data->bTag;
+		data->bTag++;
+
+		if (!data->bTag)
+			data->bTag++;
+
+		if (retval < 0) {
+			dev_err(&data->intf->dev,
+				"Unable to send data, error %d\n", retval);
+			if (data->auto_abort)
+				usbtmc_ioctl_abort_bulk_out(data);
+			goto exit;
+		}
+
+		remaining -= this_part;
+		done += this_part;
+	}
+
+	retval = count;
+exit:
+	mutex_unlock(&data->io_mutex);
+	kfree(buffer);
+	return retval;
+}
+
+static int usbtmc_ioctl_clear(struct usbtmc_device_data *data)
+{
+	struct usb_host_interface *current_setting;
+	struct usb_endpoint_descriptor *desc;
+	struct device *dev;
+	u8 *buffer;
+	int rv;
+	int n;
+	int actual = 0;
+	int max_size;
+
+	dev = &data->intf->dev;
+
+	dev_dbg(dev, "Sending INITIATE_CLEAR request\n");
+
+	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_INITIATE_CLEAR,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 1, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "INITIATE_CLEAR returned %x\n", buffer[0]);
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "INITIATE_CLEAR returned %x\n", buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	max_size = 0;
+	current_setting = data->intf->cur_altsetting;
+	for (n = 0; n < current_setting->desc.bNumEndpoints; n++) {
+		desc = &current_setting->endpoint[n].desc;
+		if (desc->bEndpointAddress == data->bulk_in)
+			max_size = usb_endpoint_maxp(desc);
+	}
+
+	if (max_size == 0) {
+		dev_err(dev, "Couldn't get wMaxPacketSize\n");
+		rv = -EPERM;
+		goto exit;
+	}
+
+	dev_dbg(dev, "wMaxPacketSize is %d\n", max_size);
+
+	n = 0;
+
+usbtmc_clear_check_status:
+
+	dev_dbg(dev, "Sending CHECK_CLEAR_STATUS request\n");
+
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_CHECK_CLEAR_STATUS,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 2, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]);
+
+	if (buffer[0] == USBTMC_STATUS_SUCCESS)
+		goto usbtmc_clear_bulk_out_halt;
+
+	if (buffer[0] != USBTMC_STATUS_PENDING) {
+		dev_err(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	if (buffer[1] == 1)
+		do {
+			dev_dbg(dev, "Reading from bulk in EP\n");
+
+			rv = usb_bulk_msg(data->usb_dev,
+					  usb_rcvbulkpipe(data->usb_dev,
+							  data->bulk_in),
+					  buffer, USBTMC_SIZE_IOBUFFER,
+					  &actual, USBTMC_TIMEOUT);
+			n++;
+
+			if (rv < 0) {
+				dev_err(dev, "usb_control_msg returned %d\n",
+					rv);
+				goto exit;
+			}
+		} while ((actual == max_size) &&
+			  (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN));
+
+	if (actual == max_size) {
+		dev_err(dev, "Couldn't clear device buffer within %d cycles\n",
+			USBTMC_MAX_READS_TO_CLEAR_BULK_IN);
+		rv = -EPERM;
+		goto exit;
+	}
+
+	goto usbtmc_clear_check_status;
+
+usbtmc_clear_bulk_out_halt:
+
+	rv = usb_clear_halt(data->usb_dev,
+			    usb_sndbulkpipe(data->usb_dev, data->bulk_out));
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+	rv = 0;
+
+exit:
+	kfree(buffer);
+	return rv;
+}
+
+static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)
+{
+	int rv;
+
+	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);
+		return rv;
+	}
+	return 0;
+}
+
+static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
+{
+	int rv;
+
+	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);
+		return rv;
+	}
+	return 0;
+}
+
+static int get_capabilities(struct usbtmc_device_data *data)
+{
+	struct device *dev = &data->usb_dev->dev;
+	char *buffer;
+	int rv = 0;
+
+	buffer = kmalloc(0x18, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_GET_CAPABILITIES,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 0x18, USBTMC_TIMEOUT);
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto err_out;
+	}
+
+	dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]);
+		rv = -EPERM;
+		goto err_out;
+	}
+	dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]);
+	dev_dbg(dev, "Device capabilities are %x\n", buffer[5]);
+	dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]);
+	dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]);
+
+	data->capabilities.interface_capabilities = buffer[4];
+	data->capabilities.device_capabilities = buffer[5];
+	data->capabilities.usb488_interface_capabilities = buffer[14];
+	data->capabilities.usb488_device_capabilities = buffer[15];
+	data->usb488_caps = (buffer[14] & 0x07) | ((buffer[15] & 0x0f) << 4);
+	rv = 0;
+
+err_out:
+	kfree(buffer);
+	return rv;
+}
+
+#define capability_attribute(name)					\
+static ssize_t name##_show(struct device *dev,				\
+			   struct device_attribute *attr, char *buf)	\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+									\
+	return sprintf(buf, "%d\n", data->capabilities.name);		\
+}									\
+static DEVICE_ATTR_RO(name)
+
+capability_attribute(interface_capabilities);
+capability_attribute(device_capabilities);
+capability_attribute(usb488_interface_capabilities);
+capability_attribute(usb488_device_capabilities);
+
+static struct attribute *capability_attrs[] = {
+	&dev_attr_interface_capabilities.attr,
+	&dev_attr_device_capabilities.attr,
+	&dev_attr_usb488_interface_capabilities.attr,
+	&dev_attr_usb488_device_capabilities.attr,
+	NULL,
+};
+
+static const struct attribute_group capability_attr_grp = {
+	.attrs = capability_attrs,
+};
+
+static ssize_t TermChar_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%c\n", data->TermChar);
+}
+
+static ssize_t TermChar_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);
+
+	if (count < 1)
+		return -EINVAL;
+	data->TermChar = buf[0];
+	return count;
+}
+static DEVICE_ATTR_RW(TermChar);
+
+#define data_attribute(name)						\
+static ssize_t name##_show(struct device *dev,				\
+			   struct device_attribute *attr, char *buf)	\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+									\
+	return sprintf(buf, "%d\n", data->name);			\
+}									\
+static ssize_t name##_store(struct device *dev,				\
+			    struct device_attribute *attr,		\
+			    const char *buf, size_t count)		\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\
+	ssize_t result;							\
+	unsigned val;							\
+									\
+	result = sscanf(buf, "%u\n", &val);				\
+	if (result != 1)						\
+		result = -EINVAL;					\
+	data->name = val;						\
+	if (result < 0)							\
+		return result;						\
+	else								\
+		return count;						\
+}									\
+static DEVICE_ATTR_RW(name)
+
+data_attribute(TermCharEnabled);
+data_attribute(auto_abort);
+
+static struct attribute *data_attrs[] = {
+	&dev_attr_TermChar.attr,
+	&dev_attr_TermCharEnabled.attr,
+	&dev_attr_auto_abort.attr,
+	NULL,
+};
+
+static const struct attribute_group data_attr_grp = {
+	.attrs = data_attrs,
+};
+
+static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data)
+{
+	struct device *dev;
+	u8 *buffer;
+	int rv;
+
+	dev = &data->intf->dev;
+
+	buffer = kmalloc(2, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rv = usb_control_msg(data->usb_dev,
+			     usb_rcvctrlpipe(data->usb_dev, 0),
+			     USBTMC_REQUEST_INDICATOR_PULSE,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			     0, 0, buffer, 0x01, USBTMC_TIMEOUT);
+
+	if (rv < 0) {
+		dev_err(dev, "usb_control_msg returned %d\n", rv);
+		goto exit;
+	}
+
+	dev_dbg(dev, "INDICATOR_PULSE returned %x\n", buffer[0]);
+
+	if (buffer[0] != USBTMC_STATUS_SUCCESS) {
+		dev_err(dev, "INDICATOR_PULSE returned %x\n", buffer[0]);
+		rv = -EPERM;
+		goto exit;
+	}
+	rv = 0;
+
+exit:
+	kfree(buffer);
+	return rv;
+}
+
+/*
+ * Get the usb timeout value
+ */
+static int usbtmc_ioctl_get_timeout(struct usbtmc_file_data *file_data,
+				void __user *arg)
+{
+	u32 timeout;
+
+	timeout = file_data->timeout;
+
+	return put_user(timeout, (__u32 __user *)arg);
+}
+
+/*
+ * Set the usb timeout value
+ */
+static int usbtmc_ioctl_set_timeout(struct usbtmc_file_data *file_data,
+				void __user *arg)
+{
+	u32 timeout;
+
+	if (get_user(timeout, (__u32 __user *)arg))
+		return -EFAULT;
+
+	/* Note that timeout = 0 means
+	 * MAX_SCHEDULE_TIMEOUT in usb_control_msg
+	 */
+	if (timeout < USBTMC_MIN_TIMEOUT)
+		return -EINVAL;
+
+	file_data->timeout = timeout;
+
+	return 0;
+}
+
+/*
+ * enables/disables sending EOM on write
+ */
+static int usbtmc_ioctl_eom_enable(struct usbtmc_file_data *file_data,
+				void __user *arg)
+{
+	u8 eom_enable;
+
+	if (copy_from_user(&eom_enable, arg, sizeof(eom_enable)))
+		return -EFAULT;
+
+	if (eom_enable > 1)
+		return -EINVAL;
+
+	file_data->eom_val = eom_enable;
+
+	return 0;
+}
+
+/*
+ * Configure termination character for read()
+ */
+static int usbtmc_ioctl_config_termc(struct usbtmc_file_data *file_data,
+				void __user *arg)
+{
+	struct usbtmc_termchar termc;
+
+	if (copy_from_user(&termc, arg, sizeof(termc)))
+		return -EFAULT;
+
+	if ((termc.term_char_enabled > 1) ||
+		(termc.term_char_enabled &&
+		!(file_data->data->capabilities.device_capabilities & 1)))
+		return -EINVAL;
+
+	file_data->term_char = termc.term_char;
+	file_data->term_char_enabled = termc.term_char_enabled;
+
+	return 0;
+}
+
+static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usbtmc_file_data *file_data;
+	struct usbtmc_device_data *data;
+	int retval = -EBADRQC;
+
+	file_data = file->private_data;
+	data = file_data->data;
+
+	mutex_lock(&data->io_mutex);
+	if (data->zombie) {
+		retval = -ENODEV;
+		goto skip_io_on_zombie;
+	}
+
+	switch (cmd) {
+	case USBTMC_IOCTL_CLEAR_OUT_HALT:
+		retval = usbtmc_ioctl_clear_out_halt(data);
+		break;
+
+	case USBTMC_IOCTL_CLEAR_IN_HALT:
+		retval = usbtmc_ioctl_clear_in_halt(data);
+		break;
+
+	case USBTMC_IOCTL_INDICATOR_PULSE:
+		retval = usbtmc_ioctl_indicator_pulse(data);
+		break;
+
+	case USBTMC_IOCTL_CLEAR:
+		retval = usbtmc_ioctl_clear(data);
+		break;
+
+	case USBTMC_IOCTL_ABORT_BULK_OUT:
+		retval = usbtmc_ioctl_abort_bulk_out(data);
+		break;
+
+	case USBTMC_IOCTL_ABORT_BULK_IN:
+		retval = usbtmc_ioctl_abort_bulk_in(data);
+		break;
+
+	case USBTMC_IOCTL_GET_TIMEOUT:
+		retval = usbtmc_ioctl_get_timeout(file_data,
+						  (void __user *)arg);
+		break;
+
+	case USBTMC_IOCTL_SET_TIMEOUT:
+		retval = usbtmc_ioctl_set_timeout(file_data,
+						  (void __user *)arg);
+		break;
+
+	case USBTMC_IOCTL_EOM_ENABLE:
+		retval = usbtmc_ioctl_eom_enable(file_data,
+						 (void __user *)arg);
+		break;
+
+	case USBTMC_IOCTL_CONFIG_TERMCHAR:
+		retval = usbtmc_ioctl_config_termc(file_data,
+						   (void __user *)arg);
+		break;
+
+	case USBTMC488_IOCTL_GET_CAPS:
+		retval = copy_to_user((void __user *)arg,
+				&data->usb488_caps,
+				sizeof(data->usb488_caps));
+		if (retval)
+			retval = -EFAULT;
+		break;
+
+	case USBTMC488_IOCTL_READ_STB:
+		retval = usbtmc488_ioctl_read_stb(file_data,
+						  (void __user *)arg);
+		break;
+
+	case USBTMC488_IOCTL_REN_CONTROL:
+		retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
+						USBTMC488_REQUEST_REN_CONTROL);
+		break;
+
+	case USBTMC488_IOCTL_GOTO_LOCAL:
+		retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
+						USBTMC488_REQUEST_GOTO_LOCAL);
+		break;
+
+	case USBTMC488_IOCTL_LOCAL_LOCKOUT:
+		retval = usbtmc488_ioctl_simple(data, (void __user *)arg,
+						USBTMC488_REQUEST_LOCAL_LOCKOUT);
+		break;
+
+	case USBTMC488_IOCTL_TRIGGER:
+		retval = usbtmc488_ioctl_trigger(file_data);
+		break;
+	}
+
+skip_io_on_zombie:
+	mutex_unlock(&data->io_mutex);
+	return retval;
+}
+
+static int usbtmc_fasync(int fd, struct file *file, int on)
+{
+	struct usbtmc_file_data *file_data = file->private_data;
+
+	return fasync_helper(fd, file, on, &file_data->data->fasync);
+}
+
+static __poll_t usbtmc_poll(struct file *file, poll_table *wait)
+{
+	struct usbtmc_file_data *file_data = file->private_data;
+	struct usbtmc_device_data *data = file_data->data;
+	__poll_t mask;
+
+	mutex_lock(&data->io_mutex);
+
+	if (data->zombie) {
+		mask = EPOLLHUP | EPOLLERR;
+		goto no_poll;
+	}
+
+	poll_wait(file, &data->waitq, wait);
+
+	mask = (atomic_read(&file_data->srq_asserted)) ? EPOLLPRI : 0;
+
+no_poll:
+	mutex_unlock(&data->io_mutex);
+	return mask;
+}
+
+static const struct file_operations fops = {
+	.owner		= THIS_MODULE,
+	.read		= usbtmc_read,
+	.write		= usbtmc_write,
+	.open		= usbtmc_open,
+	.release	= usbtmc_release,
+	.unlocked_ioctl	= usbtmc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= usbtmc_ioctl,
+#endif
+	.fasync         = usbtmc_fasync,
+	.poll           = usbtmc_poll,
+	.llseek		= default_llseek,
+};
+
+static struct usb_class_driver usbtmc_class = {
+	.name =		"usbtmc%d",
+	.fops =		&fops,
+	.minor_base =	USBTMC_MINOR_BASE,
+};
+
+static void usbtmc_interrupt(struct urb *urb)
+{
+	struct usbtmc_device_data *data = urb->context;
+	struct device *dev = &data->intf->dev;
+	int status = urb->status;
+	int rv;
+
+	dev_dbg(&data->intf->dev, "int status: %d len %d\n",
+		status, urb->actual_length);
+
+	switch (status) {
+	case 0: /* SUCCESS */
+		/* check for valid STB notification */
+		if (data->iin_buffer[0] > 0x81) {
+			data->bNotify1 = data->iin_buffer[0];
+			data->bNotify2 = data->iin_buffer[1];
+			atomic_set(&data->iin_data_valid, 1);
+			wake_up_interruptible(&data->waitq);
+			goto exit;
+		}
+		/* check for SRQ notification */
+		if (data->iin_buffer[0] == 0x81) {
+			unsigned long flags;
+			struct list_head *elem;
+
+			if (data->fasync)
+				kill_fasync(&data->fasync,
+					SIGIO, POLL_PRI);
+
+			spin_lock_irqsave(&data->dev_lock, flags);
+			list_for_each(elem, &data->file_list) {
+				struct usbtmc_file_data *file_data;
+
+				file_data = list_entry(elem,
+						       struct usbtmc_file_data,
+						       file_elem);
+				file_data->srq_byte = data->iin_buffer[1];
+				atomic_set(&file_data->srq_asserted, 1);
+			}
+			spin_unlock_irqrestore(&data->dev_lock, flags);
+
+			dev_dbg(dev, "srq received bTag %x stb %x\n",
+				(unsigned int)data->iin_buffer[0],
+				(unsigned int)data->iin_buffer[1]);
+			wake_up_interruptible_all(&data->waitq);
+			goto exit;
+		}
+		dev_warn(dev, "invalid notification: %x\n",
+			 data->iin_buffer[0]);
+		break;
+	case -EOVERFLOW:
+		dev_err(dev, "overflow with length %d, actual length is %d\n",
+			data->iin_wMaxPacketSize, urb->actual_length);
+		/* fall through */
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -EILSEQ:
+	case -ETIME:
+	case -EPIPE:
+		/* urb terminated, clean up */
+		dev_dbg(dev, "urb terminated, status: %d\n", status);
+		return;
+	default:
+		dev_err(dev, "unknown status received: %d\n", status);
+	}
+exit:
+	rv = usb_submit_urb(urb, GFP_ATOMIC);
+	if (rv)
+		dev_err(dev, "usb_submit_urb failed: %d\n", rv);
+}
+
+static void usbtmc_free_int(struct usbtmc_device_data *data)
+{
+	if (!data->iin_ep_present || !data->iin_urb)
+		return;
+	usb_kill_urb(data->iin_urb);
+	kfree(data->iin_buffer);
+	usb_free_urb(data->iin_urb);
+	kref_put(&data->kref, usbtmc_delete);
+}
+
+static int usbtmc_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usbtmc_device_data *data;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *bulk_in, *bulk_out, *int_in;
+	int retcode;
+
+	dev_dbg(&intf->dev, "%s called\n", __func__);
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->intf = intf;
+	data->id = id;
+	data->usb_dev = usb_get_dev(interface_to_usbdev(intf));
+	usb_set_intfdata(intf, data);
+	kref_init(&data->kref);
+	mutex_init(&data->io_mutex);
+	init_waitqueue_head(&data->waitq);
+	atomic_set(&data->iin_data_valid, 0);
+	INIT_LIST_HEAD(&data->file_list);
+	spin_lock_init(&data->dev_lock);
+
+	data->zombie = 0;
+
+	/* Initialize USBTMC bTag and other fields */
+	data->bTag	= 1;
+	data->TermCharEnabled = 0;
+	data->TermChar = '\n';
+	/*  2 <= bTag <= 127   USBTMC-USB488 subclass specification 4.3.1 */
+	data->iin_bTag = 2;
+
+	/* USBTMC devices have only one setting, so use that */
+	iface_desc = data->intf->cur_altsetting;
+	data->ifnum = iface_desc->desc.bInterfaceNumber;
+
+	/* Find bulk endpoints */
+	retcode = usb_find_common_endpoints(iface_desc,
+			&bulk_in, &bulk_out, NULL, NULL);
+	if (retcode) {
+		dev_err(&intf->dev, "bulk endpoints not found\n");
+		goto err_put;
+	}
+
+	data->bulk_in = bulk_in->bEndpointAddress;
+	dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in);
+
+	data->bulk_out = bulk_out->bEndpointAddress;
+	dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", data->bulk_out);
+
+	/* Find int endpoint */
+	retcode = usb_find_int_in_endpoint(iface_desc, &int_in);
+	if (!retcode) {
+		data->iin_ep_present = 1;
+		data->iin_ep = int_in->bEndpointAddress;
+		data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in);
+		data->iin_interval = int_in->bInterval;
+		dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
+				data->iin_ep);
+	}
+
+	retcode = get_capabilities(data);
+	if (retcode)
+		dev_err(&intf->dev, "can't read capabilities\n");
+	else
+		retcode = sysfs_create_group(&intf->dev.kobj,
+					     &capability_attr_grp);
+
+	if (data->iin_ep_present) {
+		/* allocate int urb */
+		data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!data->iin_urb) {
+			retcode = -ENOMEM;
+			goto error_register;
+		}
+
+		/* Protect interrupt in endpoint data until iin_urb is freed */
+		kref_get(&data->kref);
+
+		/* allocate buffer for interrupt in */
+		data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
+					GFP_KERNEL);
+		if (!data->iin_buffer) {
+			retcode = -ENOMEM;
+			goto error_register;
+		}
+
+		/* fill interrupt urb */
+		usb_fill_int_urb(data->iin_urb, data->usb_dev,
+				usb_rcvintpipe(data->usb_dev, data->iin_ep),
+				data->iin_buffer, data->iin_wMaxPacketSize,
+				usbtmc_interrupt,
+				data, data->iin_interval);
+
+		retcode = usb_submit_urb(data->iin_urb, GFP_KERNEL);
+		if (retcode) {
+			dev_err(&intf->dev, "Failed to submit iin_urb\n");
+			goto error_register;
+		}
+	}
+
+	retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp);
+
+	retcode = usb_register_dev(intf, &usbtmc_class);
+	if (retcode) {
+		dev_err(&intf->dev, "Not able to get a minor"
+			" (base %u, slice default): %d\n", USBTMC_MINOR_BASE,
+			retcode);
+		goto error_register;
+	}
+	dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor);
+
+	return 0;
+
+error_register:
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	usbtmc_free_int(data);
+err_put:
+	kref_put(&data->kref, usbtmc_delete);
+	return retcode;
+}
+
+static void usbtmc_disconnect(struct usb_interface *intf)
+{
+	struct usbtmc_device_data *data  = usb_get_intfdata(intf);
+
+	usb_deregister_dev(intf, &usbtmc_class);
+	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+	mutex_lock(&data->io_mutex);
+	data->zombie = 1;
+	wake_up_interruptible_all(&data->waitq);
+	mutex_unlock(&data->io_mutex);
+	usbtmc_free_int(data);
+	kref_put(&data->kref, usbtmc_delete);
+}
+
+static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	/* this driver does not have pending URBs */
+	return 0;
+}
+
+static int usbtmc_resume(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static struct usb_driver usbtmc_driver = {
+	.name		= "usbtmc",
+	.id_table	= usbtmc_devices,
+	.probe		= usbtmc_probe,
+	.disconnect	= usbtmc_disconnect,
+	.suspend	= usbtmc_suspend,
+	.resume		= usbtmc_resume,
+};
+
+module_usb_driver(usbtmc_driver);
+
+MODULE_LICENSE("GPL");