[RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

Jeremy White
2015-06-30T22:00:48Z

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

Signed-off-by: Jeremy White <jwhite@codeweavers.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@codeweavers.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/usbredir/
+
USB PEGASUS DRIVER
M: Petko Manolov <petkan@nucleusys.com>
L: linux-usb@vger.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@redhat.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@redhat.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@redhat.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@redhat.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@redhat.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@redhat.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