lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1435700650-640-2-git-send-email-jwhite@codeweavers.com>
Date:	Tue, 30 Jun 2015 16:44:10 -0500
From:	Jeremy White <jwhite@...eweavers.com>
To:	hdegoede@...hat.com, spice-devel@...ts.freedesktop.org,
	linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

This module uses the usbredir protocol and user space tools,
which are used by the SPICE project.

Signed-off-by: Jeremy White <jwhite@...eweavers.com>
---
 MAINTAINERS                                 |    6 +
 drivers/usb/Kconfig                         |    2 +
 drivers/usb/Makefile                        |    1 +
 drivers/usb/usbredir/Kconfig                |   25 +
 drivers/usb/usbredir/Makefile               |    4 +
 drivers/usb/usbredir/README                 |   20 +
 drivers/usb/usbredir/TODO                   |   12 +
 drivers/usb/usbredir/device.c               |  327 +++++
 drivers/usb/usbredir/hub.c                  |  489 ++++++++
 drivers/usb/usbredir/main.c                 |  100 ++
 drivers/usb/usbredir/redir.c                |  535 ++++++++
 drivers/usb/usbredir/rx.c                   |   40 +
 drivers/usb/usbredir/strtok_r.c             |   69 +
 drivers/usb/usbredir/strtok_r.h             |   26 +
 drivers/usb/usbredir/sysfs.c                |  145 +++
 drivers/usb/usbredir/tx.c                   |  151 +++
 drivers/usb/usbredir/urb.c                  |  266 ++++
 drivers/usb/usbredir/usbredir.h             |  225 ++++
 drivers/usb/usbredir/usbredirfilter.c       |  294 +++++
 drivers/usb/usbredir/usbredirfilter.h       |  144 +++
 drivers/usb/usbredir/usbredirparser.c       | 1795 +++++++++++++++++++++++++++
 drivers/usb/usbredir/usbredirparser.h       |  386 ++++++
 drivers/usb/usbredir/usbredirproto-compat.h |   88 ++
 drivers/usb/usbredir/usbredirproto.h        |  309 +++++
 24 files changed, 5459 insertions(+)
 create mode 100644 drivers/usb/usbredir/Kconfig
 create mode 100644 drivers/usb/usbredir/Makefile
 create mode 100644 drivers/usb/usbredir/README
 create mode 100644 drivers/usb/usbredir/TODO
 create mode 100644 drivers/usb/usbredir/device.c
 create mode 100644 drivers/usb/usbredir/hub.c
 create mode 100644 drivers/usb/usbredir/main.c
 create mode 100644 drivers/usb/usbredir/redir.c
 create mode 100644 drivers/usb/usbredir/rx.c
 create mode 100644 drivers/usb/usbredir/strtok_r.c
 create mode 100644 drivers/usb/usbredir/strtok_r.h
 create mode 100644 drivers/usb/usbredir/sysfs.c
 create mode 100644 drivers/usb/usbredir/tx.c
 create mode 100644 drivers/usb/usbredir/urb.c
 create mode 100644 drivers/usb/usbredir/usbredir.h
 create mode 100644 drivers/usb/usbredir/usbredirfilter.c
 create mode 100644 drivers/usb/usbredir/usbredirfilter.h
 create mode 100644 drivers/usb/usbredir/usbredirparser.c
 create mode 100644 drivers/usb/usbredir/usbredirparser.h
 create mode 100644 drivers/usb/usbredir/usbredirproto-compat.h
 create mode 100644 drivers/usb/usbredir/usbredirproto.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d8afd29..738a80d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10363,6 +10363,12 @@ S:	Maintained
 F:	drivers/usb/usbip/
 F:	tools/usb/usbip/
 
+USBREDIR IP DRIVER
+M:	Jeremy White <jwhite@...eweavers.com>
+L:	linux-usb@...r.kernel.org
+S:	Maintained
+F:	drivers/usb/usbredir/
+
 USB PEGASUS DRIVER
 M:	Petko Manolov <petkan@...leusys.com>
 L:	linux-usb@...r.kernel.org
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ed451d..18940f5 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -94,6 +94,8 @@ source "drivers/usb/image/Kconfig"
 
 source "drivers/usb/usbip/Kconfig"
 
+source "drivers/usb/usbredir/Kconfig"
+
 endif
 
 source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d8926c6..384fd9f 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_USB_GADGET)	+= gadget/
 obj-$(CONFIG_USB_COMMON)	+= common/
 
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
+obj-$(CONFIG_USBREDIR)		+= usbredir/
diff --git a/drivers/usb/usbredir/Kconfig b/drivers/usb/usbredir/Kconfig
new file mode 100644
index 0000000..284fd02
--- /dev/null
+++ b/drivers/usb/usbredir/Kconfig
@@ -0,0 +1,25 @@
+config USBREDIR
+	tristate "USBREDIR support"
+	depends on USB && NET
+	---help---
+	  This enables connecting a remote USB device over IP using
+          the USBREDIR protocol.  This module provides a sysfs attach
+          interface which, if given a socket connected to a remote
+          usbredirserver, will enable the remote device to behave as
+          though it were connected to the system running this module.
+
+          For more information and user space tools, refer to the
+          USBREDIR project, which can be found at
+          http://www.spice-space.org/page/UsbRedir.
+
+	  To compile this as a module, choose M here: the module will
+	  be called usbredir.
+
+	  If unsure, say N.
+
+config USBREDIR_DEBUG
+	bool "Debug messages for USBREDIR"
+	depends on USBREDIR
+	---help---
+	  This enables the debug messages from the USBREDIR drivers.
+
diff --git a/drivers/usb/usbredir/Makefile b/drivers/usb/usbredir/Makefile
new file mode 100644
index 0000000..531ff31
--- /dev/null
+++ b/drivers/usb/usbredir/Makefile
@@ -0,0 +1,4 @@
+ccflags-$(CONFIG_USBREDIR_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_USBREDIR) += usbredir.o
+usbredir-y := main.o sysfs.o hub.o device.o urb.o redir.o tx.o rx.o usbredirparser.o strtok_r.o usbredirfilter.o
diff --git a/drivers/usb/usbredir/README b/drivers/usb/usbredir/README
new file mode 100644
index 0000000..217a2e4
--- /dev/null
+++ b/drivers/usb/usbredir/README
@@ -0,0 +1,20 @@
+USB Redirection Kernel Module
+
+This module allows a Linux system to instatiate USB devices
+that are located on a remote device.  The USB data is transferred
+over a socket using the USBREDIR protocol, which is generally
+used in conjunction with the SPICE project.
+
+You will need the USBREDIR user space tools.  They can
+be found at http://www.spice-space.org/page/UsbRedir.
+
+To use, start the usbredirserver on a remote system.
+For example,
+ ./usbredirserver --port 4000 125f:db8a
+will export my ADATA thumb drive on the remote system.
+
+Next, on the local system, connect a socket and relay that to
+the kernel module.  The connectkernel utility will do this as follows:
+  ./connectkernel adata4000 my.remote.device.com 4000
+
+The device should attach and be usable on the local system.
diff --git a/drivers/usb/usbredir/TODO b/drivers/usb/usbredir/TODO
new file mode 100644
index 0000000..605551a
--- /dev/null
+++ b/drivers/usb/usbredir/TODO
@@ -0,0 +1,12 @@
+TODO:
+  *  Note - during testing, having a device die unexpectedly
+     could go on to cause fairly serious problems.  Need to
+     explore that.
+  *  Still getting sporadic failures
+     - One clue - got an unexpected rc from the write in redir.c
+  *  Hmm.  KFP_ATOMIC on our mallocs may be better than _KERNEL...
+  *  Read and deal with the many TODOs
+  *  Not just hacking the usbredirparser, but proper patches
+     - The filter needs love.  Use strsep, not strtok?
+  *  Expose a sysfs attribute and have udev use it
+  *  Turn on more capabilities and watch it go
diff --git a/drivers/usb/usbredir/device.c b/drivers/usb/usbredir/device.c
new file mode 100644
index 0000000..114bc48
--- /dev/null
+++ b/drivers/usb/usbredir/device.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/net.h>
+
+#include "usbredir.h"
+
+void usbredir_device_init(struct usbredir_device *udev, int port,
+			  struct usbredir_hub *hub)
+{
+	memset(udev, 0, sizeof(*udev));
+
+	udev->rhport = port;
+	udev->hub = hub;
+	atomic_set(&udev->active, 0);
+	spin_lock_init(&udev->lock);
+	spin_lock_init(&udev->lists_lock);
+
+	INIT_LIST_HEAD(&udev->urblist_rx);
+	INIT_LIST_HEAD(&udev->urblist_tx);
+	INIT_LIST_HEAD(&udev->unlink_tx);
+	INIT_LIST_HEAD(&udev->unlink_rx);
+
+	init_waitqueue_head(&udev->waitq_tx);
+}
+
+/* Presumes udev->lock is held on entry */
+void usbredir_device_allocate(struct usbredir_device *udev,
+			      const char *devid,
+			      struct socket *socket)
+{
+	char pname[32];
+
+	udev->parser = redir_parser_init(udev);
+	if (!udev->parser) {
+		pr_err("Unable to allocate USBREDIR parser.\n");
+		return;
+	}
+
+	udev->devid  = kstrdup(devid, GFP_ATOMIC);
+	atomic_set(&udev->active, 1);
+	udev->socket = socket;
+
+	udev->port_status = 0;
+
+	/* TODO - safe to hold udev lock through thread creation? */
+	sprintf(pname, "usbredir/rx:%d", udev->rhport);
+	udev->rx = kthread_run(usbredir_rx_loop, udev, pname);
+	sprintf(pname, "usbredir/tx:%d", udev->rhport);
+	udev->tx = kthread_run(usbredir_tx_loop, udev, pname);
+}
+
+void usbredir_device_cleanup_unlink(struct usbredir_device *udev)
+{
+	struct usbredir_unlink *unlink, *tmp;
+
+	spin_lock(&udev->lists_lock);
+	list_for_each_entry_safe(unlink, tmp, &udev->unlink_tx, list) {
+		list_del(&unlink->list);
+		kfree(unlink);
+	}
+
+	list_for_each_entry_safe(unlink, tmp, &udev->unlink_rx, list) {
+		list_del(&unlink->list);
+		kfree(unlink);
+	}
+	spin_unlock(&udev->lists_lock);
+}
+
+void usbredir_device_deallocate(struct usbredir_device *udev,
+				bool stoprx, bool stoptx)
+{
+	pr_debug("%s %d/%d (active %d)\n", __func__, udev->hub->id,
+		 udev->rhport, atomic_read(&udev->active));
+	if (atomic_dec_if_positive(&udev->active) < 0)
+		return;
+
+	/* Release the rx thread */
+	spin_lock(&udev->lock);
+	if (udev->socket)
+		kernel_sock_shutdown(udev->socket, SHUT_RDWR);
+	spin_unlock(&udev->lock);
+
+	/* Release the tx thread */
+	wake_up_interruptible(&udev->waitq_tx);
+
+	/* The key is that kthread_stop waits until that thread has exited,
+	 *   so we don't clean up resources still in use */
+	if (stoprx && udev->rx)
+		kthread_stop(udev->rx);
+
+	if (stoptx && udev->tx)
+		kthread_stop(udev->tx);
+
+	spin_lock(&udev->lock);
+
+	udev->rx = NULL;
+	udev->tx = NULL;
+
+	if (udev->socket) {
+		sockfd_put(udev->socket);
+		udev->socket = NULL;
+	}
+
+	usb_put_dev(udev->usb_dev);
+	udev->usb_dev = NULL;
+
+	kfree(udev->devid);
+	udev->devid = NULL;
+
+	if (udev->parser) {
+		usbredirparser_destroy(udev->parser);
+		udev->parser = NULL;
+	}
+
+	usbredir_device_cleanup_unlink(udev);
+	usbredir_urb_cleanup_urblists(udev);
+
+	spin_unlock(&udev->lock);
+}
+
+static u32 speed_to_portflag(enum usb_device_speed speed)
+{
+	switch (speed) {
+	case usb_redir_speed_low:   return USB_PORT_STAT_LOW_SPEED;
+	case usb_redir_speed_high:  return USB_PORT_STAT_HIGH_SPEED;
+
+	case usb_redir_speed_full:
+	case usb_redir_speed_super:
+	default:		    return 0;
+	}
+}
+
+/* TODO - no thought at all to Super speed stuff... */
+void usbredir_device_connect(struct usbredir_device *udev)
+{
+	spin_lock(&udev->lock);
+	pr_debug("%s %d/%d:%s\n", __func__,
+		 udev->hub->id, udev->rhport, udev->devid);
+	udev->port_status |= USB_PORT_STAT_CONNECTION |
+			    (1 << USB_PORT_FEAT_C_CONNECTION);
+	udev->port_status |= speed_to_portflag(udev->connect_header.speed);
+	spin_unlock(&udev->lock);
+
+	usb_hcd_poll_rh_status(udev->hub->hcd);
+}
+
+void usbredir_device_disconnect(struct usbredir_device *udev)
+{
+	spin_lock(&udev->lock);
+	pr_debug("%s %d/%d:%s\n", __func__,
+		 udev->hub->id, udev->rhport, udev->devid);
+	udev->port_status  &= ~USB_PORT_STAT_CONNECTION;
+	udev->port_status  |= (1 << USB_PORT_FEAT_C_CONNECTION);
+	spin_unlock(&udev->lock);
+
+	usb_hcd_poll_rh_status(udev->hub->hcd);
+}
+
+
+
+static struct usbredir_device *usbredir_device_get(struct usbredir_hub *hub,
+						 int rhport)
+{
+	struct usbredir_device *udev;
+
+	if (rhport < 0 || rhport >= hub->device_count) {
+		pr_err("invalid port number %d\n", rhport);
+		return NULL;
+	}
+	udev = hub->devices + rhport;
+
+	spin_lock(&hub->lock);
+	spin_lock(&udev->lock);
+	return udev;
+}
+
+static void usbredir_device_put(struct usbredir_device *udev)
+{
+	spin_unlock(&udev->lock);
+	spin_unlock(&udev->hub->lock);
+}
+
+int usbredir_device_clear_port_feature(struct usbredir_hub *hub,
+			       int rhport, u16 wValue)
+{
+	struct usbredir_device *udev = usbredir_device_get(hub, rhport);
+
+	if (!udev)
+		return -ENODEV;
+
+	switch (wValue) {
+	case USB_PORT_FEAT_SUSPEND:
+		pr_debug(" ClearPortFeature: USB_PORT_FEAT_SUSPEND\n");
+		if (udev->port_status & USB_PORT_STAT_SUSPEND) {
+			/* 20msec signaling */
+			/* TODO - see note on suspend/resume below */
+			hub->resuming = 1;
+			hub->re_timeout =
+				jiffies + msecs_to_jiffies(20);
+		}
+		break;
+	case USB_PORT_FEAT_POWER:
+		pr_debug(" ClearPortFeature: USB_PORT_FEAT_POWER\n");
+		udev->port_status = 0;
+		hub->resuming = 0;
+		break;
+	case USB_PORT_FEAT_C_RESET:
+		pr_debug(" ClearPortFeature: USB_PORT_FEAT_C_RESET\n");
+		/* TODO - USB 3.0 stuff as well? */
+		switch (udev->connect_header.speed) {
+		case usb_redir_speed_high:
+			udev->port_status |= USB_PORT_STAT_HIGH_SPEED;
+			break;
+		case usb_redir_speed_low:
+			udev->port_status |= USB_PORT_STAT_LOW_SPEED;
+			break;
+		default:
+			break;
+		}
+	default:
+		pr_debug(" ClearPortFeature: default %x\n", wValue);
+		udev->port_status &= ~(1 << wValue);
+		break;
+	}
+
+	usbredir_device_put(udev);
+
+	return 0;
+}
+
+int usbredir_device_port_status(struct usbredir_hub *hub, int rhport, char *buf)
+{
+	struct usbredir_device *udev = usbredir_device_get(hub, rhport);
+
+	if (!udev)
+		return -ENODEV;
+
+	pr_debug("%s %d/%d 0x%x\n", __func__,
+		 udev->hub->id, rhport, udev->port_status);
+
+	/* TODO - the logic on resume/reset etc is really
+	 *   just blindly copied from USBIP.  Make sure
+	 *   this eventually gets thoughtful review and testing. */
+
+	/* whoever resets or resumes must GetPortStatus to
+	 * complete it!!
+	 */
+	if (hub->resuming && time_after(jiffies, hub->re_timeout)) {
+		udev->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+		udev->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
+		hub->resuming = 0;
+		hub->re_timeout = 0;
+	}
+
+	if ((udev->port_status & (1 << USB_PORT_FEAT_RESET)) &&
+	     time_after(jiffies, hub->re_timeout)) {
+		udev->port_status |= (1 << USB_PORT_FEAT_C_RESET);
+		udev->port_status &= ~(1 << USB_PORT_FEAT_RESET);
+		hub->re_timeout = 0;
+
+		if (atomic_read(&udev->active)) {
+			pr_debug(" enable rhport %d\n", rhport);
+			udev->port_status |= USB_PORT_STAT_ENABLE;
+		}
+	}
+
+	((__le16 *) buf)[0] = cpu_to_le16(udev->port_status);
+	((__le16 *) buf)[1] =
+		cpu_to_le16(udev->port_status >> 16);
+
+	pr_debug(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0],
+			  ((u16 *)buf)[1]);
+
+	usbredir_device_put(udev);
+
+	return 0;
+}
+
+int usbredir_device_set_port_feature(struct usbredir_hub *hub,
+			       int rhport, u16 wValue)
+{
+	struct usbredir_device *udev = usbredir_device_get(hub, rhport);
+
+	if (!udev)
+		return -ENODEV;
+
+	switch (wValue) {
+	case USB_PORT_FEAT_SUSPEND:
+		pr_debug(" SetPortFeature: USB_PORT_FEAT_SUSPEND\n");
+		break;
+	case USB_PORT_FEAT_RESET:
+		pr_debug(" SetPortFeature: USB_PORT_FEAT_RESET\n");
+		udev->port_status &= ~USB_PORT_STAT_ENABLE;
+
+		/* 50msec reset signaling */
+		/* TODO - why?  Seems like matching core/hub.c
+		 *	SHORT_RESET_TIME would be better */
+		hub->re_timeout = jiffies + msecs_to_jiffies(50);
+
+		/* FALLTHROUGH */
+	default:
+		pr_debug(" SetPortFeature: default %d\n", wValue);
+		udev->port_status |= (1 << wValue);
+		break;
+	}
+
+	usbredir_device_put(udev);
+
+	return 0;
+}
diff --git a/drivers/usb/usbredir/hub.c b/drivers/usb/usbredir/hub.c
new file mode 100644
index 0000000..f6eeb20
--- /dev/null
+++ b/drivers/usb/usbredir/hub.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/kthread.h>
+
+#include "usbredir.h"
+
+static spinlock_t hubs_lock;
+static struct list_head hubs;
+static atomic_t hub_count;
+
+static int usbredir_hcd_start(struct usb_hcd *hcd)
+{
+	struct usbredir_hub *hub = usbredir_hub_from_hcd(hcd);
+	int i;
+
+	spin_lock(&hub->lock);
+	pr_debug("%s %d\n", __func__, hub->id);
+
+	hub->device_count = devices_per_hub;
+	hub->devices = kcalloc(hub->device_count, sizeof(*hub->devices),
+			       GFP_ATOMIC);
+	if (!hub->devices) {
+		spin_unlock(&hub->lock);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < hub->device_count; i++)
+		usbredir_device_init(hub->devices + i, i, hub);
+
+	hcd->power_budget = 0; /* no limit */
+	hcd->uses_new_polling = 1;
+	atomic_set(&hub->aseqnum, 0);
+	spin_unlock(&hub->lock);
+
+	return 0;
+}
+
+static void usbredir_hub_stop(struct usbredir_hub *hub)
+{
+	int i;
+
+	pr_debug("%s %d\n", __func__, hub->id);
+
+	for (i = 0; i < hub->device_count && hub->devices; i++) {
+		usbredir_device_disconnect(hub->devices + i);
+		usbredir_device_deallocate(hub->devices + i, true, true);
+	}
+
+	spin_lock(&hub->lock);
+	kfree(hub->devices);
+	hub->devices = NULL;
+	hub->device_count = 0;
+	spin_unlock(&hub->lock);
+}
+
+static void usbredir_hcd_stop(struct usb_hcd *hcd)
+{
+	usbredir_hub_stop(usbredir_hub_from_hcd(hcd));
+}
+
+static int usbredir_get_frame_number(struct usb_hcd *hcd)
+{
+	pr_err("TODO: get_frame_number: not implemented\n");
+	return 0;
+}
+
+static int usbredir_hub_status(struct usb_hcd *hcd, char *buf)
+{
+	struct usbredir_hub *hub = usbredir_hub_from_hcd(hcd);
+	int		ret;
+	int		rhport;
+	int		changed = 0;
+
+	spin_lock(&hub->lock);
+
+	pr_debug("%s %d\n", __func__, hub->id);
+
+	ret = DIV_ROUND_UP(hub->device_count + 1, 8);
+	memset(buf, 0, ret);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		pr_debug("hw accessible flag not on?\n");
+		spin_unlock(&hub->lock);
+		return 0;
+	}
+
+	/* check pseudo status register for each port */
+	for (rhport = 0; rhport < hub->device_count; rhport++) {
+		struct usbredir_device *udev = hub->devices + rhport;
+
+		spin_lock(&udev->lock);
+		if (udev->port_status &
+			((USB_PORT_STAT_C_CONNECTION
+			  | USB_PORT_STAT_C_ENABLE
+			  | USB_PORT_STAT_C_SUSPEND
+			  | USB_PORT_STAT_C_OVERCURRENT
+			  | USB_PORT_STAT_C_RESET) << 16)) {
+
+			/* The status of a port has been changed, */
+			pr_debug("port %d status changed\n", rhport);
+
+			buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8;
+			changed = 1;
+		}
+		spin_unlock(&udev->lock);
+	}
+
+	spin_unlock(&hub->lock);
+
+	if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
+		usb_hcd_resume_root_hub(hcd);
+
+	pr_debug("%s %schanged\n", __func__, changed ? "" : "un");
+
+	return changed ? ret : 0;
+}
+
+static inline void usbredir_hub_descriptor(struct usbredir_hub *hub,
+					   struct usb_hub_descriptor *desc)
+{
+	memset(desc, 0, sizeof(*desc));
+/* TODO - where do these magic numbers come from? */
+	desc->bDescriptorType = 0x29;
+	desc->bDescLength = 9;
+	desc->wHubCharacteristics = cpu_to_le16(
+		HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+	desc->bNbrPorts = hub->device_count;
+	desc->u.hs.DeviceRemovable[0] = 0xff;
+	desc->u.hs.DeviceRemovable[1] = 0xff;
+}
+
+static int usbredir_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+			    u16 wIndex, char *buf, u16 wLength)
+{
+	struct usbredir_hub *hub;
+	int             ret = 0;
+	int		rhport;
+
+	if (!HCD_HW_ACCESSIBLE(hcd))
+		return -ETIMEDOUT;
+
+	hub = usbredir_hub_from_hcd(hcd);
+
+	pr_debug("%s hub %d: ", __func__, hub->id);
+	pr_debug("[wValue %x|wIndex%u|wLength %u]",
+		 wValue, wIndex, wLength);
+
+	/* wIndex is 1 based */
+	rhport = ((__u8)(wIndex & 0x00ff)) - 1;
+
+	switch (typeReq) {
+	case ClearHubFeature:
+		pr_debug(" ClearHubFeature\n");
+		break;
+	case SetHubFeature:
+		pr_debug(" SetHubFeature\n");
+		ret = -EPIPE;
+		break;
+	case GetHubDescriptor:
+		pr_debug(" GetHubDescriptor\n");
+		usbredir_hub_descriptor(hub, (struct usb_hub_descriptor *) buf);
+		break;
+	case GetHubStatus:
+		pr_debug(" GetHubStatus\n");
+		*(__le32 *) buf = cpu_to_le32(0);
+		break;
+	case ClearPortFeature:
+		pr_debug(" ClearPortFeature\n");
+		return usbredir_device_clear_port_feature(hub, rhport, wValue);
+	case SetPortFeature:
+		pr_debug(" SetPortFeature\n");
+		return usbredir_device_set_port_feature(hub, rhport, wValue);
+	case GetPortStatus:
+		pr_debug(" GetPortStatus\n");
+		return usbredir_device_port_status(hub, rhport, buf);
+	default:
+		pr_debug(" unknown type %x\n", typeReq);
+		pr_err("usbredir_hub_control: no handler for request %x\n",
+		       typeReq);
+
+		/* "protocol stall" on error */
+		ret = -EPIPE;
+	}
+	return ret;
+}
+
+#ifdef CONFIG_PM
+/* FIXME: suspend/resume */
+static int usbredir_bus_suspend(struct usb_hcd *hcd)
+{
+	dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
+
+	hcd->state = HC_STATE_SUSPENDED;
+
+	return 0;
+}
+
+static int usbredir_bus_resume(struct usb_hcd *hcd)
+{
+	int rc = 0;
+
+	dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
+
+	if (!HCD_HW_ACCESSIBLE(hcd))
+		rc = -ESHUTDOWN;
+	else
+		hcd->state = HC_STATE_RUNNING;
+	return rc;
+}
+#else
+
+#define usbredir_bus_suspend      NULL
+#define usbredir_bus_resume       NULL
+#endif
+
+
+static void usbredir_release_hub_dev(struct device *dev)
+{
+	/* TODO - what do we need to implement here? */
+	pr_err("%s: not implemented\n", __func__);
+}
+
+static int usbredir_register_hub(struct usbredir_hub *hub)
+{
+	int ret;
+
+	hub->pdev.name = driver_name;
+	hub->pdev.id = hub->id;
+	hub->pdev.dev.release = usbredir_release_hub_dev;
+
+	ret = platform_device_register(&hub->pdev);
+	if (ret) {
+		pr_err("Unable to register platform device %d\n", hub->id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void usbredir_unregister_hub(struct usbredir_hub *hub)
+{
+	platform_device_unregister(&hub->pdev);
+}
+
+
+static struct hc_driver usbredir_hc_driver = {
+	.description	= driver_name,
+	.product_desc	= driver_desc,
+	.hcd_priv_size	= sizeof(struct usbredir_hub *),
+
+	/* TODO = what other flags are available and what of USB3? */
+	.flags		= HCD_USB2,
+
+	.start		= usbredir_hcd_start,
+	.stop		= usbredir_hcd_stop,
+
+	.urb_enqueue	= usbredir_urb_enqueue,
+	.urb_dequeue	= usbredir_urb_dequeue,
+
+	.get_frame_number = usbredir_get_frame_number,
+
+	.hub_status_data = usbredir_hub_status,
+	.hub_control    = usbredir_hub_control,
+	.bus_suspend	= usbredir_bus_suspend,
+	.bus_resume	= usbredir_bus_resume,
+};
+
+
+static int usbredir_create_hcd(struct usbredir_hub *hub)
+{
+	int ret;
+
+	hub->hcd = usb_create_hcd(&usbredir_hc_driver, &hub->pdev.dev,
+			     dev_name(&hub->pdev.dev));
+	if (!hub->hcd) {
+		pr_err("usb_create_hcd failed\n");
+		return -ENOMEM;
+	}
+
+	/* TODO - review if we want to has_tt, and anything like it... */
+	hub->hcd->has_tt = 1;
+
+	/* TODO - no one else stores a pointer
+	*        may want to rethink the structure.
+	*        Question:  do we really need to create the pdev first? */
+	*((struct usbredir_hub **) hub->hcd->hcd_priv) = hub;
+
+	ret = usb_add_hcd(hub->hcd, 0, 0);
+	if (ret != 0) {
+		pr_err("usb_add_hcd failed %d\n", ret);
+		usb_put_hcd(hub->hcd);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void usbredir_destroy_hcd(struct usbredir_hub *hub)
+{
+	if (hub->hcd) {
+		usb_remove_hcd(hub->hcd);
+		usb_put_hcd(hub->hcd);
+	}
+	hub->hcd = NULL;
+}
+
+struct usbredir_hub *usbredir_hub_create(void)
+{
+	struct usbredir_hub *hub;
+	int id = atomic_inc_return(&hub_count);
+
+	if (id > max_hubs)
+		goto dec_exit;
+
+	hub = kzalloc(sizeof(*hub), GFP_ATOMIC);
+	if (!hub)
+		goto dec_exit;
+	hub->id = id - 1;
+
+	if (usbredir_register_hub(hub)) {
+		kfree(hub);
+		goto dec_exit;
+	}
+
+	if (usbredir_create_hcd(hub)) {
+		usbredir_unregister_hub(hub);
+		kfree(hub);
+		goto dec_exit;
+	}
+
+	spin_lock(&hubs_lock);
+	list_add_tail(&hub->list, &hubs);
+	spin_unlock(&hubs_lock);
+	return hub;
+dec_exit:
+	atomic_dec(&hub_count);
+	return NULL;
+}
+
+void usbredir_hub_destroy(struct usbredir_hub *hub)
+{
+	usbredir_hub_stop(hub);
+	usbredir_destroy_hcd(hub);
+	usbredir_unregister_hub(hub);
+}
+
+struct usbredir_device *usbredir_hub_find_device(const char *devid)
+{
+	struct usbredir_device *ret = NULL;
+	struct usbredir_hub *hub;
+	int i;
+
+	spin_lock(&hubs_lock);
+	list_for_each_entry(hub, &hubs, list) {
+		spin_lock(&hub->lock);
+		for (i = 0; i < hub->device_count; i++) {
+			struct usbredir_device *udev = hub->devices + i;
+
+			spin_lock(&udev->lock);
+			if (atomic_read(&udev->active) &&
+			    udev->devid &&
+			    strcmp(udev->devid, devid) == 0)
+				ret = udev;
+			spin_unlock(&udev->lock);
+			if (ret)
+				break;
+		}
+		spin_unlock(&hub->lock);
+		if (ret)
+			break;
+	}
+	spin_unlock(&hubs_lock);
+	return ret;
+}
+
+struct usbredir_device *usbredir_hub_allocate_device(const char *devid,
+						     struct socket *socket)
+{
+	int found = 0;
+	struct usbredir_hub *hub;
+	struct usbredir_device *udev;
+	int i;
+
+	spin_lock(&hubs_lock);
+	list_for_each_entry(hub, &hubs, list) {
+		spin_lock(&hub->lock);
+		for (i = 0; i < hub->device_count; i++) {
+			udev = hub->devices + i;
+			spin_lock(&udev->lock);
+			if (!atomic_read(&udev->active)) {
+				found++;
+				/* Note: lock is *held* */
+				break;
+			}
+			spin_unlock(&udev->lock);
+		}
+		spin_unlock(&hub->lock);
+		if (found)
+			break;
+	}
+	spin_unlock(&hubs_lock);
+
+	if (!found) {
+		hub = usbredir_hub_create();
+		if (!hub)
+			return NULL;
+
+		return usbredir_hub_allocate_device(devid, socket);
+	}
+
+	usbredir_device_allocate(udev, devid, socket);
+
+	spin_unlock(&udev->lock);
+
+	return udev;
+}
+
+int usbredir_hub_show_global_status(char *out)
+{
+	int count = 0;
+	int active = 0;
+	int used = 0;
+
+	struct usbredir_hub *hub;
+	struct usbredir_device *udev;
+	int i;
+
+	spin_lock(&hubs_lock);
+	list_for_each_entry(hub, &hubs, list) {
+		spin_lock(&hub->lock);
+		for (i = 0; i < hub->device_count; count++, i++) {
+			udev = hub->devices + i;
+			spin_lock(&udev->lock);
+			active += atomic_read(&udev->active);
+			if (udev->usb_dev)
+				used++;
+			spin_unlock(&udev->lock);
+		}
+		spin_unlock(&hub->lock);
+	}
+	spin_unlock(&hubs_lock);
+
+	sprintf(out, "%d/%d hubs. %d/%d devices (%d active, %d used).\n",
+			atomic_read(&hub_count), max_hubs,
+			count, max_hubs * devices_per_hub, active, used);
+
+	return strlen(out);
+}
+
+
+int usbredir_hub_init(void)
+{
+	INIT_LIST_HEAD(&hubs);
+	atomic_set(&hub_count, 0);
+	spin_lock_init(&hubs_lock);
+
+	return 0;
+}
+
+void usbredir_hub_exit(void)
+{
+	struct usbredir_hub *hub, *tmp;
+
+	spin_lock(&hubs_lock);
+	list_for_each_entry_safe(hub, tmp, &hubs, list) {
+		usbredir_hub_destroy(hub);
+		list_del(&hub->list);
+		kfree(hub);
+	}
+	spin_unlock(&hubs_lock);
+}
diff --git a/drivers/usb/usbredir/main.c b/drivers/usb/usbredir/main.c
new file mode 100644
index 0000000..b63e6c6
--- /dev/null
+++ b/drivers/usb/usbredir/main.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 Jeremy White
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+
+#include "usbredir.h"
+
+#define DRIVER_NAME	"usbredir"
+#define DRIVER_AUTHOR	"Jeremy White"
+#define DRIVER_DESC	"USBREDIR Host Controller Driver"
+#define DRIVER_VERSION	USBREDIR_MODULE_VERSION
+
+const char driver_name[] = DRIVER_NAME;
+const char driver_desc[] = DRIVER_DESC;
+
+
+static struct platform_driver usbredir_driver = {
+	.driver	= {
+		.name = driver_name,
+	},
+};
+
+static int __init usbredir_main_init(void)
+{
+	int ret;
+
+	pr_debug("usbredir loaded\n");
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	if (devices_per_hub > USB_MAXCHILDREN) {
+		pr_err("Error:  cannot use %d devices per hub; max %d\n",
+		       devices_per_hub, USB_MAXCHILDREN);
+		return -ENODEV;
+	}
+
+
+	ret = platform_driver_register(&usbredir_driver);
+	if (ret) {
+		pr_err("Unable to register usbredir_driver.\n");
+		return ret;
+	}
+
+	ret = usbredir_hub_init();
+	if (ret) {
+		platform_driver_unregister(&usbredir_driver);
+		return ret;
+	}
+
+	ret = usbredir_sysfs_register(&usbredir_driver.driver);
+	if (ret) {
+		pr_err("Unable to create sysfs files for usbredir driver.\n");
+		usbredir_hub_exit();
+		platform_driver_unregister(&usbredir_driver);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit usbredir_main_exit(void)
+{
+	usbredir_sysfs_unregister(&usbredir_driver.driver);
+	usbredir_hub_exit();
+	platform_driver_unregister(&usbredir_driver);
+	pr_debug("usbredir exited\n");
+}
+
+unsigned int max_hubs = 64;
+module_param(max_hubs, uint, S_IRUSR|S_IWUSR);
+MODULE_PARM_DESC(max_hubs, "Maximum number of USB hubs to create; default 64");
+
+unsigned int devices_per_hub = 16;
+module_param(devices_per_hub, uint, S_IRUSR|S_IWUSR);
+MODULE_PARM_DESC(devices_per_hub,
+		"Maximum number of devices per hub; default 16");
+
+module_init(usbredir_main_init);
+module_exit(usbredir_main_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/usbredir/redir.c b/drivers/usb/usbredir/redir.c
new file mode 100644
index 0000000..33d9475
--- /dev/null
+++ b/drivers/usb/usbredir/redir.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2015 Jeremy White
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/net.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+
+#include "usbredirparser.h"
+#include "usbredir.h"
+
+
+#define TODO_IMPLEMENT pr_err("Error: %s unimplemented.\n", __func__)
+
+static void redir_log(void *priv, int level, const char *msg)
+{
+	switch (level) {
+	case usbredirparser_error:
+		pr_err("%s", msg);
+		break;
+
+	case usbredirparser_warning:
+		pr_warn("%s", msg);
+		break;
+
+	case usbredirparser_info:
+		pr_info("%s", msg);
+		break;
+
+	default:
+		pr_debug("%s", msg);
+		break;
+	}
+}
+
+static int redir_read(void *priv, uint8_t *data, int count)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+	struct msghdr msg;
+	struct kvec iov;
+	struct socket *socket;
+	int rc;
+
+	if (kthread_should_stop() || !atomic_read(&udev->active))
+		return -ESRCH;
+
+	spin_lock(&udev->lock);
+	socket = udev->socket;
+	/* TODO - reference/dereference the socket? */
+	spin_unlock(&udev->lock);
+
+	socket->sk->sk_allocation = GFP_NOIO;
+	iov.iov_base    = data;
+	iov.iov_len     = count;
+	msg.msg_name    = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = MSG_NOSIGNAL;
+
+	rc = kernel_recvmsg(socket, &msg, &iov, 1, count, MSG_WAITALL);
+
+	return rc;
+}
+
+static int redir_write(void *priv, uint8_t *data, int count)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+	struct msghdr msg;
+	struct kvec iov;
+	int rc;
+	struct socket *socket;
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&iov, 0, sizeof(iov));
+	msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
+	iov.iov_base = data;
+	iov.iov_len  = count;
+
+	spin_lock(&udev->lock);
+	socket = udev->socket;
+	spin_unlock(&udev->lock);
+
+	rc = kernel_sendmsg(socket, &msg, &iov, 1, count);
+	/* TODO - In theory, a return of 0 should be okay,
+	 *        but, again, in theory, it will cause an error. */
+	if (rc <= 0)
+		pr_err("Error: TODO - unexpected write return code %d.\n", rc);
+
+	return rc;
+}
+
+
+/* Locking functions for use by multithread apps */
+static void *redir_alloc_lock(void)
+{
+	struct semaphore *s = kmalloc(sizeof(*s), GFP_KERNEL);
+
+	sema_init(s, 1);
+	return s;
+}
+
+static void redir_lock(void *lock)
+{
+	while (down_interruptible((struct semaphore *) lock))
+		;
+}
+
+static void redir_unlock(void *lock)
+{
+	up((struct semaphore *) lock);
+}
+
+static void redir_free_lock(void *lock)
+{
+	kfree(lock);
+}
+
+
+/* The below callbacks are called when a complete packet of the relevant
+   type has been received.
+
+   Note that the passed in packet-type-specific-header's lifetime is only
+   guarenteed to be that of the callback.
+
+*/
+static void redir_hello(void *priv, struct usb_redir_hello_header *hello)
+{
+	pr_debug("Hello!\n");
+}
+
+static void redir_device_connect(void *priv,
+	struct usb_redir_device_connect_header *device_connect)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+
+	pr_debug("  connect: class %2d subclass %2d protocol %2d",
+		device_connect->device_class, device_connect->device_subclass,
+		device_connect->device_protocol);
+	pr_debug("  vendor 0x%04x product %04x\n",
+		device_connect->vendor_id, device_connect->product_id);
+
+	spin_lock(&udev->lock);
+	udev->connect_header = *device_connect;
+	spin_unlock(&udev->lock);
+
+	usbredir_device_connect(udev);
+}
+
+static void redir_device_disconnect(void *priv)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_reset(void *priv)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_interface_info(void *priv,
+	struct usb_redir_interface_info_header *info)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+	int i;
+
+	for (i = 0; i < info->interface_count; i++) {
+		pr_debug("interface %d class %2d subclass %2d protocol %2d",
+			info->interface[i], info->interface_class[i],
+			info->interface_subclass[i],
+			info->interface_protocol[i]);
+	}
+
+	spin_lock(&udev->lock);
+	udev->info_header = *info;
+	spin_unlock(&udev->lock);
+}
+
+static void redir_ep_info(void *priv,
+	struct usb_redir_ep_info_header *ep_info)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+
+	spin_lock(&udev->lock);
+	udev->ep_info_header = *ep_info;
+	spin_unlock(&udev->lock);
+}
+
+static void redir_set_configuration(void *priv,
+	uint64_t id,
+	struct usb_redir_set_configuration_header *set_configuration)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_get_configuration(void *priv, uint64_t id)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_configuration_status(void *priv,
+	uint64_t id,
+	struct usb_redir_configuration_status_header *configuration_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_set_alt_setting(void *priv,
+	uint64_t id,
+	struct usb_redir_set_alt_setting_header *set_alt_setting)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_get_alt_setting(void *priv,
+	uint64_t id,
+	struct usb_redir_get_alt_setting_header *get_alt_setting)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_alt_setting_status(void *priv,
+	uint64_t id,
+	struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_start_iso_stream(void *priv,
+	uint64_t id,
+	struct usb_redir_start_iso_stream_header *start_iso_stream)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_stop_iso_stream(void *priv,
+	uint64_t id,
+	struct usb_redir_stop_iso_stream_header *stop_iso_stream)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_iso_stream_status(void *priv,
+	uint64_t id,
+	struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_start_interrupt_receiving(void *priv,
+	uint64_t id,
+	struct usb_redir_start_interrupt_receiving_header
+		*start_interrupt_receiving)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_stop_interrupt_receiving(void *priv,
+	uint64_t id,
+	struct usb_redir_stop_interrupt_receiving_header
+		*stop_interrupt_receiving)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_interrupt_receiving_status(void *priv,
+	uint64_t id,
+	struct usb_redir_interrupt_receiving_status_header
+		*interrupt_receiving_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_alloc_bulk_streams(void *priv,
+	uint64_t id,
+	struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_free_bulk_streams(void *priv,
+	uint64_t id,
+	struct usb_redir_free_bulk_streams_header *free_bulk_streams)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_bulk_streams_status(void *priv,
+	uint64_t id,
+	struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_cancel_data_packet(void *priv, uint64_t id)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_filter_reject(void *priv)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_filter_filter(void *priv,
+	struct usbredirfilter_rule *rules, int rules_count)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_device_disconnect_ack(void *priv)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_start_bulk_receiving(void *priv,
+	uint64_t id,
+	struct usb_redir_start_bulk_receiving_header *start_bulk_receiving)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_stop_bulk_receiving(void *priv,
+	uint64_t id,
+	struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_bulk_receiving_status(void *priv,
+	uint64_t id,
+	struct usb_redir_bulk_receiving_status_header *bulk_receiving_status)
+{
+	TODO_IMPLEMENT;
+}
+
+static int redir_map_status(int redir_status)
+{
+	switch (redir_status) {
+	case usb_redir_success:
+		return 0;
+	case usb_redir_cancelled:
+		return -ENOENT;
+	case usb_redir_inval:
+		return -EINVAL;
+	case usb_redir_stall:
+		return -EPIPE;
+	case usb_redir_timeout:
+		return -ETIMEDOUT;
+	case usb_redir_babble:
+		return -EOVERFLOW;
+		/* Catchall error condition */
+	case usb_redir_ioerror:
+	default:
+		return -ENODEV;
+	}
+}
+
+
+static void redir_control_packet(void *priv,
+	uint64_t id,
+	struct usb_redir_control_packet_header *control_header,
+	uint8_t *data, int data_len)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+	struct urb *urb;
+
+	urb = usbredir_pop_rx_urb(udev, id);
+	if (!urb) {
+		pr_err("Error: control id %lu with no matching entry.\n",
+		       (unsigned long) id);
+		return;
+	}
+
+	/* TODO - handle more than this flavor... */
+	urb->status = redir_map_status(control_header->status);
+	if (usb_pipein(urb->pipe)) {
+		urb->actual_length = min_t(u32, data_len,
+					 urb->transfer_buffer_length);
+		if (urb->transfer_buffer)
+			memcpy(urb->transfer_buffer, data, urb->actual_length);
+	} else {
+		urb->actual_length = control_header->length;
+	}
+
+	usb_hcd_unlink_urb_from_ep(udev->hub->hcd, urb);
+	usb_hcd_giveback_urb(udev->hub->hcd, urb, urb->status);
+}
+
+static void redir_bulk_packet(void *priv,
+	uint64_t id,
+	struct usb_redir_bulk_packet_header *bulk_header,
+	uint8_t *data, int data_len)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) priv;
+	struct urb *urb;
+
+	urb = usbredir_pop_rx_urb(udev, id);
+	if (!urb) {
+		pr_err("Error: bulk id %lu with no matching entry.\n",
+		       (unsigned long) id);
+		return;
+	}
+
+	urb->status = redir_map_status(bulk_header->status);
+	if (usb_pipein(urb->pipe)) {
+		urb->actual_length = min_t(u32, data_len,
+					 urb->transfer_buffer_length);
+		if (urb->transfer_buffer)
+			memcpy(urb->transfer_buffer, data, urb->actual_length);
+	} else {
+		urb->actual_length = bulk_header->length;
+	}
+
+	/* TODO - what to do with stream_id */
+	/* TODO - handle more than this flavor... */
+
+	usb_hcd_unlink_urb_from_ep(udev->hub->hcd, urb);
+	usb_hcd_giveback_urb(udev->hub->hcd, urb, urb->status);
+}
+
+static void redir_iso_packet(void *priv,
+	uint64_t id,
+	struct usb_redir_iso_packet_header *iso_header,
+	uint8_t *data, int data_len)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_interrupt_packet(void *priv,
+	uint64_t id,
+	struct usb_redir_interrupt_packet_header *interrupt_header,
+	uint8_t *data, int data_len)
+{
+	TODO_IMPLEMENT;
+}
+
+static void redir_buffered_bulk_packet(void *priv, uint64_t id,
+	struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header,
+	uint8_t *data, int data_len)
+{
+	TODO_IMPLEMENT;
+}
+
+
+struct usbredirparser *redir_parser_init(void *priv)
+{
+	struct usbredirparser *parser;
+	char version[40];
+
+	uint32_t caps[USB_REDIR_CAPS_SIZE];
+
+	parser = usbredirparser_create();
+
+	parser->priv = priv;
+
+	parser->log_func = redir_log;
+	parser->read_func = redir_read;
+	parser->write_func = redir_write;
+	parser->device_connect_func = redir_device_connect;
+	parser->device_disconnect_func = redir_device_disconnect;
+	parser->reset_func = redir_reset;
+	parser->interface_info_func = redir_interface_info;
+	parser->ep_info_func = redir_ep_info;
+	parser->set_configuration_func = redir_set_configuration;
+	parser->get_configuration_func = redir_get_configuration;
+	parser->configuration_status_func = redir_configuration_status;
+	parser->set_alt_setting_func = redir_set_alt_setting;
+	parser->get_alt_setting_func = redir_get_alt_setting;
+	parser->alt_setting_status_func = redir_alt_setting_status;
+	parser->start_iso_stream_func = redir_start_iso_stream;
+	parser->stop_iso_stream_func = redir_stop_iso_stream;
+	parser->iso_stream_status_func = redir_iso_stream_status;
+	parser->start_interrupt_receiving_func =
+		redir_start_interrupt_receiving;
+	parser->stop_interrupt_receiving_func = redir_stop_interrupt_receiving;
+	parser->interrupt_receiving_status_func =
+		redir_interrupt_receiving_status;
+	parser->alloc_bulk_streams_func = redir_alloc_bulk_streams;
+	parser->free_bulk_streams_func = redir_free_bulk_streams;
+	parser->bulk_streams_status_func = redir_bulk_streams_status;
+	parser->cancel_data_packet_func = redir_cancel_data_packet;
+	parser->control_packet_func = redir_control_packet;
+	parser->bulk_packet_func = redir_bulk_packet;
+	parser->iso_packet_func = redir_iso_packet;
+	parser->interrupt_packet_func = redir_interrupt_packet;
+	parser->alloc_lock_func = redir_alloc_lock;
+	parser->lock_func = redir_lock;
+	parser->unlock_func = redir_unlock;
+	parser->free_lock_func = redir_free_lock;
+	parser->hello_func = redir_hello;
+	parser->filter_reject_func = redir_filter_reject;
+	parser->filter_filter_func = redir_filter_filter;
+	parser->device_disconnect_ack_func = redir_device_disconnect_ack;
+	parser->start_bulk_receiving_func = redir_start_bulk_receiving;
+	parser->stop_bulk_receiving_func = redir_stop_bulk_receiving;
+	parser->bulk_receiving_status_func = redir_bulk_receiving_status;
+	parser->buffered_bulk_packet_func = redir_buffered_bulk_packet;
+
+	memset(caps, 0, sizeof(caps));
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
+
+	/* TODO - figure out which of these we really can use */
+#if defined(USE_ALL_CAPS)
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_device_disconnect_ack);
+	usbredirparser_caps_set_cap(caps,
+				usb_redir_cap_ep_info_max_packet_size);
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+	usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
+#endif
+
+	sprintf(version, "kmodule v%s. Protocol %x",
+		USBREDIR_MODULE_VERSION, USBREDIR_VERSION);
+	usbredirparser_init(parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
+
+	return parser;
+}
+
diff --git a/drivers/usb/usbredir/rx.c b/drivers/usb/usbredir/rx.c
new file mode 100644
index 0000000..92ffa97
--- /dev/null
+++ b/drivers/usb/usbredir/rx.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Jeremy White
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kthread.h>
+
+#include "usbredir.h"
+
+int usbredir_rx_loop(void *data)
+{
+	struct usbredir_device *udev = data;
+	int rc;
+
+	while (!kthread_should_stop() && atomic_read(&udev->active)) {
+		rc = usbredirparser_do_read(udev->parser);
+		if (rc != -EAGAIN) {
+			pr_info("usbredir/rx:%d connection closed\n",
+				udev->rhport);
+			break;
+		}
+	}
+
+	pr_debug("%s exit\n", __func__);
+
+	usbredir_device_disconnect(udev);
+	usbredir_device_deallocate(udev, false, true);
+
+	return 0;
+}
diff --git a/drivers/usb/usbredir/strtok_r.c b/drivers/usb/usbredir/strtok_r.c
new file mode 100644
index 0000000..2c761e1
--- /dev/null
+++ b/drivers/usb/usbredir/strtok_r.c
@@ -0,0 +1,69 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(__KERNEL__)
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+
+/* Parse S into tokens separated by characters in DELIM.
+   If S is NULL, the saved pointer in SAVE_PTR is used as
+   the next starting point.  For example:
+	char s[] = "-abc-=-def";
+	char *sp;
+	x = strtok_r(s, "-", &sp);	// x = "abc", sp = "=-def"
+	x = strtok_r(NULL, "-=", &sp);	// x = "def", sp = NULL
+	x = strtok_r(NULL, "=", &sp);	// x = NULL
+		// s = "abc\0-def\0"
+*/
+char *
+glibc_strtok_r (char *s, const char *delim, char **save_ptr)
+{
+  char *token;
+
+  if (s == NULL)
+    s = *save_ptr;
+
+  /* Scan leading delimiters.  */
+  s += strspn (s, delim);
+  if (*s == '\0')
+    {
+      *save_ptr = s;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk (token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    *save_ptr = strchr (token, '\0');
+  else
+    {
+      /* Terminate the token and make *SAVE_PTR point past it.  */
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  return token;
+}
diff --git a/drivers/usb/usbredir/strtok_r.h b/drivers/usb/usbredir/strtok_r.h
new file mode 100644
index 0000000..50e6e0e
--- /dev/null
+++ b/drivers/usb/usbredir/strtok_r.h
@@ -0,0 +1,26 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _USBREDIRPARSER_STRTOK_H_
+#define _USBREDIRPARSER_STRTOK_H_
+
+char *
+glibc_strtok_r(char *s, const char *delim, char **save_ptr);
+
+#endif
diff --git a/drivers/usb/usbredir/sysfs.c b/drivers/usb/usbredir/sysfs.c
new file mode 100644
index 0000000..e8edef3
--- /dev/null
+++ b/drivers/usb/usbredir/sysfs.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/file.h>
+#include <linux/net.h>
+
+#include "usbredir.h"
+
+
+static ssize_t status_show(struct device_driver *driver, char *out)
+{
+	return usbredir_hub_show_global_status(out);
+}
+static DRIVER_ATTR_RO(status);
+
+static ssize_t store_attach(struct device_driver *driver,
+			    const char *buf, size_t count)
+{
+	struct socket *socket;
+	int sockfd = 0;
+	char devid[256];
+	int err;
+
+	/*
+	 * usbredir sysfs attach file
+	 * @sockfd: socket descriptor of an established TCP connection
+	 * @devid: user supplied unique device identifier
+	 */
+	memset(devid, 0, sizeof(devid));
+	if (sscanf(buf, "%u %255s", &sockfd, devid) != 2)
+		return -EINVAL;
+
+	pr_debug("attach sockfd(%u) devid(%s)\n", sockfd, devid);
+
+	socket = sockfd_lookup(sockfd, &err);
+	if (!socket)
+		return -EINVAL;
+
+	if (usbredir_hub_find_device(devid)) {
+		pr_err("%s: already in use\n", devid);
+		sockfd_put(socket);
+		return -EINVAL;
+	}
+
+	if (!usbredir_hub_allocate_device(devid, socket)) {
+		pr_err("%s: unable to create\n", devid);
+		sockfd_put(socket);
+		return -EINVAL;
+	}
+
+	return count;
+}
+static DRIVER_ATTR(attach, S_IWUSR, NULL, store_attach);
+
+
+static ssize_t store_detach(struct device_driver *driver,
+			    const char *buf, size_t count)
+{
+	char devid[256];
+	struct usbredir_device *udev;
+
+	/*
+	 * usbredir sysfs detach file
+	 * @devid: user supplied unique device identifier
+	 */
+	memset(devid, 0, sizeof(devid));
+	if (sscanf(buf, "%255s", devid) != 1)
+		return -EINVAL;
+
+	pr_debug("detach devid(%s)\n", devid);
+
+	udev = usbredir_hub_find_device(devid);
+	if (!udev) {
+		pr_warn("USBREDIR device %s detach requested, but not found\n",
+			devid);
+		return count;
+	}
+
+	usbredir_device_disconnect(udev);
+	usbredir_device_deallocate(udev, true, true);
+
+	return count;
+}
+static DRIVER_ATTR(detach, S_IWUSR, NULL, store_detach);
+
+
+/**
+ * usbredir_sysfs_register()
+ * @driver	The platform driver associated with usbredir
+ *
+ * This function will register new sysfs files called 'attach', 'detach',
+ * and 'status'.
+ *
+ * To start a new connection, a user space program should establish
+ * a socket that is connected to a process that provides a USB device
+ * and that speaks the USBREDIR protocol.  The usbredirserver program
+ * is one such example.
+ *
+ * Next, the user space program should write that socket as well as a
+ * unique device id of no more than 255 characters to the 'attach' file.
+ * That should begin a connection.
+ *
+ * Writing the same id to the 'detach' file should end the connection,
+ * and examining the contents of the 'status' file should show the number
+ * of connections.
+ *
+ */
+int usbredir_sysfs_register(struct device_driver *driver)
+{
+	int ret;
+
+	ret = driver_create_file(driver, &driver_attr_status);
+	if (ret)
+		return ret;
+
+	ret = driver_create_file(driver, &driver_attr_detach);
+	if (ret)
+		return ret;
+
+	return driver_create_file(driver, &driver_attr_attach);
+}
+
+/**
+ * usbredir_sysfs_unregister()
+ * @dev The device driver associated with usbredir
+ */
+void usbredir_sysfs_unregister(struct device_driver *dev)
+{
+	driver_remove_file(dev, &driver_attr_status);
+	driver_remove_file(dev, &driver_attr_detach);
+	driver_remove_file(dev, &driver_attr_attach);
+}
diff --git a/drivers/usb/usbredir/tx.c b/drivers/usb/usbredir/tx.c
new file mode 100644
index 0000000..aae7a10
--- /dev/null
+++ b/drivers/usb/usbredir/tx.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+#include "usbredir.h"
+
+static struct usbredir_urb *get_next_urb(struct usbredir_device *udev)
+{
+	struct usbredir_urb *uurb, *tmp;
+
+	spin_lock(&udev->lists_lock);
+
+	list_for_each_entry_safe(uurb, tmp, &udev->urblist_tx, list) {
+		list_move_tail(&uurb->list, &udev->urblist_rx);
+		spin_unlock(&udev->lists_lock);
+		return uurb;
+	}
+
+	spin_unlock(&udev->lists_lock);
+
+	return NULL;
+}
+
+static void send_packet(struct usbredir_device *udev, struct usbredir_urb *uurb)
+{
+	struct urb *urb = uurb->urb;
+	__u8 type = usb_pipetype(urb->pipe);
+
+	if (type == PIPE_CONTROL && urb->setup_packet) {
+		struct usb_ctrlrequest *ctrlreq =
+			(struct usb_ctrlrequest *) urb->setup_packet;
+		struct usb_redir_control_packet_header ctrl;
+
+		ctrl.endpoint = usb_pipeendpoint(urb->pipe) |
+				usb_pipein(urb->pipe);
+		ctrl.request = ctrlreq->bRequest;
+		ctrl.requesttype = ctrlreq->bRequestType;
+		ctrl.status = 0;
+		ctrl.value = le16_to_cpu(ctrlreq->wValue);
+		ctrl.index = le16_to_cpu(ctrlreq->wIndex);
+		ctrl.length = le16_to_cpu(ctrlreq->wLength);
+
+		usbredirparser_send_control_packet(udev->parser,
+			uurb->seqnum, &ctrl,
+			usb_pipein(urb->pipe) ?
+				NULL : urb->transfer_buffer,
+			usb_pipein(urb->pipe) ?
+				0 : urb->transfer_buffer_length);
+
+	}
+
+	if (type == PIPE_BULK) {
+		struct usb_redir_bulk_packet_header bulk;
+
+		bulk.endpoint = usb_pipeendpoint(urb->pipe) |
+				usb_pipein(urb->pipe);
+		bulk.status = 0;
+		bulk.length = urb->transfer_buffer_length & 0xFFFF;
+		bulk.stream_id = urb->stream_id;
+		bulk.length_high = urb->transfer_buffer_length >> 16;
+
+		usbredirparser_send_bulk_packet(udev->parser,
+			uurb->seqnum, &bulk,
+			usb_pipein(urb->pipe) ?
+				NULL : urb->transfer_buffer,
+			usb_pipein(urb->pipe) ?
+				0 : urb->transfer_buffer_length);
+	}
+}
+
+static struct usbredir_unlink *get_next_unlink(struct usbredir_device *udev)
+{
+	struct usbredir_unlink *unlink, *tmp;
+
+	spin_lock(&udev->lists_lock);
+
+	list_for_each_entry_safe(unlink, tmp, &udev->unlink_tx, list) {
+		list_move_tail(&unlink->list, &udev->unlink_rx);
+		spin_unlock(&udev->lists_lock);
+		return unlink;
+	}
+
+	spin_unlock(&udev->lists_lock);
+
+	return NULL;
+}
+
+static void send_unlink(struct usbredir_device *udev,
+		       struct usbredir_unlink *unlink)
+{
+	while ((unlink = get_next_unlink(udev)) != NULL) {
+		/* This is a separate TODO; need to process unlink_rx... */
+		pr_debug("TODO partially unimplemented: unlink request of ");
+		pr_debug("seqnum %d, unlink seqnum %d\n",
+			unlink->seqnum, unlink->unlink_seqnum);
+
+		/* TODO - if the other side never responds, which it may
+			not do if the seqnum doesn't match, then we
+			never clear this entry.  That's probably not ideal */
+		usbredirparser_send_cancel_data_packet(udev->parser,
+						       unlink->unlink_seqnum);
+	}
+}
+
+int usbredir_tx_loop(void *data)
+{
+	struct usbredir_device *udev = data;
+	struct usbredir_urb *uurb;
+	struct usbredir_unlink *unlink;
+
+	while (!kthread_should_stop() && atomic_read(&udev->active)) {
+		if (usbredirparser_has_data_to_write(udev->parser))
+			if (usbredirparser_do_write(udev->parser))
+				break;
+
+		while ((uurb = get_next_urb(udev)) != NULL)
+			send_packet(udev, uurb);
+
+		while ((unlink = get_next_unlink(udev)) != NULL)
+			send_unlink(udev, unlink);
+
+		/* TODO - can I check list_empty without locking... */
+		wait_event_interruptible(udev->waitq_tx,
+			 (!list_empty(&udev->urblist_tx) ||
+			  !list_empty(&udev->unlink_tx) ||
+			  kthread_should_stop() ||
+			 usbredirparser_has_data_to_write(udev->parser) ||
+			 !atomic_read(&udev->active)));
+	}
+
+	pr_debug("%s exit\n", __func__);
+	usbredir_device_disconnect(udev);
+	usbredir_device_deallocate(udev, true, false);
+
+	return 0;
+}
diff --git a/drivers/usb/usbredir/urb.c b/drivers/usb/usbredir/urb.c
new file mode 100644
index 0000000..50bec2f
--- /dev/null
+++ b/drivers/usb/usbredir/urb.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "usbredir.h"
+
+static void queue_urb(struct usbredir_device *udev, struct urb *urb)
+{
+	struct usbredir_urb *uurb;
+
+	uurb = kzalloc(sizeof(struct usbredir_urb), GFP_ATOMIC);
+	if (!uurb) {
+		usbredir_device_disconnect(udev);
+		usbredir_device_deallocate(udev, true, true);
+		return;
+	}
+
+	spin_lock(&udev->lists_lock);
+
+	uurb->seqnum = usbredir_hub_seqnum(udev->hub);
+
+	uurb->udev = udev;
+	uurb->urb = urb;
+
+	urb->hcpriv = (void *) uurb;
+
+	list_add_tail(&uurb->list, &udev->urblist_tx);
+	spin_unlock(&udev->lists_lock);
+}
+
+static bool intercept_urb_request(struct usbredir_device *udev,
+				  struct urb *urb, int *ret)
+{
+	struct device *dev = &urb->dev->dev;
+	__u8 type = usb_pipetype(urb->pipe);
+	struct usb_ctrlrequest *ctrlreq =
+		(struct usb_ctrlrequest *) urb->setup_packet;
+
+	if (usb_pipedevice(urb->pipe) != 0)
+		return false;
+
+	if (type != PIPE_CONTROL || !ctrlreq) {
+		dev_err(dev, "invalid request to devnum 0; type %x, req %p\n",
+			type, ctrlreq);
+		*ret = -EINVAL;
+		return true;
+	}
+
+	if (ctrlreq->bRequest == USB_REQ_GET_DESCRIPTOR) {
+		pr_debug("Requesting descriptor; wValue %x\n", ctrlreq->wValue);
+
+		usb_put_dev(udev->usb_dev);
+		udev->usb_dev = usb_get_dev(urb->dev);
+
+		if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8))
+			pr_debug("TODO: GetDescriptor unexpected.\n");
+
+		return false;
+	}
+
+	if (ctrlreq->bRequest == USB_REQ_SET_ADDRESS) {
+		dev_info(dev, "SetAddress Request (%d) to port %d\n",
+			 ctrlreq->wValue, udev->rhport);
+
+		usb_put_dev(udev->usb_dev);
+		udev->usb_dev = usb_get_dev(urb->dev);
+
+		if (urb->status == -EINPROGRESS) {
+			/* This request is successfully completed. */
+			/* If not -EINPROGRESS, possibly unlinked. */
+			urb->status = 0;
+		}
+		return true;
+	}
+
+	dev_err(dev,
+		"invalid request to devnum 0 bRequest %u, wValue %u\n",
+		ctrlreq->bRequest,
+		ctrlreq->wValue);
+	*ret =  -EINVAL;
+
+	return true;
+}
+
+void usbredir_urb_cleanup_urblists(struct usbredir_device *udev)
+{
+	struct usbredir_urb *uurb, *tmp;
+
+	spin_lock(&udev->lists_lock);
+	list_for_each_entry_safe(uurb, tmp, &udev->urblist_rx, list) {
+		list_del(&uurb->list);
+		usb_hcd_unlink_urb_from_ep(udev->hub->hcd, uurb->urb);
+		usb_hcd_giveback_urb(udev->hub->hcd, uurb->urb, -ENODEV);
+		kfree(uurb);
+	}
+
+	list_for_each_entry_safe(uurb, tmp, &udev->urblist_tx, list) {
+		list_del(&uurb->list);
+		usb_hcd_unlink_urb_from_ep(udev->hub->hcd, uurb->urb);
+		usb_hcd_giveback_urb(udev->hub->hcd, uurb->urb, -ENODEV);
+		kfree(uurb);
+	}
+	spin_unlock(&udev->lists_lock);
+}
+
+
+
+int usbredir_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+	struct device *dev = &urb->dev->dev;
+	int ret = 0;
+	struct usbredir_hub *hub = usbredir_hub_from_hcd(hcd);
+	struct usbredir_device *udev;
+
+	pr_debug("%s: enter, usb_hcd %p urb %p mem_flags %d\n",
+			  __func__, hcd, urb, mem_flags);
+
+	if (urb->status != -EINPROGRESS) {
+		dev_err(dev, "URB already handled!, status %d\n", urb->status);
+		return urb->status;
+	}
+
+	udev = hub->devices + urb->dev->portnum - 1;
+
+	if (!atomic_read(&udev->active)) {
+		dev_err(dev, "enqueue for inactive port %d\n", udev->rhport);
+		return -ENODEV;
+	}
+
+	ret = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (ret) {
+		usb_hcd_giveback_urb(hub->hcd, urb, urb->status);
+		return ret;
+	}
+
+	if (intercept_urb_request(udev, urb, &ret)) {
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
+		usb_hcd_giveback_urb(hub->hcd, urb, urb->status);
+		return ret;
+	}
+
+	queue_urb(udev, urb);
+	wake_up_interruptible(&udev->waitq_tx);
+	return ret;
+}
+
+int usbredir_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+	struct usbredir_urb *uurb;
+	struct usbredir_device *udev;
+	struct usbredir_hub *hub = usbredir_hub_from_hcd(hcd);
+	int ret = 0;
+
+	pr_debug("%s %p\n", __func__, urb);
+
+	uurb = urb->hcpriv;
+	if (!uurb) {
+		/* URB was never linked! or will be soon given back by
+		 * rx_loop. */
+		return 0;
+	}
+
+	ret = usb_hcd_check_unlink_urb(hcd, urb, status);
+	if (ret) {
+		/* TODO - figure out if this is an unlink send case as well */
+		return ret;
+	}
+
+	udev = uurb->udev;
+	if (atomic_read(&udev->active)) {
+		struct usbredir_unlink *unlink;
+
+		spin_lock(&udev->lists_lock);
+
+		unlink = kzalloc(sizeof(struct usbredir_unlink), GFP_ATOMIC);
+		if (!unlink) {
+			spin_unlock(&udev->lists_lock);
+			/* TODO complain somehow... */
+			return -ENOMEM;
+		}
+
+		unlink->seqnum = usbredir_hub_seqnum(hub);
+		unlink->unlink_seqnum = uurb->seqnum;
+		list_add_tail(&unlink->list, &udev->unlink_tx);
+		/* TODO - are we failing to pass through the status here? */
+
+		spin_unlock(&udev->lists_lock);
+
+		wake_up(&udev->waitq_tx);
+	} else {
+		/* Connection is dead already */
+		spin_lock(&udev->lists_lock);
+
+		list_del(&uurb->list);
+		kfree(uurb);
+		urb->hcpriv = NULL;
+
+		spin_unlock(&udev->lists_lock);
+
+		pr_info("gives back urb %p\n", urb);
+
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
+		usb_hcd_giveback_urb(hub->hcd, urb, urb->status);
+	}
+
+	return ret;
+}
+
+struct urb *usbredir_pop_rx_urb(struct usbredir_device *udev, int seqnum)
+{
+	struct usbredir_urb *uurb, *tmp;
+	struct urb *urb = NULL;
+	int status;
+
+	spin_lock(&udev->lists_lock);
+
+	list_for_each_entry_safe(uurb, tmp, &udev->urblist_rx, list) {
+		if (uurb->seqnum != seqnum)
+			continue;
+
+		urb = uurb->urb;
+		status = urb->status;
+
+		switch (status) {
+		case -ENOENT:
+			/* fall through */
+		case -ECONNRESET:
+			dev_info(&urb->dev->dev,
+				 "urb %p was unlinked %ssynchronuously.\n", urb,
+				 status == -ENOENT ? "" : "a");
+			break;
+		case -EINPROGRESS:
+			/* no info output */
+			break;
+		default:
+			dev_info(&urb->dev->dev,
+				 "urb %p may be in a error, status %d\n", urb,
+				 status);
+		}
+
+		list_del(&uurb->list);
+		kfree(uurb);
+		urb->hcpriv = NULL;
+
+		break;
+	}
+	spin_unlock(&udev->lists_lock);
+
+	return urb;
+}
diff --git a/drivers/usb/usbredir/usbredir.h b/drivers/usb/usbredir/usbredir.h
new file mode 100644
index 0000000..fd80059
--- /dev/null
+++ b/drivers/usb/usbredir/usbredir.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 Jeremy White based on work by
+ * Copyright (C) 2003-2008 Takahiro Hirofuchi
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __USBREDIR_H
+#define __USBREDIR_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "usbredirparser.h"
+
+#define USBREDIR_MODULE_VERSION	"1.0"
+
+
+/**
+ * struct usbredir_device - Describe a redirected usb device
+ * @lock		spinlock for port_status, usb_dev, and other misc fields
+ * @active		indicates whether the device is actively connected
+ * @usb_dev		The usb device actively in use; captured on our first
+ *			useful control urb.  We mostly use it to signal that
+ *			a device is in use.
+ * @hub			The root hub that is associated with this device.
+ * @port_status		A status variable to track usb/core.c style status;
+ *			e.g. USB_PORT_STAT_ENABLE et all
+ * @socket		The socket used to connect to the remote device
+ * @parser		A parser which drives the socket
+ * @rx			The task structure for the receive thread
+ * @tx			The task structure for the transmit thread
+ * @devid		A user space provided id for this device; must be unique
+ * @connect_header	Stored USBREDIR connection header information
+ * @info_header		Stored USBREDIR connection information
+ * @ep_info_header	Stored USBREDIR endpoint header info
+ * @rhport		0 based port number on our root hub
+ * @lists_lock		A spinlock just for the urb and unlink lists
+ * @urblist_tx		A list of urb's ready to be transmitted
+ * @urblist_rx		A list of urbs already transmitted, awaiting
+ *                      a response
+ * @unlink_tx		A list of urb's to be send to be unlinked
+ * @unlink_xx		A list of urb's we have requested cancellation of
+ * @waitq_tx		Wait queue the transmit thread sleeps on
+ */
+struct usbredir_device {
+	spinlock_t lock;
+
+	atomic_t active;
+
+	struct usb_device *usb_dev;
+	struct usbredir_hub *hub;
+
+	u32 port_status;
+
+	struct socket *socket;
+	struct usbredirparser *parser;
+
+	struct task_struct *rx;
+	struct task_struct *tx;
+
+	char *devid;
+
+	struct usb_redir_device_connect_header connect_header;
+	struct usb_redir_interface_info_header info_header;
+	struct usb_redir_ep_info_header ep_info_header;
+
+	__u32 rhport;
+
+	spinlock_t lists_lock;
+
+	struct list_head urblist_tx;
+	struct list_head urblist_rx;
+
+	struct list_head unlink_tx;
+	struct list_head unlink_rx;
+
+	wait_queue_head_t waitq_tx;
+};
+
+/**
+ * struct usbredir_hub - Describe a virtual usb hub, which can hold
+ *			 redirected usb devices
+ *
+ * @lock		Spinlock controlling access to variables,
+ *			mostly needed for timeout and resuming flags
+ * @list		Place holder for stashing inside a larger hub list
+ * @id			A numeric identifier for this hub
+ * @pdev		A registered platform device for this hub
+ * @hcd			The usb_hcd associated with this hub
+ * @device_count	The number of devices that can be connected to this hub
+ * @devices		An array of devices
+ * @aseqnum		Sequence number for transmissions
+ * @resuming		Flag to indicate we are resuming
+ * @re_timeout		General settle timeout for our hub
+ *
+ * The usbredir_hubs are allocated dynamically, as needed, but not freed.
+ * A new devices is assigned to the first hub with a free slot.
+ */
+struct usbredir_hub {
+	spinlock_t lock;
+	struct list_head	list;
+	int			id;
+	struct platform_device	pdev;
+	struct usb_hcd		*hcd;
+
+	int			device_count;
+	struct usbredir_device *devices;
+
+	atomic_t aseqnum;
+
+	unsigned resuming:1;
+	unsigned long re_timeout;
+};
+
+/**
+ * struct usbredir_urb - Hold our information regarding a URB
+ * @seqnum		Sequence number of the urb
+ * @list		Place holder to keep it in device/urblist_[rt]x
+ * @udev		A pointer to our associated device
+ * @urb			A pointer to the associated urb
+ */
+struct usbredir_urb {
+	int seqnum;
+	struct list_head list;
+
+	struct usbredir_device *udev;
+	struct urb *urb;
+};
+
+/**
+ * struct usbredir_unlink  - Hold unlink requests
+ * @seqnum		Sequence number of this request
+ * @list		Place holder to keep it in device/unlink_[rt]x
+ * @unlink_seqnum	Sequence number of the urb to unlink
+ */
+struct usbredir_unlink {
+	int seqnum;
+
+	struct list_head list;
+
+	int unlink_seqnum;
+};
+
+
+/* main.c */
+extern unsigned int max_hubs;
+extern unsigned int devices_per_hub;
+
+extern const char driver_name[];
+extern const char driver_desc[];
+
+/* sysfs.c */
+int usbredir_sysfs_register(struct device_driver *dev);
+void usbredir_sysfs_unregister(struct device_driver *dev);
+
+/* hub.c */
+int usbredir_hub_init(void);
+void usbredir_hub_exit(void);
+struct usbredir_hub *usbredir_hub_create(void);
+void usbredir_hub_destroy(struct usbredir_hub *hub);
+struct usbredir_device *usbredir_hub_allocate_device(const char *devid,
+						     struct socket *socket);
+struct usbredir_device *usbredir_hub_find_device(const char *devid);
+int usbredir_hub_show_global_status(char *out);
+
+
+/* device.c */
+void usbredir_device_init(struct usbredir_device *udev, int port,
+			  struct usbredir_hub *hub);
+void usbredir_device_allocate(struct usbredir_device *udev,
+			      const char *devid,
+			      struct socket *socket);
+void usbredir_device_deallocate(struct usbredir_device *udev,
+				bool stop, bool stoptx);
+void usbredir_device_connect(struct usbredir_device *udev);
+void usbredir_device_disconnect(struct usbredir_device *udev);
+int usbredir_device_clear_port_feature(struct usbredir_hub *hub,
+			       int rhport, u16 wValue);
+int usbredir_device_port_status(struct usbredir_hub *hub, int rhport,
+				char *buf);
+int usbredir_device_set_port_feature(struct usbredir_hub *hub,
+			       int rhport, u16 wValue);
+
+/* redir.c */
+struct usbredirparser *redir_parser_init(void *priv);
+
+/* rx.c */
+int usbredir_rx_loop(void *data);
+
+/* tx.c */
+int usbredir_tx_loop(void *data);
+
+/* urb.c */
+int usbredir_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+			    gfp_t mem_flags);
+int usbredir_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+struct urb *usbredir_pop_rx_urb(struct usbredir_device *udev, int seqnum);
+void usbredir_urb_cleanup_urblists(struct usbredir_device *udev);
+
+/* Fast lookup functions */
+static inline struct usbredir_hub *usbredir_hub_from_hcd(struct usb_hcd *hcd)
+{
+	return *(struct usbredir_hub **) hcd->hcd_priv;
+}
+
+static inline int usbredir_hub_seqnum(struct usbredir_hub *hub)
+{
+	int ret = atomic_inc_return(&hub->aseqnum);
+	/* Atomics are only guaranteed to 24 bits */
+	if (ret < 0 || ret > (1 << 23)) {
+		ret = 1;
+		atomic_set(&hub->aseqnum, 1);
+	}
+	return ret;
+}
+
+#endif /* __USBREDIR_H */
diff --git a/drivers/usb/usbredir/usbredirfilter.c b/drivers/usb/usbredir/usbredirfilter.c
new file mode 100644
index 0000000..4e3655a
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirfilter.c
@@ -0,0 +1,294 @@
+/* usbredirfilter.h usb redirection filter header
+
+   Copyright 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if defined(__KERNEL__)
+#include <linux/string.h>
+#include <linux/slab.h>
+#else
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#endif
+
+#if defined(WIN32) || defined(__KERNEL__)
+#include "strtok_r.h"
+#define strtok_r  glibc_strtok_r
+#endif
+
+#if defined(__KERNEL__)
+#define CALLOC(a, b)    kcalloc((a), (b), GFP_KERNEL)
+#define MALLOC(a)       kmalloc((a), GFP_KERNEL)
+#define STRDUP(a)       kstrdup((a), GFP_KERNEL)
+#define FREE kfree
+#else
+#define CALLOC          calloc
+#define MALLOC          malloc
+#define STRDUP          strdup
+#define FREE            free
+#endif
+#include "usbredirfilter.h"
+
+int filter_strtoi(char *str, int *res)
+{
+    long l;
+    int rc = 0;
+#if defined(__KERNEL__)
+    rc = kstrtol(str, 0, &l);
+#else
+    char *ep;
+
+    l = strtol(str, &ep, 0);
+    if (*ep)
+        rc = -1;
+#endif
+    if (rc == 0)
+        *res = (int) l;
+    return rc;
+}
+
+int usbredirfilter_string_to_rules(
+    const char *filter_str, const char *token_sep, const char *rule_sep,
+    struct usbredirfilter_rule **rules_ret, int *rules_count_ret)
+{
+    char *rule, *rule_saveptr, *token, *token_saveptr;
+    struct usbredirfilter_rule *rules = NULL;
+    int i, rules_count, *values, ret = 0;
+    char *buf = NULL;
+    const char *r;
+
+    *rules_ret = NULL;
+    *rules_count_ret = 0;
+
+    /* Figure out how much rules there are in the file, so we know how
+       much memory we must allocate for the rules array.
+       Note this will come up with a slightly too large number if there are
+       empty rule strings in the set. */
+    r = filter_str;
+    rules_count = 0;
+    while (r) {
+        r = strchr(r, rule_sep[0]);
+        if (r)
+            r++;
+        rules_count++;
+    }
+
+    rules = CALLOC(rules_count, sizeof(struct usbredirfilter_rule));
+    if (!rules)
+        return -ENOMEM;
+
+    /* Make a copy since strtok mangles the string */
+    buf = STRDUP(filter_str);
+    if (!buf) {
+        ret = -ENOMEM;
+        goto leave;
+    }
+
+    /* And actually parse the string */
+    rules_count = 0;
+    rule = strtok_r(buf, rule_sep, &rule_saveptr);
+    while (rule) {
+        /* We treat the filter rule as an array of ints for easier parsing */
+        values = (int *)&rules[rules_count];
+        token = strtok_r(rule, token_sep, &token_saveptr);
+        for (i = 0; i < 5 && token; i++) {
+	    if (filter_strtoi(token, &values[i]))
+	        break;
+            token = strtok_r(NULL, token_sep, &token_saveptr);
+        }
+        if (i != 5 || token != NULL ||
+                usbredirfilter_verify(&rules[rules_count], 1)) {
+            ret = -EINVAL;
+            goto leave;
+        }
+        rules_count++;
+        rule = strtok_r(NULL, rule_sep, &rule_saveptr);
+    }
+
+    *rules_ret = rules;
+    *rules_count_ret = rules_count;
+
+leave:
+    if (ret)
+        FREE(rules);
+    FREE(buf);
+    return ret;
+}
+
+char *usbredirfilter_rules_to_string(const struct usbredirfilter_rule *rules,
+    int rules_count, const char *token_sep, const char *rule_sep)
+{
+    int i;
+    char *str, *p;
+
+    if (usbredirfilter_verify(rules, rules_count))
+        return NULL;
+
+    /* We need 28 bytes per rule in the worst case */
+    str = MALLOC(28 * rules_count + 1);
+    if (!str)
+        return NULL;
+
+    p = str;
+    for (i = 0; i < rules_count; i++) {
+        if (rules[i].device_class != -1)
+            p += sprintf(p, "0x%02x%c", rules[i].device_class, *token_sep);
+        else
+            p += sprintf(p, "-1%c", *token_sep);
+
+        if (rules[i].vendor_id != -1)
+            p += sprintf(p, "0x%04x%c", rules[i].vendor_id, *token_sep);
+        else
+            p += sprintf(p, "-1%c", *token_sep);
+
+        if (rules[i].product_id != -1)
+            p += sprintf(p, "0x%04x%c", rules[i].product_id, *token_sep);
+        else
+            p += sprintf(p, "-1%c", *token_sep);
+
+        if (rules[i].device_version_bcd != -1)
+            p += sprintf(p, "0x%04x%c", rules[i].device_version_bcd, *token_sep);
+        else
+            p += sprintf(p, "-1%c", *token_sep);
+
+        p += sprintf(p, "%d%c", rules[i].allow ? 1:0, *rule_sep);
+    }
+
+    return str;
+}
+
+static int usbredirfilter_check1(const struct usbredirfilter_rule *rules,
+    int rules_count, uint8_t device_class, uint16_t vendor_id,
+    uint16_t product_id, uint16_t device_version_bcd, int default_allow)
+{
+    int i;
+
+    for (i = 0; i < rules_count; i++) {
+        if ((rules[i].device_class == -1 ||
+                rules[i].device_class == device_class) &&
+            (rules[i].vendor_id == -1 ||
+                rules[i].vendor_id == vendor_id) &&
+            (rules[i].product_id == -1 ||
+                rules[i].product_id == product_id) &&
+            (rules[i].device_version_bcd == -1 ||
+                rules[i].device_version_bcd == device_version_bcd)) {
+            /* Found a match ! */
+            return rules[i].allow ? 0 : -EPERM;
+        }
+    }
+
+    return default_allow ? 0 : -ENOENT;
+}
+
+int usbredirfilter_check(
+    const struct usbredirfilter_rule *rules, int rules_count,
+    uint8_t device_class, uint8_t device_subclass, uint8_t device_protocol,
+    uint8_t *interface_class, uint8_t *interface_subclass,
+    uint8_t *interface_protocol, int interface_count,
+    uint16_t vendor_id, uint16_t product_id, uint16_t device_version_bcd,
+    int flags)
+{
+    int i, rc;
+
+    if (usbredirfilter_verify(rules, rules_count))
+        return -EINVAL;
+
+    /* Check the device_class */
+    if (device_class != 0x00 && device_class != 0xef) {
+        rc = usbredirfilter_check1(rules, rules_count, device_class,
+                                   vendor_id, product_id, device_version_bcd,
+                                   flags & usbredirfilter_fl_default_allow);
+        if (rc)
+            return rc;
+    }
+
+    /* Check the interface classes */
+    for (i = 0; i < interface_count; i++) {
+        if (!(flags & usbredirfilter_fl_dont_skip_non_boot_hid) &&
+                interface_count > 1 && interface_class[i] == 0x03 &&
+                interface_subclass[i] == 0x00 && interface_protocol[i] == 0x00)
+            continue;
+
+        rc = usbredirfilter_check1(rules, rules_count, interface_class[i],
+                                   vendor_id, product_id, device_version_bcd,
+                                   flags & usbredirfilter_fl_default_allow);
+        if (rc)
+            return rc;
+    }
+
+    return 0;
+}
+
+int usbredirfilter_verify(
+    const struct usbredirfilter_rule *rules, int rules_count)
+{
+    int i;
+
+    for (i = 0; i < rules_count; i++) {
+        if (rules[i].device_class < -1 || rules[i].device_class > 255)
+            return -EINVAL;
+        if (rules[i].vendor_id < -1 || rules[i].vendor_id > 65535)
+            return -EINVAL;
+        if (rules[i].product_id < -1 || rules[i].product_id > 65535)
+            return -EINVAL;
+        if (rules[i].device_version_bcd < -1 ||
+                rules[i].device_version_bcd > 65535)
+            return -EINVAL;
+    }
+    return 0;
+}
+
+#if ! defined(__KERNEL__)
+void usbredirfilter_print(
+    const struct usbredirfilter_rule *rules, int rules_count, FILE *out)
+{
+    int i;
+    char device_class[16], vendor[16], product[16], version[16];
+
+    for (i = 0; i < rules_count; i++) {
+        if (rules[i].device_class != -1)
+            sprintf(device_class, " %02x", rules[i].device_class);
+        else
+            strcpy(device_class, "ANY");
+
+        if (rules[i].vendor_id != -1)
+            sprintf(vendor, "%04x", rules[i].vendor_id);
+        else
+            strcpy(vendor, " ANY");
+
+        if (rules[i].product_id != -1)
+            sprintf(product, "%04x", rules[i].product_id);
+        else
+            strcpy(product, " ANY");
+
+        if (rules[i].device_version_bcd != -1)
+            sprintf(version, "%2d.%02d",
+                    ((rules[i].device_version_bcd & 0xf000) >> 12) * 10 +
+                    ((rules[i].device_version_bcd & 0x0f00) >>  8),
+                    ((rules[i].device_version_bcd & 0x00f0) >>  4) * 10 +
+                    ((rules[i].device_version_bcd & 0x000f)));
+        else
+            strcpy(version, "  ANY");
+
+        fprintf(out, "Class %s ID %s:%s Version %s %s\n", device_class, vendor,
+                product, version, rules[i].allow ? "Allow":"Block");
+    }
+}
+#endif
diff --git a/drivers/usb/usbredir/usbredirfilter.h b/drivers/usb/usbredir/usbredirfilter.h
new file mode 100644
index 0000000..42773a8
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirfilter.h
@@ -0,0 +1,144 @@
+/* usbredirfilter.h usb redirection filter header
+
+   Copyright 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __USBREDIRFILTER_H
+#define __USBREDIRFILTER_H
+
+#if ! defined(__KERNEL__)
+#include <stdio.h>
+#include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct usbredirfilter_rule {
+    int device_class;       /* 0-255, -1 to match any class */
+    int vendor_id;          /* 0-65535, -1 to match any id */
+    int product_id;         /* 0-65535, -1 to match any id */
+    int device_version_bcd; /* 0-255, -1 to match any version */
+    int allow;              /* 0: deny redir for this device, non 0: allow */
+};
+
+/* Read a filter string and parse it into an array of usbredirfilter_rule-s.
+
+   Where each rule has the form of:
+   <class>,<vendor>,<product>,<version>,<allow>
+   Assuming "," as the specified token_sep character.
+
+   And the rules are themselves are separated by the rule_sep character, ie:
+   <rule1>|<rule2>|<rule3>
+
+   Assuming "|" as the rule_sep character. Note that with the seperator used
+   in this example the format matches the format as written by the RHEV-M USB
+   filter editor tool.
+
+   Note that the seperators must be single character strings!
+
+   On success the rules get returned in rules_ret and rules_count_ret, the
+   returned rules array should be freed with free() when the caller is done
+   with it.
+
+   Return value: 0 on success, -ENOMEM when allocating the rules array fails,
+       or -EINVAL when there is on parsing error.
+*/
+int usbredirfilter_string_to_rules(
+    const char *filter_str, const char *token_sep, const char *rule_sep,
+    struct usbredirfilter_rule **rules_ret, int *rules_count_ret);
+
+/* Convert a set of rules back to a string suitable for passing to
+   usbredirfilter_string_to_rules(); The returned string must be free()-ed
+   by the caller when it is done with it.
+
+   Return value: The string on sucess, or NULL if the rules fail verification,
+      or when allocating the string fails.
+*/
+char *usbredirfilter_rules_to_string(const struct usbredirfilter_rule *rules,
+    int rules_count, const char *token_sep, const char *rule_sep);
+
+/* Check if redirection of a device with the passed in device info is allowed
+   by the passed set of filter rules.
+
+   Since a device has class info at both the device level and the interface
+   level, this function does multiple passes.
+
+   First the rules are checked one by one against the given device info using
+   the device class info, if a matching rule is found, the result of the check
+   is that of that rule.
+
+   Then the same is done for each interface the device has, substituting the
+   device class info with the class info from the interfaces.
+
+   Note that under certain circumstances some passes are skipped:
+   - For devices with a device class of 0x00 or 0xef, the pass which checks the
+     device class is skipped.
+   - If the usbredirfilter_fl_dont_skip_non_boot_hid flag is not passed then
+     for devices with more then 1 interface and an interface with an interface
+     class of 0x03, an interface subclass of 0x00 and an interface protocol
+     of 0x00. the check is skipped for that interface. This allows to skip ie
+     checking the interface for volume buttons one some usbaudio class devices.
+
+   If the result of all (not skipped) passes is allow, then 0 will be returned,
+   which indicates that redirection should be allowed.
+
+   If the result of a matching rule is deny, then processing stops and
+   -EPERM will be returned.
+
+   If a given pass does not match any rules, then processing stops and
+   -ENOENT will be returned. This behavior can be changed with the
+   usbredirfilter_fl_default_allow flag, if this flag is set the result of a
+   pass with no matching rules will be allow.
+
+   Return value:
+    0        Redirection is allowed
+    -EINVAL  Invalid parameters
+    -EPERM   Redirection is blocked by the filter rules
+    -ENOENT  None of the rules matched the device (during one of the passes)
+*/
+enum {
+    usbredirfilter_fl_default_allow = 0x01,
+    usbredirfilter_fl_dont_skip_non_boot_hid = 0x02,
+};
+int usbredirfilter_check(
+    const struct usbredirfilter_rule *rules, int rules_count,
+    uint8_t device_class, uint8_t device_subclass, uint8_t device_protocol,
+    uint8_t *interface_class, uint8_t *interface_subclass,
+    uint8_t *interface_protocol, int interface_count,
+    uint16_t vendor_id, uint16_t product_id, uint16_t device_version_bcd,
+    int flags);
+
+/* Sanity check the passed in rules
+
+   Return value: 0 on success, -EINVAL when some values are out of bound. */
+int usbredirfilter_verify(
+    const struct usbredirfilter_rule *rules, int rules_count);
+
+#if ! defined(__KERNEL__)
+/* Print the passed in rules to FILE out in human readable format */
+void usbredirfilter_print(
+    const struct usbredirfilter_rule *rules, int rules_count, FILE *out);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/usb/usbredir/usbredirparser.c b/drivers/usb/usbredir/usbredirparser.c
new file mode 100644
index 0000000..154d95b
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirparser.c
@@ -0,0 +1,1795 @@
+/* usbredirparser.c usb redirection protocol parser
+
+   Copyright 2010-2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#if defined(__KERNEL__)
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#else
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#endif
+
+#include "usbredirproto-compat.h"
+#include "usbredirparser.h"
+#include "usbredirfilter.h"
+
+#if defined(__KERNEL__)
+#define CALLOC(a, b)    kcalloc((a), (b), GFP_KERNEL)
+#define MALLOC(a)       kmalloc((a), GFP_KERNEL)
+#define REALLOC(a, b)   krealloc((a), (b), GFP_KERNEL)
+#define FREE kfree
+#else
+#define CALLOC          calloc
+#define MALLOC          malloc
+#define REALLOC         realloc
+#define FREE            free
+#endif
+
+/* Put *some* upper limit on bulk transfer sizes */
+#define MAX_BULK_TRANSFER_SIZE (128u * 1024u * 1024u)
+
+/* Locking convenience macros */
+#define LOCK(parser) \
+    do { \
+        if ((parser)->lock) \
+            (parser)->callb.lock_func((parser)->lock); \
+    } while (0)
+
+#define UNLOCK(parser) \
+    do { \
+        if ((parser)->lock) \
+            (parser)->callb.unlock_func((parser)->lock); \
+    } while (0)
+
+struct usbredirparser_buf {
+    uint8_t *buf;
+    int pos;
+    int len;
+
+    struct usbredirparser_buf *next;
+};
+
+struct usbredirparser_priv {
+    struct usbredirparser callb;
+    int flags;
+
+    int have_peer_caps;
+    uint32_t our_caps[USB_REDIR_CAPS_SIZE];
+    uint32_t peer_caps[USB_REDIR_CAPS_SIZE];
+
+    void *lock;
+
+    union {
+        struct usb_redir_header header;
+        struct usb_redir_header_32bit_id header_32bit_id;
+    };
+    uint8_t type_header[288];
+    int header_read;
+    int type_header_len;
+    int type_header_read;
+    uint8_t *data;
+    int data_len;
+    int data_read;
+    int to_skip;
+    struct usbredirparser_buf *write_buf;
+    int write_buf_count;
+};
+
+static void
+#if defined __GNUC__
+__attribute__((format(printf, 3, 4)))
+#endif
+va_log(struct usbredirparser_priv *parser, int verbose, const char *fmt, ...)
+{
+    char buf[512];
+    va_list ap;
+    int n;
+
+    n = sprintf(buf, "usbredirparser: ");
+    va_start(ap, fmt);
+    vsnprintf(buf + n, sizeof(buf) - n, fmt, ap);
+    va_end(ap);
+    
+    parser->callb.log_func(parser->callb.priv, verbose, buf);
+}
+
+#define ERROR(...)   va_log(parser, usbredirparser_error, __VA_ARGS__)
+#define WARNING(...) va_log(parser, usbredirparser_warning, __VA_ARGS__)
+#define INFO(...)    va_log(parser, usbredirparser_info, __VA_ARGS__)
+#define DEBUGP(...)    va_log(parser, usbredirparser_debug, __VA_ARGS__)
+
+#if 0 /* Can be enabled and called from random place to test serialization */
+static void serialize_test(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usbredirparser_buf *wbuf, *next_wbuf;
+    uint8_t *data;
+    int len;
+
+    if (usbredirparser_serialize(parser_pub, &data, &len))
+        return;
+
+    wbuf = parser->write_buf;
+    while (wbuf) {
+        next_wbuf = wbuf->next;
+        FREE(wbuf->buf);
+        FREE(wbuf);
+        wbuf = next_wbuf;
+    }
+    parser->write_buf = NULL;
+    parser->write_buf_count = 0;
+
+    FREE(parser->data);
+    parser->data = NULL;
+
+    parser->type_header_len = parser->data_len = parser->have_peer_caps = 0;
+
+    usbredirparser_unserialize(parser_pub, data, len);
+    FREE(data);
+}
+#endif
+
+static void usbredirparser_queue(struct usbredirparser *parser, uint32_t type,
+    uint64_t id, void *type_header_in, uint8_t *data_in, int data_len);
+static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser,
+    uint32_t *caps, int cap);
+
+struct usbredirparser *usbredirparser_create(void)
+{
+    return CALLOC(1, sizeof(struct usbredirparser_priv));
+}
+
+static void usbredirparser_verify_caps(struct usbredirparser_priv *parser,
+    uint32_t *caps, const char *desc)
+{
+    if (usbredirparser_caps_get_cap(parser, caps,
+                                    usb_redir_cap_bulk_streams) &&
+        !usbredirparser_caps_get_cap(parser, caps,
+                                     usb_redir_cap_ep_info_max_packet_size)) {
+        ERROR("error %s caps contains cap_bulk_streams without"
+              "cap_ep_info_max_packet_size", desc);
+        caps[0] &= ~(1 << usb_redir_cap_bulk_streams);
+    }
+}
+
+void usbredirparser_init(struct usbredirparser *parser_pub,
+    const char *version, uint32_t *caps, int caps_len, int flags)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usb_redir_hello_header hello = { { 0 }, };
+
+    parser->flags = (flags & ~usbredirparser_fl_no_hello);
+    if (parser->callb.alloc_lock_func) {
+        parser->lock = parser->callb.alloc_lock_func();
+    }
+
+    snprintf(hello.version, sizeof(hello.version), "%s", version);
+    if (caps_len > USB_REDIR_CAPS_SIZE) {
+        caps_len = USB_REDIR_CAPS_SIZE;
+    }
+    memcpy(parser->our_caps, caps, caps_len * sizeof(uint32_t));
+    /* libusbredirparser handles sending the ack internally */
+    if (!(flags & usbredirparser_fl_usb_host))
+        usbredirparser_caps_set_cap(parser->our_caps,
+                                    usb_redir_cap_device_disconnect_ack);
+    usbredirparser_verify_caps(parser, parser->our_caps, "our");
+    if (!(flags & usbredirparser_fl_no_hello))
+        usbredirparser_queue(parser_pub, usb_redir_hello, 0, &hello,
+                             (uint8_t *)parser->our_caps,
+                             USB_REDIR_CAPS_SIZE * sizeof(uint32_t));
+}
+
+void usbredirparser_destroy(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usbredirparser_buf *wbuf, *next_wbuf;
+
+    wbuf = parser->write_buf;
+    while (wbuf) {
+        next_wbuf = wbuf->next;
+        FREE(wbuf->buf);
+        FREE(wbuf);
+        wbuf = next_wbuf;
+    }
+
+    if (parser->lock)
+        parser->callb.free_lock_func(parser->lock);
+
+    FREE(parser);
+}
+
+static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser,
+    uint32_t *caps, int cap)
+{
+    if (cap / 32 >= USB_REDIR_CAPS_SIZE) {
+        ERROR("error request for out of bounds cap: %d", cap);
+        return 0;
+    }
+    if (caps[cap / 32] & (1 << (cap % 32))) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+void usbredirparser_caps_set_cap(uint32_t *caps, int cap)
+{
+    caps[cap / 32] |= 1 << (cap % 32);
+}
+
+int usbredirparser_have_peer_caps(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+
+    return parser->have_peer_caps;
+}
+
+int usbredirparser_peer_has_cap(struct usbredirparser *parser_pub, int cap)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    return usbredirparser_caps_get_cap(parser, parser->peer_caps, cap);
+}
+
+int usbredirparser_have_cap(struct usbredirparser *parser_pub, int cap)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    return usbredirparser_caps_get_cap(parser, parser->our_caps, cap);
+}
+
+static int usbredirparser_using_32bits_ids(struct usbredirparser *parser_pub)
+{
+    return !usbredirparser_have_cap(parser_pub, usb_redir_cap_64bits_ids) ||
+           !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_64bits_ids);
+}
+
+static void usbredirparser_handle_hello(struct usbredirparser *parser_pub,
+    struct usb_redir_hello_header *hello, uint8_t *data, int data_len)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    uint32_t *peer_caps = (uint32_t *)data;
+    char buf[64];
+    int i;
+
+    if (parser->have_peer_caps) {
+        ERROR("Received second hello message, ignoring");
+        return;
+    }
+
+    /* In case hello->version is not 0 terminated (which would be a protocol
+       violation)_ */
+    snprintf(buf, sizeof(buf), "%s", hello->version);
+
+    memset(parser->peer_caps, 0, sizeof(parser->peer_caps));
+    if (data_len > sizeof(parser->peer_caps)) {
+        data_len = sizeof(parser->peer_caps);
+    }
+    for (i = 0; i < data_len / sizeof(uint32_t); i++) {
+        parser->peer_caps[i] = peer_caps[i];
+    }
+    usbredirparser_verify_caps(parser, parser->peer_caps, "peer");
+    parser->have_peer_caps = 1;
+    FREE(data);
+
+    INFO("Peer version: %s, using %d-bits ids", buf,
+         usbredirparser_using_32bits_ids(parser_pub) ? 32 : 64);
+
+    /* Added in 0.3.2, so no guarantee it is there */
+    if (parser->callb.hello_func)
+        parser->callb.hello_func(parser->callb.priv, hello);
+}
+
+static int usbredirparser_get_header_len(struct usbredirparser *parser_pub)
+{
+    if (usbredirparser_using_32bits_ids(parser_pub))
+        return sizeof(struct usb_redir_header_32bit_id);
+    else
+        return sizeof(struct usb_redir_header);
+}
+
+static int usbredirparser_get_type_header_len(
+    struct usbredirparser *parser_pub, int32_t type, int send)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    int command_for_host = 0;
+    
+    if (parser->flags & usbredirparser_fl_usb_host) {
+        command_for_host = 1;
+    }
+    if (send) {
+        command_for_host = !command_for_host;
+    }
+
+    switch (type) {
+    case usb_redir_hello:
+        return sizeof(struct usb_redir_hello_header);
+    case usb_redir_device_connect:
+        if (!command_for_host) {
+            if (usbredirparser_have_cap(parser_pub,
+                                    usb_redir_cap_connect_device_version) &&
+                usbredirparser_peer_has_cap(parser_pub,
+                                    usb_redir_cap_connect_device_version)) {
+                return sizeof(struct usb_redir_device_connect_header);
+            } else {
+                return sizeof(struct usb_redir_device_connect_header_no_device_version);
+            }
+        } else {
+            return -1;
+        }
+    case usb_redir_device_disconnect:
+        if (!command_for_host) {
+            return 0;
+        } else {
+            return -1;
+        }
+    case usb_redir_reset:
+        if (command_for_host) {
+            return 0; /* No packet type specific header */
+        } else {
+            return -1;
+        }
+    case usb_redir_interface_info:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_interface_info_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_ep_info:
+        if (!command_for_host) {
+            if (usbredirparser_have_cap(parser_pub,
+                                    usb_redir_cap_bulk_streams) &&
+                usbredirparser_peer_has_cap(parser_pub,
+                                    usb_redir_cap_bulk_streams)) {
+                return sizeof(struct usb_redir_ep_info_header);
+            } else if (usbredirparser_have_cap(parser_pub,
+                                    usb_redir_cap_ep_info_max_packet_size) &&
+                       usbredirparser_peer_has_cap(parser_pub,
+                                    usb_redir_cap_ep_info_max_packet_size)) {
+                return sizeof(struct usb_redir_ep_info_header_no_max_streams);
+            } else {
+                return sizeof(struct usb_redir_ep_info_header_no_max_pktsz);
+            }
+        } else {
+            return -1;
+        }
+    case usb_redir_set_configuration:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_set_configuration_header);
+        } else {
+            return -1; /* Should never be send to a guest */
+        }
+    case usb_redir_get_configuration:
+        if (command_for_host) {
+            return 0; /* No packet type specific header */
+        } else {
+            return -1;
+        }
+    case usb_redir_configuration_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_configuration_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_set_alt_setting:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_set_alt_setting_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_get_alt_setting:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_get_alt_setting_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_alt_setting_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_alt_setting_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_start_iso_stream:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_start_iso_stream_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_stop_iso_stream:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_stop_iso_stream_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_iso_stream_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_iso_stream_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_start_interrupt_receiving:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_start_interrupt_receiving_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_stop_interrupt_receiving:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_stop_interrupt_receiving_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_interrupt_receiving_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_interrupt_receiving_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_alloc_bulk_streams:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_alloc_bulk_streams_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_free_bulk_streams:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_free_bulk_streams_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_bulk_streams_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_bulk_streams_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_cancel_data_packet:
+        if (command_for_host) {
+            return 0; /* No packet type specific header */
+        } else {
+            return -1;
+        }
+    case usb_redir_filter_reject:
+        if (command_for_host) {
+            return 0;
+        } else {
+            return -1;
+        }
+    case usb_redir_filter_filter:
+        return 0;
+    case usb_redir_device_disconnect_ack:
+        if (command_for_host) {
+            return 0;
+        } else {
+            return -1;
+        }
+    case usb_redir_start_bulk_receiving:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_start_bulk_receiving_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_stop_bulk_receiving:
+        if (command_for_host) {
+            return sizeof(struct usb_redir_stop_bulk_receiving_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_bulk_receiving_status:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_bulk_receiving_status_header);
+        } else {
+            return -1;
+        }
+    case usb_redir_control_packet:
+        return sizeof(struct usb_redir_control_packet_header);
+    case usb_redir_bulk_packet:
+        if (usbredirparser_have_cap(parser_pub,
+                                usb_redir_cap_32bits_bulk_length) &&
+            usbredirparser_peer_has_cap(parser_pub,
+                                usb_redir_cap_32bits_bulk_length)) {
+            return sizeof(struct usb_redir_bulk_packet_header);
+        } else {
+            return sizeof(struct usb_redir_bulk_packet_header_16bit_length);
+        }
+    case usb_redir_iso_packet:
+        return sizeof(struct usb_redir_iso_packet_header);
+    case usb_redir_interrupt_packet:
+        return sizeof(struct usb_redir_interrupt_packet_header);
+    case usb_redir_buffered_bulk_packet:
+        if (!command_for_host) {
+            return sizeof(struct usb_redir_buffered_bulk_packet_header);
+        } else {
+            return -1;
+        }
+    default:
+        return -1;
+    }
+}
+
+/* Note this function only checks if extra data is allowed for the
+   packet type being read at all, a check if it is actually allowed
+   given the direction of the packet + ep is done in _erify_type_header */
+static int usbredirparser_expect_extra_data(struct usbredirparser_priv *parser)
+{
+    switch (parser->header.type) {
+    case usb_redir_hello: /* For the variable length capabilities array */
+    case usb_redir_filter_filter:
+    case usb_redir_control_packet:
+    case usb_redir_bulk_packet:
+    case usb_redir_iso_packet:
+    case usb_redir_interrupt_packet:
+    case usb_redir_buffered_bulk_packet:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+static int usbredirparser_verify_bulk_recv_cap(
+    struct usbredirparser *parser_pub, int send)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+
+    if ((send && !usbredirparser_peer_has_cap(parser_pub,
+                                              usb_redir_cap_bulk_receiving)) ||
+        (!send && !usbredirparser_have_cap(parser_pub,
+                                           usb_redir_cap_bulk_receiving))) {
+        ERROR("error bulk_receiving without cap_bulk_receiving");
+        return 0;
+    }
+    return 1; /* Verify ok */
+}
+
+static int usbredirparser_verify_type_header(
+    struct usbredirparser *parser_pub,
+    int32_t type, void *header, uint8_t *data, int data_len, int send)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    int command_for_host = 0, expect_extra_data = 0;
+    int length = 0, ep = -1;
+
+    if (parser->flags & usbredirparser_fl_usb_host) {
+        command_for_host = 1;
+    }
+    if (send) {
+        command_for_host = !command_for_host;
+    }
+
+    switch (type) {
+    case usb_redir_interface_info: {
+        struct usb_redir_interface_info_header *intf_info = header;
+
+        if (intf_info->interface_count > 32) {
+            ERROR("error interface_count > 32");
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_start_interrupt_receiving: {
+        struct usb_redir_start_interrupt_receiving_header *start_int = header;
+
+        if (!(start_int->endpoint & 0x80)) {
+            ERROR("start int receiving on non input ep %02x",
+                  start_int->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_stop_interrupt_receiving: {
+        struct usb_redir_stop_interrupt_receiving_header *stop_int = header;
+
+        if (!(stop_int->endpoint & 0x80)) {
+            ERROR("stop int receiving on non input ep %02x",
+                  stop_int->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_interrupt_receiving_status: {
+        struct usb_redir_interrupt_receiving_status_header *int_status = header;
+
+        if (!(int_status->endpoint & 0x80)) {
+            ERROR("int receiving status for non input ep %02x",
+                  int_status->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_filter_reject:
+        if ((send && !usbredirparser_peer_has_cap(parser_pub,
+                                             usb_redir_cap_filter)) ||
+            (!send && !usbredirparser_have_cap(parser_pub,
+                                             usb_redir_cap_filter))) {
+            ERROR("error filter_reject without cap_filter");
+            return 0;
+        }
+        break;
+    case usb_redir_filter_filter:
+        if ((send && !usbredirparser_peer_has_cap(parser_pub,
+                                             usb_redir_cap_filter)) ||
+            (!send && !usbredirparser_have_cap(parser_pub,
+                                             usb_redir_cap_filter))) {
+            ERROR("error filter_filter without cap_filter");
+            return 0;
+        }
+        if (data_len < 1) {
+            ERROR("error filter_filter without data");
+            return 0;
+        }
+        if (data[data_len - 1] != 0) {
+            ERROR("error non 0 terminated filter_filter data");
+            return 0;
+        }
+        break;
+    case usb_redir_device_disconnect_ack:
+        if ((send && !usbredirparser_peer_has_cap(parser_pub,
+                                     usb_redir_cap_device_disconnect_ack)) ||
+            (!send && !usbredirparser_have_cap(parser_pub,
+                                     usb_redir_cap_device_disconnect_ack))) {
+            ERROR("error device_disconnect_ack without cap_device_disconnect_ack");
+            return 0;
+        }
+        break;
+    case usb_redir_start_bulk_receiving: {
+        struct usb_redir_start_bulk_receiving_header *start_bulk = header;
+
+        if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) {
+            return 0;
+        }
+        if (start_bulk->bytes_per_transfer > MAX_BULK_TRANSFER_SIZE) {
+            ERROR("start bulk receiving length exceeds limits %u > %u",
+                  start_bulk->bytes_per_transfer, MAX_BULK_TRANSFER_SIZE);
+            return 0;
+        }
+        if (!(start_bulk->endpoint & 0x80)) {
+            ERROR("start bulk receiving on non input ep %02x",
+                  start_bulk->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_stop_bulk_receiving: {
+        struct usb_redir_stop_bulk_receiving_header *stop_bulk = header;
+
+        if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) {
+            return 0;
+        }
+        if (!(stop_bulk->endpoint & 0x80)) {
+            ERROR("stop bulk receiving on non input ep %02x",
+                  stop_bulk->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_bulk_receiving_status: {
+        struct usb_redir_bulk_receiving_status_header *bulk_status = header;
+
+        if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) {
+            return 0;
+        }
+        if (!(bulk_status->endpoint & 0x80)) {
+            ERROR("bulk receiving status for non input ep %02x",
+                  bulk_status->endpoint);
+            return 0;
+        }
+        break;
+    }
+    case usb_redir_control_packet:
+        length = ((struct usb_redir_control_packet_header *)header)->length;
+        ep = ((struct usb_redir_control_packet_header *)header)->endpoint;
+        break;
+    case usb_redir_bulk_packet: {
+        struct usb_redir_bulk_packet_header *bulk_packet = header;
+        if (usbredirparser_have_cap(parser_pub,
+                                usb_redir_cap_32bits_bulk_length) &&
+            usbredirparser_peer_has_cap(parser_pub,
+                                usb_redir_cap_32bits_bulk_length)) {
+            length = (bulk_packet->length_high << 16) | bulk_packet->length;
+        } else {
+            length = bulk_packet->length;
+            if (!send)
+                bulk_packet->length_high = 0;
+        }
+        if ((uint32_t)length > MAX_BULK_TRANSFER_SIZE) {
+            ERROR("bulk transfer length exceeds limits %u > %u",
+                  (uint32_t)length, MAX_BULK_TRANSFER_SIZE);
+            return 0;
+        }
+        ep = bulk_packet->endpoint;
+        break;
+    }
+    case usb_redir_iso_packet:
+        length = ((struct usb_redir_iso_packet_header *)header)->length;
+        ep = ((struct usb_redir_iso_packet_header *)header)->endpoint;
+        break;
+    case usb_redir_interrupt_packet:
+        length = ((struct usb_redir_interrupt_packet_header *)header)->length;
+        ep = ((struct usb_redir_interrupt_packet_header *)header)->endpoint;
+        break;
+    case usb_redir_buffered_bulk_packet: {
+        struct usb_redir_buffered_bulk_packet_header *buf_bulk_pkt = header;
+        length = buf_bulk_pkt->length;
+        if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) {
+            return 0;
+        }
+        if ((uint32_t)length > MAX_BULK_TRANSFER_SIZE) {
+            ERROR("buffered bulk transfer length exceeds limits %u > %u",
+                  (uint32_t)length, MAX_BULK_TRANSFER_SIZE);
+            return 0;
+        }
+        ep = buf_bulk_pkt->endpoint;
+        break;
+    }
+    }
+
+    if (ep != -1) {
+        if (((ep & 0x80) && !command_for_host) ||
+            (!(ep & 0x80) && command_for_host)) {
+            expect_extra_data = 1;
+        }
+        if (expect_extra_data) {
+            if (data_len != length) {
+                ERROR("error data len %d != header len %d ep %02X",
+                      data_len, length, ep);
+                return 0;
+            }
+        } else {
+            if (data || data_len) {
+                ERROR("error unexpected extra data ep %02X", ep);
+                return 0;
+            }
+            switch (type) {
+            case usb_redir_iso_packet:
+                ERROR("error iso packet send in wrong direction");
+                return 0;
+            case usb_redir_interrupt_packet:
+                if (command_for_host) {
+                    ERROR("error interrupt packet send in wrong direction");
+                    return 0;
+                }
+                break;
+            case usb_redir_buffered_bulk_packet:
+                ERROR("error buffered bulk packet send in wrong direction");
+                return 0;
+            }
+        }
+    }
+
+    return 1; /* Verify ok */
+}
+
+static void usbredirparser_call_type_func(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    uint64_t id;
+
+    if (usbredirparser_using_32bits_ids(parser_pub))
+        id = parser->header_32bit_id.id;
+    else
+        id = parser->header.id;
+
+    switch (parser->header.type) {
+    case usb_redir_hello:
+        usbredirparser_handle_hello(parser_pub,
+            (struct usb_redir_hello_header *)parser->type_header,
+            parser->data, parser->data_len);
+        break;
+    case usb_redir_device_connect:
+        parser->callb.device_connect_func(parser->callb.priv,
+            (struct usb_redir_device_connect_header *)parser->type_header);
+        break;
+    case usb_redir_device_disconnect:
+        parser->callb.device_disconnect_func(parser->callb.priv);
+        if (usbredirparser_peer_has_cap(parser_pub,
+                                        usb_redir_cap_device_disconnect_ack))
+            usbredirparser_queue(parser_pub, usb_redir_device_disconnect_ack,
+                                 0, NULL, NULL, 0);
+        break;
+    case usb_redir_reset:
+        parser->callb.reset_func(parser->callb.priv);
+        break;
+    case usb_redir_interface_info:
+        parser->callb.interface_info_func(parser->callb.priv,
+            (struct usb_redir_interface_info_header *)parser->type_header);
+        break;
+    case usb_redir_ep_info:
+        parser->callb.ep_info_func(parser->callb.priv,
+            (struct usb_redir_ep_info_header *)parser->type_header);
+        break;
+    case usb_redir_set_configuration:
+        parser->callb.set_configuration_func(parser->callb.priv, id,
+            (struct usb_redir_set_configuration_header *)parser->type_header);
+        break;
+    case usb_redir_get_configuration:
+        parser->callb.get_configuration_func(parser->callb.priv, id);
+        break;
+    case usb_redir_configuration_status:
+        parser->callb.configuration_status_func(parser->callb.priv, id,
+          (struct usb_redir_configuration_status_header *)parser->type_header);
+        break;
+    case usb_redir_set_alt_setting:
+        parser->callb.set_alt_setting_func(parser->callb.priv, id,
+            (struct usb_redir_set_alt_setting_header *)parser->type_header);
+        break;
+    case usb_redir_get_alt_setting:
+        parser->callb.get_alt_setting_func(parser->callb.priv, id,
+            (struct usb_redir_get_alt_setting_header *)parser->type_header);
+        break;
+    case usb_redir_alt_setting_status:
+        parser->callb.alt_setting_status_func(parser->callb.priv, id,
+            (struct usb_redir_alt_setting_status_header *)parser->type_header);
+        break;
+    case usb_redir_start_iso_stream:
+        parser->callb.start_iso_stream_func(parser->callb.priv, id,
+            (struct usb_redir_start_iso_stream_header *)parser->type_header);
+        break;
+    case usb_redir_stop_iso_stream:
+        parser->callb.stop_iso_stream_func(parser->callb.priv, id,
+            (struct usb_redir_stop_iso_stream_header *)parser->type_header);
+        break;
+    case usb_redir_iso_stream_status:
+        parser->callb.iso_stream_status_func(parser->callb.priv, id,
+            (struct usb_redir_iso_stream_status_header *)parser->type_header);
+        break;
+    case usb_redir_start_interrupt_receiving:
+        parser->callb.start_interrupt_receiving_func(parser->callb.priv, id,
+            (struct usb_redir_start_interrupt_receiving_header *)
+            parser->type_header);
+        break;
+    case usb_redir_stop_interrupt_receiving:
+        parser->callb.stop_interrupt_receiving_func(parser->callb.priv, id,
+            (struct usb_redir_stop_interrupt_receiving_header *)
+            parser->type_header);
+        break;
+    case usb_redir_interrupt_receiving_status:
+        parser->callb.interrupt_receiving_status_func(parser->callb.priv, id,
+            (struct usb_redir_interrupt_receiving_status_header *)
+            parser->type_header);
+        break;
+    case usb_redir_alloc_bulk_streams:
+        parser->callb.alloc_bulk_streams_func(parser->callb.priv, id,
+            (struct usb_redir_alloc_bulk_streams_header *)parser->type_header);
+        break;
+    case usb_redir_free_bulk_streams:
+        parser->callb.free_bulk_streams_func(parser->callb.priv, id,
+            (struct usb_redir_free_bulk_streams_header *)parser->type_header);
+        break;
+    case usb_redir_bulk_streams_status:
+        parser->callb.bulk_streams_status_func(parser->callb.priv, id,
+          (struct usb_redir_bulk_streams_status_header *)parser->type_header);
+        break;
+    case usb_redir_cancel_data_packet:
+        parser->callb.cancel_data_packet_func(parser->callb.priv, id);
+        break;
+    case usb_redir_filter_reject:
+        parser->callb.filter_reject_func(parser->callb.priv);
+        break;
+    case usb_redir_filter_filter: {
+        struct usbredirfilter_rule *rules;
+        int r, count;
+
+        r = usbredirfilter_string_to_rules((char *)parser->data, ",", "|",
+                                           &rules, &count);
+        if (r) {
+            ERROR("error parsing filter (%d), ignoring filter message", r);
+            break;
+        }
+        parser->callb.filter_filter_func(parser->callb.priv, rules, count);
+        break;
+    }
+    case usb_redir_device_disconnect_ack:
+        parser->callb.device_disconnect_ack_func(parser->callb.priv);
+        break;
+    case usb_redir_start_bulk_receiving:
+        parser->callb.start_bulk_receiving_func(parser->callb.priv, id,
+            (struct usb_redir_start_bulk_receiving_header *)
+            parser->type_header);
+        break;
+    case usb_redir_stop_bulk_receiving:
+        parser->callb.stop_bulk_receiving_func(parser->callb.priv, id,
+            (struct usb_redir_stop_bulk_receiving_header *)
+            parser->type_header);
+        break;
+    case usb_redir_bulk_receiving_status:
+        parser->callb.bulk_receiving_status_func(parser->callb.priv, id,
+            (struct usb_redir_bulk_receiving_status_header *)
+            parser->type_header);
+        break;
+    case usb_redir_control_packet:
+        parser->callb.control_packet_func(parser->callb.priv, id,
+            (struct usb_redir_control_packet_header *)parser->type_header,
+            parser->data, parser->data_len);
+        break;
+    case usb_redir_bulk_packet:
+        parser->callb.bulk_packet_func(parser->callb.priv, id,
+            (struct usb_redir_bulk_packet_header *)parser->type_header,
+            parser->data, parser->data_len);
+        break;
+    case usb_redir_iso_packet:
+        parser->callb.iso_packet_func(parser->callb.priv, id,
+            (struct usb_redir_iso_packet_header *)parser->type_header,
+            parser->data, parser->data_len);
+        break;
+    case usb_redir_interrupt_packet:
+        parser->callb.interrupt_packet_func(parser->callb.priv, id,
+            (struct usb_redir_interrupt_packet_header *)parser->type_header,
+            parser->data, parser->data_len);
+        break;
+    case usb_redir_buffered_bulk_packet:
+        parser->callb.buffered_bulk_packet_func(parser->callb.priv, id,
+          (struct usb_redir_buffered_bulk_packet_header *)parser->type_header,
+          parser->data, parser->data_len);
+        break;
+    }
+}
+
+int usbredirparser_do_read(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    int r, header_len, type_header_len, data_len;
+    uint8_t *dest;
+
+    header_len = usbredirparser_get_header_len(parser_pub);
+
+    /* Skip forward to next packet (only used in error conditions) */
+    while (parser->to_skip > 0) {
+        uint8_t buf[1024];
+        r = (parser->to_skip > sizeof(buf)) ? sizeof(buf) : parser->to_skip;
+        r = parser->callb.read_func(parser->callb.priv, buf, r);
+        if (r <= 0)
+            return r;
+        parser->to_skip -= r;
+    }
+
+    /* Consume data until read would block or returns an error */
+    while (1) {
+        if (parser->header_read < header_len) {
+            r = header_len - parser->header_read;
+            dest = (uint8_t *)&parser->header + parser->header_read;
+        } else if (parser->type_header_read < parser->type_header_len) {
+            r = parser->type_header_len - parser->type_header_read;
+            dest = parser->type_header + parser->type_header_read;
+        } else {
+            r = parser->data_len - parser->data_read;
+            dest = parser->data + parser->data_read;
+        }
+
+        if (r > 0) {
+            r = parser->callb.read_func(parser->callb.priv, dest, r);
+            if (r <= 0) {
+                return r;
+            }
+        }
+
+        if (parser->header_read < header_len) {
+            parser->header_read += r;
+            if (parser->header_read == header_len) {
+                type_header_len =
+                    usbredirparser_get_type_header_len(parser_pub,
+                                                       parser->header.type, 0);
+                if (type_header_len < 0) {
+                    ERROR("error invalid usb-redir packet type: %u",
+                          parser->header.type);
+                    parser->to_skip = parser->header.length;
+                    parser->header_read = 0;
+                    return -2;
+                }
+                /* This should never happen */
+                if (type_header_len > sizeof(parser->type_header)) {
+                    ERROR("error type specific header buffer too small, please report!!");
+                    parser->to_skip = parser->header.length;
+                    parser->header_read = 0;
+                    return -2;
+                }
+                if ((int)parser->header.length < type_header_len ||
+                    ((int)parser->header.length > type_header_len &&
+                     !usbredirparser_expect_extra_data(parser))) {
+                    ERROR("error invalid packet type %u length: %u",
+                          parser->header.type, parser->header.length);
+                    parser->to_skip = parser->header.length;
+                    parser->header_read = 0;
+                    return -2;
+                }
+                data_len = parser->header.length - type_header_len;
+                if (data_len) {
+                    parser->data = MALLOC(data_len);
+                    if (!parser->data) {
+                        ERROR("Out of memory allocating data buffer");
+                        parser->to_skip = parser->header.length;
+                        parser->header_read = 0;
+                        return -2;
+                    }
+                }
+                parser->type_header_len = type_header_len;
+                parser->data_len = data_len;
+            }
+        } else if (parser->type_header_read < parser->type_header_len) {
+            parser->type_header_read += r;
+        } else {
+            parser->data_read += r;
+            if (parser->data_read == parser->data_len) {
+                r = usbredirparser_verify_type_header(parser_pub,
+                         parser->header.type, parser->type_header,
+                         parser->data, parser->data_len, 0);
+                if (r)
+                    usbredirparser_call_type_func(parser_pub);
+                parser->header_read = 0;
+                parser->type_header_len  = 0;
+                parser->type_header_read = 0;
+                parser->data_len  = 0;
+                parser->data_read = 0;
+                parser->data = NULL;
+                if (!r)
+                    return -2;
+                /* header len may change if this was an hello packet */
+                header_len = usbredirparser_get_header_len(parser_pub);
+            }
+        }
+    }
+}
+
+int usbredirparser_has_data_to_write(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    return parser->write_buf_count;
+}
+
+int usbredirparser_do_write(struct usbredirparser *parser_pub)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usbredirparser_buf* wbuf;
+    int w, ret = 0;
+
+    LOCK(parser);
+    for (;;) {    
+        wbuf = parser->write_buf;
+        if (!wbuf)
+            break;
+
+        w = wbuf->len - wbuf->pos;
+        w = parser->callb.write_func(parser->callb.priv,
+                                     wbuf->buf + wbuf->pos, w);
+        if (w <= 0) {
+            ret = w;
+            break;
+        }
+
+        /* See usbredirparser_write documentation */
+        if ((parser->flags & usbredirparser_fl_write_cb_owns_buffer) &&
+                w != wbuf->len) {
+#if ! defined(__KERNEL__)
+            abort();
+#else
+            ret = -1;
+            break;
+#endif
+        }
+
+        wbuf->pos += w;
+        if (wbuf->pos == wbuf->len) {
+            parser->write_buf = wbuf->next;
+            if (!(parser->flags & usbredirparser_fl_write_cb_owns_buffer))
+                FREE(wbuf->buf);
+            FREE(wbuf);
+            parser->write_buf_count--;
+        }
+    }
+    UNLOCK(parser);
+    return ret;
+}
+
+void usbredirparser_free_write_buffer(struct usbredirparser *parser,
+    uint8_t *data)
+{
+    FREE(data);
+}
+
+void usbredirparser_free_packet_data(struct usbredirparser *parser,
+    uint8_t *data)
+{
+    FREE(data);
+}
+
+static void usbredirparser_queue(struct usbredirparser *parser_pub,
+    uint32_t type, uint64_t id, void *type_header_in,
+    uint8_t *data_in, int data_len)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    uint8_t *buf, *type_header_out, *data_out;
+    struct usb_redir_header *header;
+    struct usbredirparser_buf *wbuf, *new_wbuf;
+    int header_len, type_header_len;
+
+    header_len = usbredirparser_get_header_len(parser_pub);
+    type_header_len = usbredirparser_get_type_header_len(parser_pub, type, 1);
+    if (type_header_len < 0) { /* This should never happen */
+        ERROR("error packet type unknown with internal call, please report!!");
+        return;
+    }
+
+    if (!usbredirparser_verify_type_header(parser_pub, type, type_header_in,
+                                           data_in, data_len, 1)) {
+        ERROR("error usbredirparser_send_* call invalid params, please report!!");
+        return;
+    }
+
+    new_wbuf = CALLOC(1, sizeof(*new_wbuf));
+    buf = MALLOC(header_len + type_header_len + data_len);
+    if (!new_wbuf || !buf) {
+        ERROR("Out of memory allocating buffer to send packet, dropping!");
+        FREE(new_wbuf); FREE(buf);
+        return;
+    }
+
+    new_wbuf->buf = buf;
+    new_wbuf->len = header_len + type_header_len + data_len;
+
+    header = (struct usb_redir_header *)buf;
+    type_header_out = buf + header_len;
+    data_out = type_header_out + type_header_len;
+
+    header->type   = type;
+    header->length = type_header_len + data_len;
+    if (usbredirparser_using_32bits_ids(parser_pub))
+        ((struct usb_redir_header_32bit_id *)header)->id = id;
+    else
+        header->id = id;
+    memcpy(type_header_out, type_header_in, type_header_len);
+    memcpy(data_out, data_in, data_len);
+
+    LOCK(parser);
+    if (!parser->write_buf) {
+        parser->write_buf = new_wbuf;
+    } else {
+        /* limiting the write_buf's stack depth is our users responsibility */
+        wbuf = parser->write_buf;
+        while (wbuf->next)
+            wbuf = wbuf->next;
+
+        wbuf->next = new_wbuf;
+    }
+    parser->write_buf_count++;
+    UNLOCK(parser);
+}
+
+void usbredirparser_send_device_connect(struct usbredirparser *parser,
+    struct usb_redir_device_connect_header *device_connect)
+{
+    usbredirparser_queue(parser, usb_redir_device_connect, 0, device_connect,
+                         NULL, 0);
+}
+
+void usbredirparser_send_device_disconnect(struct usbredirparser *parser)
+{
+    usbredirparser_queue(parser, usb_redir_device_disconnect, 0, NULL,
+                         NULL, 0);
+}
+
+void usbredirparser_send_reset(struct usbredirparser *parser)
+{
+    usbredirparser_queue(parser, usb_redir_reset, 0, NULL, NULL, 0);
+}
+
+void usbredirparser_send_interface_info(struct usbredirparser *parser,
+    struct usb_redir_interface_info_header *interface_info)
+{
+    usbredirparser_queue(parser, usb_redir_interface_info, 0, interface_info,
+                         NULL, 0);
+}
+
+void usbredirparser_send_ep_info(struct usbredirparser *parser,
+    struct usb_redir_ep_info_header *ep_info)
+{
+    usbredirparser_queue(parser, usb_redir_ep_info, 0, ep_info, NULL, 0);
+}
+
+void usbredirparser_send_set_configuration(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_set_configuration_header *set_configuration)
+{
+    usbredirparser_queue(parser, usb_redir_set_configuration, id,
+                         set_configuration, NULL, 0);
+}
+
+void usbredirparser_send_get_configuration(struct usbredirparser *parser,
+    uint64_t id)
+{
+    usbredirparser_queue(parser, usb_redir_get_configuration, id,
+                         NULL, NULL, 0);
+}
+
+void usbredirparser_send_configuration_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_configuration_status_header *configuration_status)
+{
+    usbredirparser_queue(parser, usb_redir_configuration_status, id,
+                         configuration_status, NULL, 0);
+}
+
+void usbredirparser_send_set_alt_setting(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_set_alt_setting_header *set_alt_setting)
+{
+    usbredirparser_queue(parser, usb_redir_set_alt_setting, id,
+                         set_alt_setting, NULL, 0);
+}
+
+void usbredirparser_send_get_alt_setting(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_get_alt_setting_header *get_alt_setting)
+{
+    usbredirparser_queue(parser, usb_redir_get_alt_setting, id,
+                         get_alt_setting, NULL, 0);
+}
+
+void usbredirparser_send_alt_setting_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status)
+{
+    usbredirparser_queue(parser, usb_redir_alt_setting_status, id,
+                         alt_setting_status, NULL, 0);
+}
+
+void usbredirparser_send_start_iso_stream(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_iso_stream_header *start_iso_stream)
+{
+    usbredirparser_queue(parser, usb_redir_start_iso_stream, id,
+                         start_iso_stream, NULL, 0);
+}
+
+void usbredirparser_send_stop_iso_stream(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_iso_stream_header *stop_iso_stream)
+{
+    usbredirparser_queue(parser, usb_redir_stop_iso_stream, id,
+                         stop_iso_stream, NULL, 0);
+}
+
+void usbredirparser_send_iso_stream_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status)
+{
+    usbredirparser_queue(parser, usb_redir_iso_stream_status, id,
+                         iso_stream_status, NULL, 0);
+}
+
+void usbredirparser_send_start_interrupt_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving)
+{
+    usbredirparser_queue(parser, usb_redir_start_interrupt_receiving, id,
+                         start_interrupt_receiving, NULL, 0);
+}
+
+void usbredirparser_send_stop_interrupt_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving)
+{
+    usbredirparser_queue(parser, usb_redir_stop_interrupt_receiving, id,
+                         stop_interrupt_receiving, NULL, 0);
+}
+
+void usbredirparser_send_interrupt_receiving_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status)
+{
+    usbredirparser_queue(parser, usb_redir_interrupt_receiving_status, id,
+                         interrupt_receiving_status, NULL, 0);
+}
+
+void usbredirparser_send_alloc_bulk_streams(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams)
+{
+    usbredirparser_queue(parser, usb_redir_alloc_bulk_streams, id,
+                         alloc_bulk_streams, NULL, 0);
+}
+
+void usbredirparser_send_free_bulk_streams(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_free_bulk_streams_header *free_bulk_streams)
+{
+    usbredirparser_queue(parser, usb_redir_free_bulk_streams, id,
+                         free_bulk_streams, NULL, 0);
+}
+
+void usbredirparser_send_bulk_streams_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+{
+    usbredirparser_queue(parser, usb_redir_bulk_streams_status, id,
+                         bulk_streams_status, NULL, 0);
+}
+
+void usbredirparser_send_cancel_data_packet(struct usbredirparser *parser,
+    uint64_t id)
+{
+    usbredirparser_queue(parser, usb_redir_cancel_data_packet, id,
+                         NULL, NULL, 0);
+}
+
+void usbredirparser_send_filter_reject(struct usbredirparser *parser)
+{
+    if (!usbredirparser_peer_has_cap(parser, usb_redir_cap_filter))
+        return;
+
+    usbredirparser_queue(parser, usb_redir_filter_reject, 0, NULL, NULL, 0);
+}
+
+void usbredirparser_send_filter_filter(struct usbredirparser *parser_pub,
+    const struct usbredirfilter_rule *rules, int rules_count)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    char *str;
+
+    if (!usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_filter))
+        return;
+
+    str = usbredirfilter_rules_to_string(rules, rules_count, ",", "|");
+    if (!str) {
+        ERROR("error creating filter string, not sending filter");
+        return;
+    }
+    usbredirparser_queue(parser_pub, usb_redir_filter_filter, 0, NULL,
+                         (uint8_t *)str, strlen(str) + 1);
+    FREE(str);
+}
+
+void usbredirparser_send_start_bulk_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_bulk_receiving_header *start_bulk_receiving)
+{
+    usbredirparser_queue(parser, usb_redir_start_bulk_receiving, id,
+                         start_bulk_receiving, NULL, 0);
+}
+
+void usbredirparser_send_stop_bulk_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving)
+{
+    usbredirparser_queue(parser, usb_redir_stop_bulk_receiving, id,
+                         stop_bulk_receiving, NULL, 0);
+}
+
+void usbredirparser_send_bulk_receiving_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_receiving_status_header *bulk_receiving_status)
+{
+    usbredirparser_queue(parser, usb_redir_bulk_receiving_status, id,
+                         bulk_receiving_status, NULL, 0);
+}
+
+/* Data packets: */
+void usbredirparser_send_control_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_control_packet_header *control_header,
+    uint8_t *data, int data_len)
+{
+    usbredirparser_queue(parser, usb_redir_control_packet, id, control_header,
+                         data, data_len);
+}
+
+void usbredirparser_send_bulk_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_packet_header *bulk_header,
+    uint8_t *data, int data_len)
+{
+    usbredirparser_queue(parser, usb_redir_bulk_packet, id, bulk_header,
+                         data, data_len);
+}
+
+void usbredirparser_send_iso_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_iso_packet_header *iso_header,
+    uint8_t *data, int data_len)
+{
+    usbredirparser_queue(parser, usb_redir_iso_packet, id, iso_header,
+                         data, data_len);
+}
+
+void usbredirparser_send_interrupt_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_header,
+    uint8_t *data, int data_len)
+{
+    usbredirparser_queue(parser, usb_redir_interrupt_packet, id,
+                         interrupt_header, data, data_len);
+}
+
+void usbredirparser_send_buffered_bulk_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header,
+    uint8_t *data, int data_len)
+{
+    usbredirparser_queue(parser, usb_redir_buffered_bulk_packet, id,
+                         buffered_bulk_header, data, data_len);
+}
+
+/****** Serialization support ******/
+
+#define USBREDIRPARSER_SERIALIZE_MAGIC        0x55525031
+#define USBREDIRPARSER_SERIALIZE_BUF_SIZE     65536
+
+/* Serialization format, send and receiving endian are expected to be the same!
+    uint32 MAGIC: 0x55525031 ascii: URP1 (UsbRedirParser version 1)
+    uint32 len: length of the entire serialized state, including MAGIC
+    uint32 our_caps_len
+    uint32 our_caps[our_caps_len]
+    uint32 peer_caps_len
+    uint32 peer_caps[peer_caps_len]
+    uint32 to_skip
+    uint32 header_read
+    uint8  header[header_read]
+    uint32 type_header_read
+    uint8  type_header[type_header_read]
+    uint32 data_read
+    uint8  data[data_read]
+    uint32 write_buf_count: followed by write_buf_count times:
+        uint32 write_buf_len
+        uint8  write_buf_data[write_buf_len]
+*/
+
+static int serialize_alloc(struct usbredirparser_priv *parser,
+                           uint8_t **state, uint8_t **pos,
+                           uint32_t *remain, uint32_t needed)
+{
+    uint8_t *old_state = *state;
+    uint32_t used, size;
+
+    if (*remain >= needed)
+        return 0;
+
+    used = *pos - *state;
+    size = (used + needed + USBREDIRPARSER_SERIALIZE_BUF_SIZE - 1) &
+           ~(USBREDIRPARSER_SERIALIZE_BUF_SIZE - 1);
+
+    *state = REALLOC(*state, size);
+    if (!*state) {
+        FREE(old_state);
+        ERROR("Out of memory allocating serialization buffer");
+        return -1;
+    }
+
+    *pos = *state + used;
+    *remain = size - used;
+
+    return 0;
+}
+
+static int serialize_int(struct usbredirparser_priv *parser,
+                         uint8_t **state, uint8_t **pos, uint32_t *remain,
+                         uint32_t val, const char *desc)
+{
+    DEBUGP("serializing int %08x : %s", val, desc);
+
+    if (serialize_alloc(parser, state, pos, remain, sizeof(uint32_t)))
+        return -1;
+
+    memcpy(*pos, &val, sizeof(uint32_t));
+    *pos += sizeof(uint32_t);
+    *remain -= sizeof(uint32_t);
+
+    return 0;
+}
+
+static int unserialize_int(struct usbredirparser_priv *parser,
+                           uint8_t **pos, uint32_t *remain, uint32_t *val,
+                           const char *desc)
+{
+    if (*remain < sizeof(uint32_t)) {
+        ERROR("error buffer underrun while unserializing state");
+        return -1;
+    }
+    memcpy(val, *pos, sizeof(uint32_t));
+    *pos += sizeof(uint32_t);
+    *remain -= sizeof(uint32_t);
+
+    DEBUGP("unserialized int %08x : %s", *val, desc);
+
+    return 0;
+}
+
+static int serialize_data(struct usbredirparser_priv *parser,
+                          uint8_t **state, uint8_t **pos, uint32_t *remain,
+                          uint8_t *data, uint32_t len, const char *desc)
+{
+    DEBUGP("serializing %d bytes of %s data", len, desc);
+    if (len >= 8)
+        DEBUGP("First 8 bytes of %s: %02x %02x %02x %02x %02x %02x %02x %02x",
+              desc, data[0], data[1], data[2], data[3],
+                    data[4], data[5], data[6], data[7]);
+
+    if (serialize_alloc(parser, state, pos, remain, sizeof(uint32_t) + len))
+        return -1;
+
+    memcpy(*pos, &len, sizeof(uint32_t));
+    *pos += sizeof(uint32_t);
+    *remain -= sizeof(uint32_t);
+
+    memcpy(*pos, data, len);
+    *pos += len;
+    *remain -= len;
+
+    return 0;
+}
+
+/* If *data == NULL, allocs buffer dynamically, else len_in_out must contain
+   the length of the passed in buffer. */
+static int unserialize_data(struct usbredirparser_priv *parser,
+                            uint8_t **pos, uint32_t *remain,
+                            uint8_t **data, uint32_t *len_in_out,
+                            const char *desc)
+{
+    uint32_t len;
+
+    if (*remain < sizeof(uint32_t)) {
+        ERROR("error buffer underrun while unserializing state");
+        return -1;
+    }
+    memcpy(&len, *pos, sizeof(uint32_t));
+    *pos += sizeof(uint32_t);
+    *remain -= sizeof(uint32_t);
+
+    if (*remain < len) {
+        ERROR("error buffer underrun while unserializing state");
+        return -1;
+    }
+    if (*data == NULL && len > 0) {
+        *data = MALLOC(len);
+        if (!*data) {
+            ERROR("Out of memory allocating unserialize buffer");
+            return -1;
+        }
+    } else {
+        if (*len_in_out < len) {
+            ERROR("error buffer overrun while unserializing state");
+            return -1;
+        }
+    }
+
+    memcpy(*data, *pos, len);
+    *pos += len;
+    *remain -= len;
+    *len_in_out = len;
+
+    DEBUGP("unserialized %d bytes of %s data", len, desc);
+    if (len >= 8)
+        DEBUGP("First 8 bytes of %s: %02x %02x %02x %02x %02x %02x %02x %02x",
+              desc, (*data)[0], (*data)[1], (*data)[2], (*data)[3],
+              (*data)[4], (*data)[5], (*data)[6], (*data)[7]);
+
+    return 0;
+}
+
+int usbredirparser_serialize(struct usbredirparser *parser_pub,
+                             uint8_t **state_dest, int *state_len)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usbredirparser_buf *wbuf;
+    uint8_t *write_buf_count_pos, *state = NULL, *pos = NULL;
+    uint32_t write_buf_count = 0, len, remain = 0;
+
+    *state_dest = NULL;
+    *state_len = 0;
+
+    if (serialize_int(parser, &state, &pos, &remain,
+                                   USBREDIRPARSER_SERIALIZE_MAGIC, "magic"))
+        return -1;
+
+    /* To be replaced with length later */
+    if (serialize_int(parser, &state, &pos, &remain, 0, "length"))
+        return -1;
+
+    if (serialize_data(parser, &state, &pos, &remain,
+                       (uint8_t *)parser->our_caps,
+                       USB_REDIR_CAPS_SIZE * sizeof(int32_t), "our_caps"))
+        return -1;
+
+    if (parser->have_peer_caps) {
+        if (serialize_data(parser, &state, &pos, &remain,
+                           (uint8_t *)parser->peer_caps,
+                           USB_REDIR_CAPS_SIZE * sizeof(int32_t), "peer_caps"))
+            return -1;
+    } else {
+        if (serialize_int(parser, &state, &pos, &remain, 0, "peer_caps_len"))
+            return -1;
+    }
+
+    if (serialize_int(parser, &state, &pos, &remain, parser->to_skip, "skip"))
+        return -1;
+
+    if (serialize_data(parser, &state, &pos, &remain,
+                       (uint8_t *)&parser->header, parser->header_read,
+                       "header"))
+        return -1;
+
+    if (serialize_data(parser, &state, &pos, &remain,
+                       parser->type_header, parser->type_header_read,
+                       "type_header"))
+        return -1;
+
+    if (serialize_data(parser, &state, &pos, &remain,
+                       parser->data, parser->data_read, "packet-data"))
+        return -1;
+
+    write_buf_count_pos = pos;
+    /* To be replaced with write_buf_count later */
+    if (serialize_int(parser, &state, &pos, &remain, 0, "write_buf_count"))
+        return -1;
+
+    wbuf = parser->write_buf;
+    while (wbuf) {
+        if (serialize_data(parser, &state, &pos, &remain,
+                           wbuf->buf + wbuf->pos, wbuf->len - wbuf->pos,
+                           "write-buf"))
+            return -1;
+        write_buf_count++;
+        wbuf = wbuf->next;
+    }
+    /* Patch in write_buf_count */
+    memcpy(write_buf_count_pos, &write_buf_count, sizeof(int32_t));
+
+    /* Patch in length */
+    len = pos - state;
+    memcpy(state + sizeof(int32_t), &len, sizeof(int32_t));
+
+    *state_dest = state;
+    *state_len = len;
+
+    return 0;
+}
+
+int usbredirparser_unserialize(struct usbredirparser *parser_pub,
+                               uint8_t *state, int len)
+{
+    struct usbredirparser_priv *parser =
+        (struct usbredirparser_priv *)parser_pub;
+    struct usbredirparser_buf *wbuf, **next;
+    uint32_t orig_caps[USB_REDIR_CAPS_SIZE];
+    uint8_t *data;
+    uint32_t i, l, header_len, remain = len;
+
+    if (unserialize_int(parser, &state, &remain, &i, "magic"))
+        return -1;
+    if (i != USBREDIRPARSER_SERIALIZE_MAGIC) {
+        ERROR("error unserialize magic mismatch");
+        return -1;
+    }
+
+    if (unserialize_int(parser, &state, &remain, &i, "length"))
+        return -1;
+    if (i != len) {
+        ERROR("error unserialize length mismatch");
+        return -1;
+    }
+
+    data = (uint8_t *)parser->our_caps;
+    i = USB_REDIR_CAPS_SIZE * sizeof(int32_t);
+    memcpy(orig_caps, parser->our_caps, i);
+    if (unserialize_data(parser, &state, &remain, &data, &i, "our_caps"))
+        return -1;
+    if (memcmp(parser->our_caps, orig_caps,
+               USB_REDIR_CAPS_SIZE * sizeof(int32_t)) != 0) {
+        ERROR("error unserialize caps mismatch");
+        return -1;
+    }
+
+    data = (uint8_t *)parser->peer_caps;
+    i = USB_REDIR_CAPS_SIZE * sizeof(int32_t);
+    if (unserialize_data(parser, &state, &remain, &data, &i, "peer_caps"))
+        return -1;
+    if (i)
+        parser->have_peer_caps = 1;
+
+    if (unserialize_int(parser, &state, &remain, &i, "skip"))
+        return -1;
+    parser->to_skip = i;
+
+    header_len = usbredirparser_get_header_len(parser_pub);
+    data = (uint8_t *)&parser->header;
+    i = header_len;
+    if (unserialize_data(parser, &state, &remain, &data, &i, "header"))
+        return -1;
+    parser->header_read = i;
+
+    /* Set various length field froms the header (if we've a header) */
+    if (parser->header_read == header_len) {
+                int type_header_len =
+                    usbredirparser_get_type_header_len(parser_pub,
+                                                       parser->header.type, 0);
+                if (type_header_len < 0 || 
+                    type_header_len > sizeof(parser->type_header) || 
+                    parser->header.length < type_header_len ||
+                    (parser->header.length > type_header_len &&
+                     !usbredirparser_expect_extra_data(parser))) {
+                    ERROR("error unserialize packet header invalid");
+                    return -1;
+                }
+                parser->type_header_len = type_header_len;
+                parser->data_len = parser->header.length - type_header_len;
+    }
+
+    data = parser->type_header;
+    i = parser->type_header_len;
+    if (unserialize_data(parser, &state, &remain, &data, &i, "type_header"))
+        return -1;
+    parser->type_header_read = i;
+
+    if (parser->data_len) {
+        parser->data = MALLOC(parser->data_len);
+        if (!parser->data) {
+            ERROR("Out of memory allocating unserialize buffer");
+            return -1;
+        }
+    }
+    i = parser->data_len;
+    if (unserialize_data(parser, &state, &remain, &parser->data, &i, "data"))
+        return -1;
+    parser->data_read = i;
+
+    /* Get the write buffer count and the write buffers */
+    if (unserialize_int(parser, &state, &remain, &i, "write_buf_count"))
+        return -1;
+    next = &parser->write_buf;
+    while (i) {
+        wbuf = CALLOC(1, sizeof(*wbuf));
+        if (!wbuf) {
+            ERROR("Out of memory allocating unserialize buffer");
+            return -1;
+        }
+        *next = wbuf;
+        l = 0;
+        if (unserialize_data(parser, &state, &remain, &wbuf->buf, &l, "wbuf"))
+            return -1;
+        wbuf->len = l;
+        next = &wbuf->next;
+        i--;
+    }
+
+    if (remain) {
+        ERROR("error unserialize %d bytes of extraneous state data", remain);
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/drivers/usb/usbredir/usbredirparser.h b/drivers/usb/usbredir/usbredirparser.h
new file mode 100644
index 0000000..ffeb1d7
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirparser.h
@@ -0,0 +1,386 @@
+/* usbredirparser.h usb redirection protocol parser header
+
+   Copyright 2010-2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __USBREDIRPARSER_H
+#define __USBREDIRPARSER_H
+
+#include "usbredirproto.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct usbredirparser;
+struct usbredirfilter_rule;
+
+/* Called by a usbredirparser to log various messages */
+enum { usbredirparser_none, usbredirparser_error, usbredirparser_warning,
+       usbredirparser_info, usbredirparser_debug, usbredirparser_debug_data };
+typedef void (*usbredirparser_log)(void *priv, int level, const char *msg);
+
+/* Called by a usbredirparser to read/write data to its peer.
+   Must return the amount of bytes read/written, 0 when the read/write would
+   block (and this is undesirable) and -1 on error.
+
+   If the usbredirparser_fl_write_cb_owns_buffer flag is passed to
+   usbredirparser_init, then the usbredirparser_write callback becomes
+   the owner of the buffer pointed to by data and should call
+   usbredirparser_free_write_buffer() when it is done with the buffer.
+   In this case the callback is not allowed to return any amount of bytes
+   written, it must either accept the entire buffer (return count),
+   or signal blocking (return 0) or error (return -1). Returning any other
+   value will result in a call to abort().
+*/
+typedef int (*usbredirparser_read)(void *priv, uint8_t *data, int count);
+typedef int (*usbredirparser_write)(void *priv, uint8_t *data, int count);
+
+/* Locking functions for use by multithread apps */
+typedef void *(*usbredirparser_alloc_lock)(void);
+typedef void (*usbredirparser_lock)(void *lock);
+typedef void (*usbredirparser_unlock)(void *lock);
+typedef void (*usbredirparser_free_lock)(void *lock);
+
+/* The below callbacks are called when a complete packet of the relevant
+   type has been received.
+
+   Note that the passed in packet-type-specific-header's lifetime is only
+   guarenteed to be that of the callback.
+
+   Control packets: */
+typedef void (*usbredirparser_hello)(void *priv,
+    struct usb_redir_hello_header *hello);
+typedef void (*usbredirparser_device_connect)(void *priv,
+    struct usb_redir_device_connect_header *device_connect);
+typedef void (*usbredirparser_device_disconnect)(void *priv);
+typedef void (*usbredirparser_reset)(void *priv);
+typedef void (*usbredirparser_interface_info)(void *priv,
+    struct usb_redir_interface_info_header *interface_info);
+typedef void (*usbredirparser_ep_info)(void *priv,
+    struct usb_redir_ep_info_header *ep_info);
+typedef void (*usbredirparser_set_configuration)(void *priv,
+    uint64_t id, struct usb_redir_set_configuration_header *set_configuration);
+typedef void (*usbredirparser_get_configuration)(void *priv, uint64_t id);
+typedef void (*usbredirparser_configuration_status)(void *priv,
+    uint64_t id, struct usb_redir_configuration_status_header *configuration_status);
+typedef void (*usbredirparser_set_alt_setting)(void *priv,
+    uint64_t id, struct usb_redir_set_alt_setting_header *set_alt_setting);
+typedef void (*usbredirparser_get_alt_setting)(void *priv,
+    uint64_t id, struct usb_redir_get_alt_setting_header *get_alt_setting);
+typedef void (*usbredirparser_alt_setting_status)(void *priv,
+    uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status);
+typedef void (*usbredirparser_start_iso_stream)(void *priv,
+    uint64_t id, struct usb_redir_start_iso_stream_header *start_iso_stream);
+typedef void (*usbredirparser_stop_iso_stream)(void *priv,
+    uint64_t id, struct usb_redir_stop_iso_stream_header *stop_iso_stream);
+typedef void (*usbredirparser_iso_stream_status)(void *priv,
+    uint64_t id, struct usb_redir_iso_stream_status_header *iso_stream_status);
+typedef void (*usbredirparser_start_interrupt_receiving)(void *priv,
+    uint64_t id, struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving);
+typedef void (*usbredirparser_stop_interrupt_receiving)(void *priv,
+    uint64_t id, struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving);
+typedef void (*usbredirparser_interrupt_receiving_status)(void *priv,
+    uint64_t id, struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status);
+typedef void (*usbredirparser_alloc_bulk_streams)(void *priv,
+    uint64_t id, struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams);
+typedef void (*usbredirparser_free_bulk_streams)(void *priv,
+    uint64_t id, struct usb_redir_free_bulk_streams_header *free_bulk_streams);
+typedef void (*usbredirparser_bulk_streams_status)(void *priv,
+    uint64_t id, struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+typedef void (*usbredirparser_cancel_data_packet)(void *priv, uint64_t id);
+typedef void (*usbredirparser_filter_reject)(void *priv);
+/* Note that the ownership of the rules array is passed on to the callback. */
+typedef void (*usbredirparser_filter_filter)(void *priv,
+    struct usbredirfilter_rule *rules, int rules_count);
+typedef void (*usbredirparser_device_disconnect_ack)(void *priv);
+typedef void (*usbredirparser_start_bulk_receiving)(void *priv,
+    uint64_t id, struct usb_redir_start_bulk_receiving_header *start_bulk_receiving);
+typedef void (*usbredirparser_stop_bulk_receiving)(void *priv,
+    uint64_t id, struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving);
+typedef void (*usbredirparser_bulk_receiving_status)(void *priv,
+    uint64_t id, struct usb_redir_bulk_receiving_status_header *bulk_receiving_status);
+
+/* Data packets:
+
+   Note that ownership of the the data buffer (if not NULL) is passed on to
+   the callback. The callback should free it by calling
+   usbredirparser_free_packet_data when it is done with it. */
+typedef void (*usbredirparser_control_packet)(void *priv,
+    uint64_t id, struct usb_redir_control_packet_header *control_header,
+    uint8_t *data, int data_len);
+typedef void (*usbredirparser_bulk_packet)(void *priv,
+    uint64_t id, struct usb_redir_bulk_packet_header *bulk_header,
+    uint8_t *data, int data_len);
+typedef void (*usbredirparser_iso_packet)(void *priv,
+    uint64_t id, struct usb_redir_iso_packet_header *iso_header,
+    uint8_t *data, int data_len);
+typedef void (*usbredirparser_interrupt_packet)(void *priv,
+    uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_header,
+    uint8_t *data, int data_len);
+typedef void (*usbredirparser_buffered_bulk_packet)(void *priv, uint64_t id,
+    struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header,
+    uint8_t *data, int data_len);
+
+
+/* Public part of the data allocated by usbredirparser_alloc, *never* allocate
+   a usbredirparser struct yourself, it may be extended in the future to add
+   callbacks for new packet types (which will then get added at the end),
+   *and* usbredirparser_alloc will also alloc some space behind it for
+   private data */
+struct usbredirparser {
+    /* app private data passed into all callbacks as the priv argument */
+    void *priv;
+    /* non packet callbacks */
+    usbredirparser_log log_func;
+    usbredirparser_read read_func;
+    usbredirparser_write write_func;
+    /* usb-redir-protocol v0.3 control packet complete callbacks */
+    usbredirparser_device_connect device_connect_func;
+    usbredirparser_device_disconnect device_disconnect_func;
+    usbredirparser_reset reset_func;
+    usbredirparser_interface_info interface_info_func;
+    usbredirparser_ep_info ep_info_func;
+    usbredirparser_set_configuration set_configuration_func;
+    usbredirparser_get_configuration get_configuration_func;
+    usbredirparser_configuration_status configuration_status_func;
+    usbredirparser_set_alt_setting set_alt_setting_func;
+    usbredirparser_get_alt_setting get_alt_setting_func;
+    usbredirparser_alt_setting_status alt_setting_status_func;
+    usbredirparser_start_iso_stream start_iso_stream_func;
+    usbredirparser_stop_iso_stream stop_iso_stream_func;
+    usbredirparser_iso_stream_status iso_stream_status_func;
+    usbredirparser_start_interrupt_receiving start_interrupt_receiving_func;
+    usbredirparser_stop_interrupt_receiving stop_interrupt_receiving_func;
+    usbredirparser_interrupt_receiving_status interrupt_receiving_status_func;
+    usbredirparser_alloc_bulk_streams alloc_bulk_streams_func;
+    usbredirparser_free_bulk_streams free_bulk_streams_func;
+    usbredirparser_bulk_streams_status bulk_streams_status_func;
+    usbredirparser_cancel_data_packet cancel_data_packet_func;
+    /* usb-redir-protocol v0.3 data packet complete callbacks */
+    usbredirparser_control_packet control_packet_func;
+    usbredirparser_bulk_packet bulk_packet_func;
+    usbredirparser_iso_packet iso_packet_func;
+    usbredirparser_interrupt_packet interrupt_packet_func;
+    /* usbredir 0.3.2 new non packet callbacks (for multi-thread locking) */
+    usbredirparser_alloc_lock alloc_lock_func;
+    usbredirparser_lock lock_func;
+    usbredirparser_unlock unlock_func;
+    usbredirparser_free_lock free_lock_func;
+    /* usbredir 0.3.2 new control packet complete callbacks */
+    usbredirparser_hello hello_func;
+    /* usbredir 0.4 new control packet complete callbacks */
+    usbredirparser_filter_reject filter_reject_func;
+    usbredirparser_filter_filter filter_filter_func;
+    usbredirparser_device_disconnect_ack device_disconnect_ack_func;
+    /* usbredir 0.6 new control packet complete callbacks */
+    usbredirparser_start_bulk_receiving start_bulk_receiving_func;
+    usbredirparser_stop_bulk_receiving stop_bulk_receiving_func;
+    usbredirparser_bulk_receiving_status bulk_receiving_status_func;
+    /* usbredir 0.6 new data packet complete callbacks */
+    usbredirparser_buffered_bulk_packet buffered_bulk_packet_func;
+};
+
+/* Allocate a usbredirparser, after this the app should set the callback app
+   private data and all the callbacks it needs, before calling
+   usbredirparser_init */
+struct usbredirparser *usbredirparser_create(void);
+
+/* Set capability cap in the USB_REDIR_CAPS_SIZE sized caps array,
+   this is a helper function to set capabilities in the caps array
+   passed to usbredirparser_init(). */
+void usbredirparser_caps_set_cap(uint32_t *caps, int cap);
+
+/* Init the parser, this will queue an initial usb_redir_hello packet,
+   sending the version and caps to the peer, as well as configure the parsing
+   according to the passed in flags. */
+enum {
+    usbredirparser_fl_usb_host = 0x01,
+    usbredirparser_fl_write_cb_owns_buffer = 0x02,
+    usbredirparser_fl_no_hello = 0x04,
+};
+
+void usbredirparser_init(struct usbredirparser *parser,
+    const char *version, uint32_t *caps, int caps_len, int flags);
+
+void usbredirparser_destroy(struct usbredirparser *parser);
+
+/* See if our side has a certain cap (checks the caps passed into _init) */
+int usbredirparser_have_cap(struct usbredirparser *parser, int cap);
+
+/* Check if we've received the caps from the peer */
+int usbredirparser_have_peer_caps(struct usbredirparser *parser);
+
+/* Check if our peer has a certain capability. Note this function
+   should not be used before the hello_func callback has been called. */
+int usbredirparser_peer_has_cap(struct usbredirparser *parser, int cap);
+
+/* Call this whenever there is data ready from the otherside to parse
+   On an usbredirparser_read_io_error this function will continue where it
+   left of the last time on the next call. On an
+   usbredirparser_read_parse_error it will skip to the next packet (*).
+   *) As determined by the faulty's package headers length field */
+enum {
+    usbredirparser_read_io_error    = -1,
+    usbredirparser_read_parse_error = -2,
+};
+int usbredirparser_do_read(struct usbredirparser *parser);
+
+/* This returns the number of usbredir packets queued up for writing */
+int usbredirparser_has_data_to_write(struct usbredirparser *parser);
+
+/* Call this when usbredirparser_has_data_to_write returns > 0
+   returns 0 on success, -1 if a write error happened.
+   If a write error happened, this function will retry writing any queued data
+   on the next call, and will continue doing so until it has succeeded! */
+enum {
+    usbredirparser_write_io_error   = -1,
+};
+int usbredirparser_do_write(struct usbredirparser *parser);
+
+/* See usbredirparser_write documentation */
+void usbredirparser_free_write_buffer(struct usbredirparser *parser,
+    uint8_t *data);
+
+/* See the data packet callbacks documentation */
+void usbredirparser_free_packet_data(struct usbredirparser *parser,
+    uint8_t *data);
+
+/* Functions to marshall and queue a packet for sending to its peer. Note:
+   1) it will not be actually send until usbredirparser_do_write is called
+   2) if their is not enough memory for buffers the packet will be dropped
+      (and an error message will be logged */
+/* Control packets: */
+
+/* Note this function should not be used before the hello_func callback has
+   been called (as it checks the usb_redir_cap_connect_device_version cap). */
+void usbredirparser_send_device_connect(struct usbredirparser *parser,
+    struct usb_redir_device_connect_header *device_connect);
+void usbredirparser_send_device_disconnect(struct usbredirparser *parser);
+void usbredirparser_send_reset(struct usbredirparser *parser);
+void usbredirparser_send_interface_info(struct usbredirparser *parser,
+    struct usb_redir_interface_info_header *interface_info);
+void usbredirparser_send_ep_info(struct usbredirparser *parser,
+    struct usb_redir_ep_info_header *ep_info);
+void usbredirparser_send_set_configuration(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_set_configuration_header *set_configuration);
+void usbredirparser_send_get_configuration(struct usbredirparser *parser,
+    uint64_t id);
+void usbredirparser_send_configuration_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_configuration_status_header *configuration_status);
+void usbredirparser_send_set_alt_setting(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_set_alt_setting_header *set_alt_setting);
+void usbredirparser_send_get_alt_setting(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_get_alt_setting_header *get_alt_setting);
+void usbredirparser_send_alt_setting_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_alt_setting_status_header *alt_setting_status);
+void usbredirparser_send_start_iso_stream(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_iso_stream_header *start_iso_stream);
+void usbredirparser_send_stop_iso_stream(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_iso_stream_header *stop_iso_stream);
+void usbredirparser_send_iso_stream_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_iso_stream_status_header *iso_stream_status);
+void usbredirparser_send_start_interrupt_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving);
+void usbredirparser_send_stop_interrupt_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving);
+void usbredirparser_send_interrupt_receiving_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status);
+void usbredirparser_send_alloc_bulk_streams(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams);
+void usbredirparser_send_free_bulk_streams(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_free_bulk_streams_header *free_bulk_streams);
+void usbredirparser_send_bulk_streams_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+void usbredirparser_send_cancel_data_packet(struct usbredirparser *parser,
+    uint64_t id);
+void usbredirparser_send_filter_reject(struct usbredirparser *parser);
+void usbredirparser_send_filter_filter(struct usbredirparser *parser,
+    const struct usbredirfilter_rule *rules, int rules_count);
+void usbredirparser_send_start_bulk_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_start_bulk_receiving_header *start_bulk_receiving);
+void usbredirparser_send_stop_bulk_receiving(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving);
+void usbredirparser_send_bulk_receiving_status(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_receiving_status_header *bulk_receiving_status);
+/* Data packets: */
+void usbredirparser_send_control_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_control_packet_header *control_header,
+    uint8_t *data, int data_len);
+void usbredirparser_send_bulk_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_bulk_packet_header *bulk_header,
+    uint8_t *data, int data_len);
+void usbredirparser_send_iso_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_iso_packet_header *iso_header,
+    uint8_t *data, int data_len);
+void usbredirparser_send_interrupt_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_interrupt_packet_header *interrupt_header,
+    uint8_t *data, int data_len);
+void usbredirparser_send_buffered_bulk_packet(struct usbredirparser *parser,
+    uint64_t id,
+    struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header,
+    uint8_t *data, int data_len);
+
+
+/* Serialization */
+
+/* This function serializes the current usbredirparser state. It will allocate
+   a large enough buffer for this itself and store this in state_dest, it will
+   store the size of this buffer in state_len.
+
+   Return value: 0 on success, -1 on error (out of memory).
+
+   The buffer should be free-ed by the caller using free(). */
+int usbredirparser_serialize(struct usbredirparser *parser,
+                             uint8_t **state_dest, int *state_len);
+
+/* This function sets the current usbredirparser state from a serialized state.
+   This function assumes that the parser has just been initialized with the
+   usbredirparser_fl_no_hello flag.
+
+   Return value: 0 on success, -1 on error (out of memory, or
+                                            invalid state data). */
+int usbredirparser_unserialize(struct usbredirparser *parser_pub,
+                               uint8_t *state, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/usb/usbredir/usbredirproto-compat.h b/drivers/usb/usbredir/usbredirproto-compat.h
new file mode 100644
index 0000000..55d2ccf
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirproto-compat.h
@@ -0,0 +1,88 @@
+/* usbredirproto-compat.h usb redirection compatibility protocol definitions
+
+   Copyright 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __USBREDIRPROTO_COMPAT_H
+#define __USBREDIRPROTO_COMPAT_H
+
+/* PACK macros borrowed from spice-protocol */
+#ifdef __GNUC__
+
+#define ATTR_PACKED __attribute__ ((__packed__))
+
+#ifdef __MINGW32__
+#pragma pack(push,1)
+#endif
+
+#else
+
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#pragma warning(disable:4200)
+#pragma warning(disable:4103)
+
+#endif
+
+#if ! defined(__KERNEL__)
+#include <stdint.h>
+#endif
+
+struct usb_redir_device_connect_header_no_device_version {
+    uint8_t speed;
+    uint8_t device_class;
+    uint8_t device_subclass;
+    uint8_t device_protocol;
+    uint16_t vendor_id;
+    uint16_t product_id;
+} ATTR_PACKED;
+
+struct usb_redir_ep_info_header_no_max_pktsz {
+    uint8_t type[32];
+    uint8_t interval[32];
+    uint8_t interface[32];
+} ATTR_PACKED;
+
+struct usb_redir_ep_info_header_no_max_streams {
+    uint8_t type[32];
+    uint8_t interval[32];
+    uint8_t interface[32];
+    uint16_t max_packet_size[32];
+} ATTR_PACKED;
+
+struct usb_redir_header_32bit_id {
+    uint32_t type;
+    uint32_t length;
+    uint32_t id;
+} ATTR_PACKED;
+
+struct usb_redir_bulk_packet_header_16bit_length {
+    uint8_t endpoint;
+    uint8_t status;
+    uint16_t length;
+    uint32_t stream_id;
+} ATTR_PACKED;
+
+#undef ATTR_PACKED
+
+#if defined(__MINGW32__) || !defined(__GNUC__)
+#pragma pack(pop)
+#endif
+
+#endif
diff --git a/drivers/usb/usbredir/usbredirproto.h b/drivers/usb/usbredir/usbredirproto.h
new file mode 100644
index 0000000..46b82e5
--- /dev/null
+++ b/drivers/usb/usbredir/usbredirproto.h
@@ -0,0 +1,309 @@
+/* usbredirproto.h usb redirection protocol definitions
+
+   Copyright 2010-2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede@...hat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __USBREDIRPROTO_H
+#define __USBREDIRPROTO_H
+
+/* PACK macros borrowed from spice-protocol */
+#ifdef __GNUC__
+
+#define ATTR_PACKED __attribute__ ((__packed__))
+
+#ifdef __MINGW32__
+#pragma pack(push,1)
+#endif
+
+#else
+
+#pragma pack(push)
+#pragma pack(1)
+#define ATTR_PACKED
+#pragma warning(disable:4200)
+#pragma warning(disable:4103)
+
+#endif
+
+#if ! defined(__KERNEL__)
+#include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define USBREDIR_VERSION 0x000700 /* 0.7 [.0] */
+
+enum {
+    usb_redir_success,
+    usb_redir_cancelled,    /* The transfer was cancelled */
+    usb_redir_inval,        /* Invalid packet type / length / ep, etc. */
+    usb_redir_ioerror,      /* IO error */
+    usb_redir_stall,        /* Stalled */
+    usb_redir_timeout,      /* Request timed out */
+    usb_redir_babble,       /* The device has "babbled" (since 0.4.2) */
+};
+
+enum {
+    /* Note these 4 match the usb spec! */
+    usb_redir_type_control,
+    usb_redir_type_iso,
+    usb_redir_type_bulk,
+    usb_redir_type_interrupt,
+    usb_redir_type_invalid = 255
+};
+
+enum {
+    usb_redir_speed_low,
+    usb_redir_speed_full,
+    usb_redir_speed_high,
+    usb_redir_speed_super,
+    usb_redir_speed_unknown = 255
+};
+
+enum {
+    /* Control packets */
+    usb_redir_hello,
+    usb_redir_device_connect,
+    usb_redir_device_disconnect,
+    usb_redir_reset,
+    usb_redir_interface_info,
+    usb_redir_ep_info,
+    usb_redir_set_configuration,
+    usb_redir_get_configuration,
+    usb_redir_configuration_status,
+    usb_redir_set_alt_setting,
+    usb_redir_get_alt_setting,
+    usb_redir_alt_setting_status,
+    usb_redir_start_iso_stream,
+    usb_redir_stop_iso_stream,
+    usb_redir_iso_stream_status,
+    usb_redir_start_interrupt_receiving,
+    usb_redir_stop_interrupt_receiving,
+    usb_redir_interrupt_receiving_status,
+    usb_redir_alloc_bulk_streams,
+    usb_redir_free_bulk_streams,
+    usb_redir_bulk_streams_status,
+    usb_redir_cancel_data_packet,
+    usb_redir_filter_reject,
+    usb_redir_filter_filter,
+    usb_redir_device_disconnect_ack,
+    usb_redir_start_bulk_receiving,
+    usb_redir_stop_bulk_receiving,
+    usb_redir_bulk_receiving_status,
+
+    /* Data packets */
+    usb_redir_control_packet = 100,
+    usb_redir_bulk_packet,
+    usb_redir_iso_packet,
+    usb_redir_interrupt_packet,
+    usb_redir_buffered_bulk_packet,
+};
+
+enum {
+    /* Supports USB 3 bulk streams */
+    usb_redir_cap_bulk_streams, 
+    /* The device_connect packet has the device_version_bcd field */
+    usb_redir_cap_connect_device_version,
+    /* Supports usb_redir_filter_reject and usb_redir_filter_filter pkts */
+    usb_redir_cap_filter,
+    /* Supports the usb_redir_device_disconnect_ack packet */
+    usb_redir_cap_device_disconnect_ack,
+    /* The ep_info packet has the max_packet_size field */
+    usb_redir_cap_ep_info_max_packet_size,
+    /* Supports 64 bits ids in usb_redir_header */
+    usb_redir_cap_64bits_ids,
+    /* Supports 32 bits length in usb_redir_bulk_packet_header */
+    usb_redir_cap_32bits_bulk_length,
+    /* Supports bulk receiving / buffered bulk input */
+    usb_redir_cap_bulk_receiving,
+};
+/* Number of uint32_t-s needed to hold all (known) capabilities */
+#define USB_REDIR_CAPS_SIZE 1
+
+struct usb_redir_header {
+    uint32_t type;
+    uint32_t length;
+    uint64_t id;  
+} ATTR_PACKED;
+
+struct usb_redir_hello_header {
+    char     version[64];
+    uint32_t capabilities[0];
+} ATTR_PACKED;
+
+struct usb_redir_device_connect_header {
+    uint8_t speed;
+    uint8_t device_class;
+    uint8_t device_subclass;
+    uint8_t device_protocol;
+    uint16_t vendor_id;
+    uint16_t product_id;
+    uint16_t device_version_bcd;
+} ATTR_PACKED;
+
+struct usb_redir_interface_info_header {
+    uint32_t interface_count;
+    uint8_t interface[32];
+    uint8_t interface_class[32];
+    uint8_t interface_subclass[32];
+    uint8_t interface_protocol[32];
+} ATTR_PACKED;
+
+struct usb_redir_ep_info_header {
+    uint8_t type[32];
+    uint8_t interval[32];
+    uint8_t interface[32];
+    uint16_t max_packet_size[32];
+    uint32_t max_streams[32];
+} ATTR_PACKED;
+
+struct usb_redir_set_configuration_header {
+    uint8_t configuration;
+} ATTR_PACKED;
+
+struct usb_redir_configuration_status_header {
+    uint8_t status;
+    uint8_t configuration;
+} ATTR_PACKED;
+
+struct usb_redir_set_alt_setting_header {
+    uint8_t interface;
+    uint8_t alt;
+} ATTR_PACKED;
+
+struct usb_redir_get_alt_setting_header {
+    uint8_t interface;
+} ATTR_PACKED;
+
+struct usb_redir_alt_setting_status_header {
+    uint8_t status;
+    uint8_t interface;
+    uint8_t alt;
+} ATTR_PACKED;
+
+struct usb_redir_start_iso_stream_header {
+    uint8_t endpoint;
+    uint8_t pkts_per_urb;
+    uint8_t no_urbs;
+} ATTR_PACKED;
+
+struct usb_redir_stop_iso_stream_header {
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_iso_stream_status_header {
+    uint8_t status;
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_start_interrupt_receiving_header {
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_stop_interrupt_receiving_header {
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_interrupt_receiving_status_header {
+    uint8_t status;
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_alloc_bulk_streams_header {
+    uint32_t endpoints; /* bitmask indicating on which eps to alloc streams */
+    uint32_t no_streams;
+} ATTR_PACKED;
+
+struct usb_redir_free_bulk_streams_header {
+    uint32_t endpoints; /* bitmask indicating on which eps to free streams */
+} ATTR_PACKED;
+
+struct usb_redir_bulk_streams_status_header {
+    uint32_t endpoints; /* bitmask indicating eps this status message is for */
+    uint32_t no_streams;
+    uint8_t status;
+} ATTR_PACKED;
+
+struct usb_redir_start_bulk_receiving_header {
+    uint32_t stream_id;
+    uint32_t bytes_per_transfer;
+    uint8_t endpoint;
+    uint8_t no_transfers;
+} ATTR_PACKED;
+
+struct usb_redir_stop_bulk_receiving_header {
+    uint32_t stream_id;
+    uint8_t endpoint;
+} ATTR_PACKED;
+
+struct usb_redir_bulk_receiving_status_header {
+    uint32_t stream_id;
+    uint8_t endpoint;
+    uint8_t status;
+} ATTR_PACKED;
+
+struct usb_redir_control_packet_header {
+    uint8_t endpoint;
+    uint8_t request;
+    uint8_t requesttype;
+    uint8_t status;
+    uint16_t value;
+    uint16_t index;
+    uint16_t length; 
+} ATTR_PACKED;
+
+struct usb_redir_bulk_packet_header {
+    uint8_t endpoint;
+    uint8_t status;
+    uint16_t length;
+    uint32_t stream_id;
+    uint16_t length_high; /* High 16 bits of the packet length */
+} ATTR_PACKED;
+
+struct usb_redir_iso_packet_header {
+    uint8_t endpoint;
+    uint8_t status;
+    uint16_t length;
+} ATTR_PACKED;
+
+struct usb_redir_interrupt_packet_header {
+    uint8_t endpoint;
+    uint8_t status;
+    uint16_t length;
+} ATTR_PACKED;
+
+struct usb_redir_buffered_bulk_packet_header {
+    uint32_t stream_id;
+    uint32_t length;
+    uint8_t endpoint;
+    uint8_t status;
+} ATTR_PACKED;
+
+#undef ATTR_PACKED
+
+#if defined(__MINGW32__) || !defined(__GNUC__)
+#pragma pack(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ