2015-06-30 22:01:10

by Jeremy White

[permalink] [raw]
Subject: [RFC PATCH 0/1] RFC - Implement a usbredir kernel module

The following patch proposes a new kernel module to provide an
alternate protocol for transporting USB devices over a TCP/IP connection.

This flows from a few conversations on the Spice devel mailing list.[1][2]

I am relying heavily on the opinion of Hans de Goede, who believes
that the usbredir protocol is superior to that used by the existing
usbip kernel module[3].

The basic approach in this module is the same as usbip (virtual hcd driver)
but it attempts to support a configurable number of devices and hubs,
rather than just one hub and the limit of 8 devices.

This version of the code should be sufficient to review my general
approach, but it is known to be incomplete. I have successfully mounted
and operated a thumb drive across the interface.

Hopefully it is enough to warrant advice and comments; feedback is
greatly appreciated.

Anyone wishing to test this will also need a related set of usbredir patches,
recently sent to the spice-devel mailing list.[4]

Roughly half of the code (all of the code that fails checkpatch <grin>)
was copied, essentially unchanged, from the usbredir project. I felt
that diffability was more important than checkpatch; I stand ready to
be corrected.

After review, I plan to build a fairly extensive set of unit tests
(see the new usbredirtestserver in [3]), and would hope to submit
a complete module later in the summer.

Cheers,

Jeremy


[1] http://lists.freedesktop.org/archives/spice-devel/2014-August/017145.html
[2] http://lists.freedesktop.org/archives/spice-devel/2015-April/019451.html
[3] http://lists.freedesktop.org/archives/spice-devel/2014-August/017174.html
[4] http://lists.freedesktop.org/archives/spice-devel/2015-June/020595.html


2015-06-30 22:00:48

by Jeremy White

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

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

Signed-off-by: Jeremy White <[email protected]>
---
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 <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/usb/usbredir/
+
USB PEGASUS DRIVER
M: Petko Manolov <[email protected]>
L: [email protected]
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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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

2015-06-30 23:48:10

by Greg KH

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Tue, Jun 30, 2015 at 04:44:10PM -0500, Jeremy White wrote:
> This module uses the usbredir protocol and user space tools,
> which are used by the SPICE project.
>
> Signed-off-by: Jeremy White <[email protected]>
> ---
> 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

It's pointless to post a patch that you know has problems with it (i.e.
it's not even in proper kernel coding style), as it will never be
reviewed or even looked at.

Please do the most basic of polite things and fix this up before posting
things.

And really, all in one patch? That too is pretty hard to review...

thanks,

greg k-h

2015-07-01 03:36:29

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

>
> It's pointless to post a patch that you know has problems with it (i.e.
> it's not even in proper kernel coding style), as it will never be
> reviewed or even looked at.

Thanks for the reply, and I'm sorry for the clumsy ask.

I would still appreciate feedback on two points:

1. Is the basic premise reasonable? Is Hans correct in asserting
that an alternate USB over IP module will be considered?

2. Do I correctly understand that there are no circumstances where
copied code can be left unmodified? Even in the case where the copied
code is working, production code, and the changes would be just for style?

>
> Please do the most basic of polite things and fix this up before posting
> things.

It is often difficult for a newcomer to know what the polite thing is,
even after studying FAQs and documentation.

I appreciate your patience (and clue bats) as I try to learn.

>
> And really, all in one patch? That too is pretty hard to review...

Yeah. I see the point of pain. I did not see a solution as I formed
the patch, but I'll try harder before resending.

Cheers,

Jeremy

2015-07-01 05:44:32

by Greg KH

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Tue, Jun 30, 2015 at 10:34:25PM -0500, Jeremy White wrote:
> >
> >It's pointless to post a patch that you know has problems with it (i.e.
> >it's not even in proper kernel coding style), as it will never be
> >reviewed or even looked at.
>
> Thanks for the reply, and I'm sorry for the clumsy ask.
>
> I would still appreciate feedback on two points:
>
> 1. Is the basic premise reasonable? Is Hans correct in asserting that an
> alternate USB over IP module will be considered?

I have no idea, if it fully replaces the usbip functionality, I don't
see why that would be rejected. But why can't you just fix up usbip for
the issues you find lacking?

> 2. Do I correctly understand that there are no circumstances where copied
> code can be left unmodified? Even in the case where the copied code is
> working, production code, and the changes would be just for style?

I doubt the changes would just be for "style" if you are craming it into
the kernel tree, as it's a totally different environment from any other
place this code might have been running in before.

> >Please do the most basic of polite things and fix this up before posting
> >things.
>
> It is often difficult for a newcomer to know what the polite thing is, even
> after studying FAQs and documentation.

Did you read Documentation/SubmittingPatches?

> I appreciate your patience (and clue bats) as I try to learn.
>
> >
> >And really, all in one patch? That too is pretty hard to review...
>
> Yeah. I see the point of pain. I did not see a solution as I formed the
> patch, but I'll try harder before resending.

Remember you need to make this trivial to review in order to get it
accepted. You have to do extra work because of this because our limited
resource is reviewers and maintainers, not developers.

thanks,

greg k-h

2015-07-01 09:06:33

by Daniel P. Berrangé

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Tue, Jun 30, 2015 at 04:44:10PM -0500, Jeremy White wrote:
> This module uses the usbredir protocol and user space tools,
> which are used by the SPICE project.
>
> Signed-off-by: Jeremy White <[email protected]>

[snip]

> 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.

[snip]

> 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.

What is the security story here ? If I am understanding correctly, you have
a userspace helper which opens a socket, and does a connect() to establish
the connection to the remote system, and then tells the kernel to use the
file descriptor associated with the socket.

Assuming that's correct, then this seems to imply that the socket has raw
plain text data being sent/received, and thus precludes the possibility
of running any security protocol like TLS unless the kernel wants to have
an impl of the TLS protocol.

I don't really think it is sensible to be defining & implementing new
network services which can't support strong encryption and authentication.
Rather than passing the file descriptor to the kernel and having it do
the I/O directly, I think it would be better to dissassociate the kernel
from the network transport, and thus leave all sockets layer data I/O
to userspace daemons so they can layer in TLS or SASL or whatever else
is appropriate for the security need.

Regards,
Daniel
--
|: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org -o- http://virt-manager.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

2015-07-01 15:56:08

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/01/2015 12:44 AM, Greg KH wrote:
> On Tue, Jun 30, 2015 at 10:34:25PM -0500, Jeremy White wrote:
>> 1. Is the basic premise reasonable? Is Hans correct in asserting that an
>> alternate USB over IP module will be considered?
>
> I have no idea, if it fully replaces the usbip functionality, I don't
> see why that would be rejected. But why can't you just fix up usbip for
> the issues you find lacking?

This is what Hans said 5 years ago: [1]

>
> 3) The protocol itself is far from ideal.
>
> Number 3 is the big deal breaker for me. I've looked at the
> (undocumented) protocol by sifting through the source. And it is
> very low level, all it does is shove usb packets back and forth
> over the network. It has no concept of configuration
> setting (the docs say make sure the device is in the right
> configuration before sharing it). No concept of caching things
> like descriptors, active configuration, per interface alt setting,
> etc.
>
> Besides missing a lot of useful smarts the whole one packet at a
> time approach does not really fly when it comes to isoc endpoints.
> As there paper states, the vm-host / guest os drivers need to
> make sure enough packets are submitted / queued at all time
> to gap the network delay / fill the network pipe.
>
> For iso endpoints it makes much more sense to have a start / stop
> stream model, where the usb-host "pumpes" the urb ringbuffer and
> sends out data received from the usb device to the vm-host
> (isoc input endp case), or sends data received from the vm-host
> (through a buffer to deal with network jitter) to the isoc output
> endpoint.
>
> This also halves the number of packets which need to be
> send over the network, as their is no need for the vm-host to send
> a request for each packet once an input stream has started / for
> the usb-host to send an ack for each delivered packet for an ouput
> stream. It would still send an error when an error occurs, but their
> is no reason to ack all delivered packets. Given the delay
> caused by buffering, etc. not being able to match up the error to
> an exact packet is not important, as from the vm-host emulated usb
> hc (host controller), the packet has long been delivered already.
>
> Instead we will simply report the error to the guest os for the
> next packet enqueued by the guest after receiving notification of
> the error from the usb-host.

The protocol is now documented, so that part is out of date. I don't
see any evidence that the bulk of his other concerns have been
addressed, however.


>
>> 2. Do I correctly understand that there are no circumstances where copied
>> code can be left unmodified? Even in the case where the copied code is
>> working, production code, and the changes would be just for style?
>
> I doubt the changes would just be for "style" if you are craming it into
> the kernel tree, as it's a totally different environment from any other
> place this code might have been running in before.

Well, the checkpatch.pl reports were all style (and mostly whitespace);
roughly 3000 of them against 3000 lines of code :-/. I did review the
code, looking for areas where I thought it would badly cram into the
kernel, and I adjusted the few I found (and sent changes upstream).

The ideal, of course, is to not want to copy this code at all. Daniel
makes an alternate point that might lead to that; I'll reply to that thread.

>
>>> Please do the most basic of polite things and fix this up before posting
>>> things.
>>
>> It is often difficult for a newcomer to know what the polite thing is, even
>> after studying FAQs and documentation.
>
> Did you read Documentation/SubmittingPatches?

Yes, and SubmittingDrivers, SubmitChecklist, every link listed on
#kernelnewbies, and the entire lkml FAQ as well.

In hindsight, I think it's mostly a failure of common sense on my part.

The one constructive suggestion I would offer is that the 'RFC PATCH' is
used heavily by the linux kernel community, but I didn't find much
discussion of it in the documentation or FAQs. I think I jumped to some
erroneous conclusions about it's use. I'm willing to try to add a note
on that, if that would be helpful.

>
> Remember you need to make this trivial to review in order to get it
> accepted. You have to do extra work because of this because our limited
> resource is reviewers and maintainers, not developers.

Yes, understood.

Cheers,

Jeremy

[1] https://lists.gnu.org/archive/html/qemu-devel/2010-12/msg00008.html

2015-07-01 16:13:54

by Greg KH

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Wed, Jul 01, 2015 at 10:55:49AM -0500, Jeremy White wrote:
> On 07/01/2015 12:44 AM, Greg KH wrote:
> > On Tue, Jun 30, 2015 at 10:34:25PM -0500, Jeremy White wrote:
> >> 1. Is the basic premise reasonable? Is Hans correct in asserting that an
> >> alternate USB over IP module will be considered?
> >
> > I have no idea, if it fully replaces the usbip functionality, I don't
> > see why that would be rejected. But why can't you just fix up usbip for
> > the issues you find lacking?
>
> This is what Hans said 5 years ago: [1]
>
> >
> > 3) The protocol itself is far from ideal.
> >
> > Number 3 is the big deal breaker for me. I've looked at the
> > (undocumented) protocol by sifting through the source. And it is
> > very low level, all it does is shove usb packets back and forth
> > over the network. It has no concept of configuration
> > setting (the docs say make sure the device is in the right
> > configuration before sharing it). No concept of caching things
> > like descriptors, active configuration, per interface alt setting,
> > etc.
> >
> > Besides missing a lot of useful smarts the whole one packet at a
> > time approach does not really fly when it comes to isoc endpoints.
> > As there paper states, the vm-host / guest os drivers need to
> > make sure enough packets are submitted / queued at all time
> > to gap the network delay / fill the network pipe.
> >
> > For iso endpoints it makes much more sense to have a start / stop
> > stream model, where the usb-host "pumpes" the urb ringbuffer and
> > sends out data received from the usb device to the vm-host
> > (isoc input endp case), or sends data received from the vm-host
> > (through a buffer to deal with network jitter) to the isoc output
> > endpoint.
> >
> > This also halves the number of packets which need to be
> > send over the network, as their is no need for the vm-host to send
> > a request for each packet once an input stream has started / for
> > the usb-host to send an ack for each delivered packet for an ouput
> > stream. It would still send an error when an error occurs, but their
> > is no reason to ack all delivered packets. Given the delay
> > caused by buffering, etc. not being able to match up the error to
> > an exact packet is not important, as from the vm-host emulated usb
> > hc (host controller), the packet has long been delivered already.
> >
> > Instead we will simply report the error to the guest os for the
> > next packet enqueued by the guest after receiving notification of
> > the error from the usb-host.
>
> The protocol is now documented, so that part is out of date. I don't
> see any evidence that the bulk of his other concerns have been
> addressed, however.

Because no one has cared to. Now it seems you care, so I'd prefer to
see someone fix this up instead of adding another protocol that does
much the same thing.

> >> 2. Do I correctly understand that there are no circumstances where copied
> >> code can be left unmodified? Even in the case where the copied code is
> >> working, production code, and the changes would be just for style?
> >
> > I doubt the changes would just be for "style" if you are craming it into
> > the kernel tree, as it's a totally different environment from any other
> > place this code might have been running in before.
>
> Well, the checkpatch.pl reports were all style (and mostly whitespace);
> roughly 3000 of them against 3000 lines of code :-/. I did review the
> code, looking for areas where I thought it would badly cram into the
> kernel, and I adjusted the few I found (and sent changes upstream).

style matters, as it's a thing with your brain. You learn patterns and
if the patterns change, you have to do more work and don't see the real
issues involved. So by ignoring our style you are saying you don't want
anyone else in the kernel community to ever review or work on the code,
which isn't ok.

> >>> Please do the most basic of polite things and fix this up before posting
> >>> things.
> >>
> >> It is often difficult for a newcomer to know what the polite thing is, even
> >> after studying FAQs and documentation.
> >
> > Did you read Documentation/SubmittingPatches?
>
> Yes, and SubmittingDrivers, SubmitChecklist, every link listed on
> #kernelnewbies, and the entire lkml FAQ as well.
>
> In hindsight, I think it's mostly a failure of common sense on my part.
>
> The one constructive suggestion I would offer is that the 'RFC PATCH' is
> used heavily by the linux kernel community, but I didn't find much
> discussion of it in the documentation or FAQs. I think I jumped to some
> erroneous conclusions about it's use. I'm willing to try to add a note
> on that, if that would be helpful.

I personally ignore RFC patches unless they are tiny and in an area that
I happened to be worried about / working on at the moment as it feels
that the submitter doesn't think they are good enough to be merged
as-is, so I'll wait until they feel it is worthy for submission to
review it.

thanks,

greg k-h

2015-07-01 18:32:43

by Jeremy White

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

> Assuming that's correct, then this seems to imply that the socket has raw
> plain text data being sent/received, and thus precludes the possibility
> of running any security protocol like TLS unless the kernel wants to have
> an impl of the TLS protocol.

Good point. For completeness, I'll note that, in a Spice use case, the
data would be encrypted by the normal Spice mechanisms. And it would be
fairly straight forward to write a user space daemon that would accept
TLS and then relay to the unencrypted socket (of course, it would
rewrite everything, which would be inefficient).

>
> I don't really think it is sensible to be defining & implementing new
> network services which can't support strong encryption and authentication.
> Rather than passing the file descriptor to the kernel and having it do
> the I/O directly, I think it would be better to dissassociate the kernel
> from the network transport, and thus leave all sockets layer data I/O
> to userspace daemons so they can layer in TLS or SASL or whatever else
> is appropriate for the security need.

And that would also eliminate the need to copy the parsing code, which
would be a nice improvement.

I considered this approach, but discarded it, perhaps wrongly, when my
google fu suggested that netlink sockets were the best way to connect
user space and a kernel module. (Because I perceived netlink sockets to
be functionally equivalent to the relay daemon described, above).

>From the user space perspective, the usbredir parser has an interface
that exposes about 20 callback functions, which are invoked with
pointers to a variety of structures. The ideal would be to have a
mechanism to 'call into' kernel space with those varying interfaces.

Would using ioctls be a reasonable way to achieve this? Is there a
better way?

In the other direction, the usbredir hc provides a range of functions; I
think most interesting are the urb en/dequeue, hub control, and hub
status calls. Some of that can be handled in the driver; some would
need to be passed on to user space.

My google fu did not lead me to an obvious way to pass this information
to user space. The approach that comes to mind is to use a signal, or
woken socket, to instruct user space to poll.

I'd appreciate further comments and advice.

Cheers,

Jeremy

2015-07-01 18:39:46

by Hans de Goede

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

Hi,

On 01-07-15 18:13, Greg KH wrote:
> On Wed, Jul 01, 2015 at 10:55:49AM -0500, Jeremy White wrote:
>> On 07/01/2015 12:44 AM, Greg KH wrote:
>>> On Tue, Jun 30, 2015 at 10:34:25PM -0500, Jeremy White wrote:
>>>> 1. Is the basic premise reasonable? Is Hans correct in asserting that an
>>>> alternate USB over IP module will be considered?
>>>
>>> I have no idea, if it fully replaces the usbip functionality, I don't
>>> see why that would be rejected. But why can't you just fix up usbip for
>>> the issues you find lacking?
>>
>> This is what Hans said 5 years ago: [1]
>>
>>>
>>> 3) The protocol itself is far from ideal.
>>>
>>> Number 3 is the big deal breaker for me. I've looked at the
>>> (undocumented) protocol by sifting through the source. And it is
>>> very low level, all it does is shove usb packets back and forth
>>> over the network. It has no concept of configuration
>>> setting (the docs say make sure the device is in the right
>>> configuration before sharing it). No concept of caching things
>>> like descriptors, active configuration, per interface alt setting,
>>> etc.
>>>
>>> Besides missing a lot of useful smarts the whole one packet at a
>>> time approach does not really fly when it comes to isoc endpoints.
>>> As there paper states, the vm-host / guest os drivers need to
>>> make sure enough packets are submitted / queued at all time
>>> to gap the network delay / fill the network pipe.
>>>
>>> For iso endpoints it makes much more sense to have a start / stop
>>> stream model, where the usb-host "pumpes" the urb ringbuffer and
>>> sends out data received from the usb device to the vm-host
>>> (isoc input endp case), or sends data received from the vm-host
>>> (through a buffer to deal with network jitter) to the isoc output
>>> endpoint.
>>>
>>> This also halves the number of packets which need to be
>>> send over the network, as their is no need for the vm-host to send
>>> a request for each packet once an input stream has started / for
>>> the usb-host to send an ack for each delivered packet for an ouput
>>> stream. It would still send an error when an error occurs, but their
>>> is no reason to ack all delivered packets. Given the delay
>>> caused by buffering, etc. not being able to match up the error to
>>> an exact packet is not important, as from the vm-host emulated usb
>>> hc (host controller), the packet has long been delivered already.
>>>
>>> Instead we will simply report the error to the guest os for the
>>> next packet enqueued by the guest after receiving notification of
>>> the error from the usb-host.
>>
>> The protocol is now documented, so that part is out of date. I don't
>> see any evidence that the bulk of his other concerns have been
>> addressed, however.
>
> Because no one has cared to. Now it seems you care, so I'd prefer to
> see someone fix this up instead of adding another protocol that does
> much the same thing.

I understand where you are coming from, but usbip is unfixable as it
has no concept of capability negotiation, protocol versioning or some such.

What we need is an usbip v2, and usbredir was written as that, and has been
used in production for years now for redirection of usb devices from
virtual-machine-viewers into qemu based virtual-machines.

I understand that having 2 protocols for one thing is undesirable in
general, but think of this as usb-mass-storage bulk transport vs uas,
or ipv4 vs ipv6 in some cases it just is necessary to do a new
better protocol.

When I designed and implemented usbredir usbip was pretty much dead, but it
got ressurected later. I've never spoken up on this and never attempted to
block usbip's promotion out of staging, as that seemed unfair since no-one
was working on a virtual-hcd driver for the usbredir protocol.

Likewise I think it is unfair if my not speaking up back then now blocks
an usbredir virtual-hcd driver from entering the kernel.

Regards,

Hans

2015-07-01 18:45:11

by Hans de Goede

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

Hi,

On 01-07-15 20:31, Jeremy White wrote:
>> Assuming that's correct, then this seems to imply that the socket has raw
>> plain text data being sent/received, and thus precludes the possibility
>> of running any security protocol like TLS unless the kernel wants to have
>> an impl of the TLS protocol.
>
> Good point. For completeness, I'll note that, in a Spice use case, the
> data would be encrypted by the normal Spice mechanisms. And it would be
> fairly straight forward to write a user space daemon that would accept
> TLS and then relay to the unencrypted socket (of course, it would
> rewrite everything, which would be inefficient).
>
>>
>> I don't really think it is sensible to be defining & implementing new
>> network services which can't support strong encryption and authentication.
>> Rather than passing the file descriptor to the kernel and having it do
>> the I/O directly, I think it would be better to dissassociate the kernel
>> from the network transport, and thus leave all sockets layer data I/O
>> to userspace daemons so they can layer in TLS or SASL or whatever else
>> is appropriate for the security need.
>
> And that would also eliminate the need to copy the parsing code, which
> would be a nice improvement.
>
> I considered this approach, but discarded it, perhaps wrongly, when my
> google fu suggested that netlink sockets were the best way to connect
> user space and a kernel module. (Because I perceived netlink sockets to
> be functionally equivalent to the relay daemon described, above).
>
> From the user space perspective, the usbredir parser has an interface
> that exposes about 20 callback functions, which are invoked with
> pointers to a variety of structures. The ideal would be to have a
> mechanism to 'call into' kernel space with those varying interfaces.
>
> Would using ioctls be a reasonable way to achieve this? Is there a
> better way?
>
> In the other direction, the usbredir hc provides a range of functions; I
> think most interesting are the urb en/dequeue, hub control, and hub
> status calls. Some of that can be handled in the driver; some would
> need to be passed on to user space.
>
> My google fu did not lead me to an obvious way to pass this information
> to user space. The approach that comes to mind is to use a signal, or
> woken socket, to instruct user space to poll.
>
> I'd appreciate further comments and advice.

I think it makes sense to have the actual usbredir protocol parsing
in the kernel, and use a netlink interface, this will make it much
easier to deal with protocol extensions (although we have not had
any extensions to the usbredir proto in a while), and will be much
cleaner then an ioctl interface.

I think that Daniel's concern can easily be fixed by rather then
passing the fd of a socket into the kernel to simply forwarding the
data back and forth from a socket opened by userspace into the netlink
socket. This way SSL, SASL or whatever can be put in between, and
you can even built a nice test-suite this way :)

The downside of this is introducing an extra memcpy of all the data,
but an ioctl interface has the same problem and is going to be
unwieldy, so I advice against that.

As for the extra memcpy I would not worry about that, in all the
performance testing I've done it has almost always been all about
latency not throughput.

Regards,

Hans

2015-07-02 08:46:29

by Oliver Neukum

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:

> I don't really think it is sensible to be defining & implementing new
> network services which can't support strong encryption and authentication.
> Rather than passing the file descriptor to the kernel and having it do
> the I/O directly, I think it would be better to dissassociate the kernel
> from the network transport, and thus leave all sockets layer data I/O
> to userspace daemons so they can layer in TLS or SASL or whatever else
> is appropriate for the security need.

Hi,

this hits a fundamental limit. Block IO must be done entirely in kernel
space or the system will deadlock. The USB stack is part of the block
layer and the SCSI error handling. Thus if you involve user space you
cannot honor memory allocation with GFP_NOFS and you break all APIs
where we pass GFP_NOIO in the USB stack.

Supposed you need to reset a storage device for error handling.
Your user space programm does a syscall, which allocates memory
and needs to launder pages. It proceeds to write to the storage device
you wish to reset.

It is the same problem FUSE has with writable mmap. You cannot do
block devices in user space sanely.

Sorry
Oliver

2015-07-02 11:35:12

by Hans de Goede

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

Hi,

On 02-07-15 10:45, Oliver Neukum wrote:
> On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:
>
>> I don't really think it is sensible to be defining & implementing new
>> network services which can't support strong encryption and authentication.
>> Rather than passing the file descriptor to the kernel and having it do
>> the I/O directly, I think it would be better to dissassociate the kernel
>> from the network transport, and thus leave all sockets layer data I/O
>> to userspace daemons so they can layer in TLS or SASL or whatever else
>> is appropriate for the security need.
>
> Hi,
>
> this hits a fundamental limit. Block IO must be done entirely in kernel
> space or the system will deadlock. The USB stack is part of the block
> layer and the SCSI error handling. Thus if you involve user space you
> cannot honor memory allocation with GFP_NOFS and you break all APIs
> where we pass GFP_NOIO in the USB stack.
>
> Supposed you need to reset a storage device for error handling.
> Your user space programm does a syscall, which allocates memory
> and needs to launder pages. It proceeds to write to the storage device
> you wish to reset.
>
> It is the same problem FUSE has with writable mmap. You cannot do
> block devices in user space sanely.

So how is this dealt with for usbip ?

Regards,

Hans

2015-07-02 12:11:57

by Oliver Neukum

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Thu, 2015-07-02 at 13:35 +0200, Hans de Goede wrote:
> Hi,
>
> On 02-07-15 10:45, Oliver Neukum wrote:
> > On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:
> >
> >> I don't really think it is sensible to be defining & implementing new
> >> network services which can't support strong encryption and authentication.
> >> Rather than passing the file descriptor to the kernel and having it do
> >> the I/O directly, I think it would be better to dissassociate the kernel
> >> from the network transport, and thus leave all sockets layer data I/O
> >> to userspace daemons so they can layer in TLS or SASL or whatever else
> >> is appropriate for the security need.
> >
> > Hi,
> >
> > this hits a fundamental limit. Block IO must be done entirely in kernel
> > space or the system will deadlock. The USB stack is part of the block
> > layer and the SCSI error handling. Thus if you involve user space you
> > cannot honor memory allocation with GFP_NOFS and you break all APIs
> > where we pass GFP_NOIO in the USB stack.
> >
> > Supposed you need to reset a storage device for error handling.
> > Your user space programm does a syscall, which allocates memory
> > and needs to launder pages. It proceeds to write to the storage device
> > you wish to reset.
> >
> > It is the same problem FUSE has with writable mmap. You cannot do
> > block devices in user space sanely.
>
> So how is this dealt with for usbip ?

As far as I can tell, it isn't. Running a storage device over usbip
is a bit dangerous.

Regards
Oliver

2015-07-02 15:57:15

by Jeremy White

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/02/2015 07:10 AM, Oliver Neukum wrote:
> On Thu, 2015-07-02 at 13:35 +0200, Hans de Goede wrote:
>> Hi,
>>
>> On 02-07-15 10:45, Oliver Neukum wrote:
>>> On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:
>>>
>>>> I don't really think it is sensible to be defining & implementing new
>>>> network services which can't support strong encryption and authentication.
>>>> Rather than passing the file descriptor to the kernel and having it do
>>>> the I/O directly, I think it would be better to dissassociate the kernel
>>>> from the network transport, and thus leave all sockets layer data I/O
>>>> to userspace daemons so they can layer in TLS or SASL or whatever else
>>>> is appropriate for the security need.
>>>
>>> Hi,
>>>
>>> this hits a fundamental limit. Block IO must be done entirely in kernel
>>> space or the system will deadlock. The USB stack is part of the block
>>> layer and the SCSI error handling. Thus if you involve user space you
>>> cannot honor memory allocation with GFP_NOFS and you break all APIs
>>> where we pass GFP_NOIO in the USB stack.
>>>
>>> Supposed you need to reset a storage device for error handling.
>>> Your user space programm does a syscall, which allocates memory
>>> and needs to launder pages. It proceeds to write to the storage device
>>> you wish to reset.
>>>
>>> It is the same problem FUSE has with writable mmap. You cannot do
>>> block devices in user space sanely.
>>
>> So how is this dealt with for usbip ?
>
> As far as I can tell, it isn't. Running a storage device over usbip
> is a bit dangerous.

I don't follow that analysis. The usbip interactions with the usb stack
all seem to be atomic, and never trigger a syscall, as far as I can
tell. A port reset will flip a few bits and return. A urb enqueue
queues and wakes a different thread, and returns. The alternate thread
performs the sendmsg.

I'm not suggesting that running a storage device over usbip is
especially safe, but I don't see the limit on the design.

Cheers,

Jeremy

2015-07-02 18:48:15

by Oliver Neukum

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Thu, 2015-07-02 at 10:57 -0500, Jeremy White wrote:
> On 07/02/2015 07:10 AM, Oliver Neukum wrote:
> > On Thu, 2015-07-02 at 13:35 +0200, Hans de Goede wrote:
> >> Hi,
> >>
> >> On 02-07-15 10:45, Oliver Neukum wrote:
> >>> On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:
> >>>
> >>>> I don't really think it is sensible to be defining & implementing new
> >>>> network services which can't support strong encryption and authentication.
> >>>> Rather than passing the file descriptor to the kernel and having it do
> >>>> the I/O directly, I think it would be better to dissassociate the kernel
> >>>> from the network transport, and thus leave all sockets layer data I/O
> >>>> to userspace daemons so they can layer in TLS or SASL or whatever else
> >>>> is appropriate for the security need.
> >>>
> >>> Hi,
> >>>
> >>> this hits a fundamental limit. Block IO must be done entirely in kernel
> >>> space or the system will deadlock. The USB stack is part of the block
> >>> layer and the SCSI error handling. Thus if you involve user space you
> >>> cannot honor memory allocation with GFP_NOFS and you break all APIs
> >>> where we pass GFP_NOIO in the USB stack.
> >>>
> >>> Supposed you need to reset a storage device for error handling.
> >>> Your user space programm does a syscall, which allocates memory
> >>> and needs to launder pages. It proceeds to write to the storage device
> >>> you wish to reset.
> >>>
> >>> It is the same problem FUSE has with writable mmap. You cannot do
> >>> block devices in user space sanely.
> >>
> >> So how is this dealt with for usbip ?
> >
> > As far as I can tell, it isn't. Running a storage device over usbip
> > is a bit dangerous.
>
> I don't follow that analysis. The usbip interactions with the usb stack
> all seem to be atomic, and never trigger a syscall, as far as I can
> tell. A port reset will flip a few bits and return. A urb enqueue
> queues and wakes a different thread, and returns. The alternate thread
> performs the sendmsg.
>
> I'm not suggesting that running a storage device over usbip is
> especially safe, but I don't see the limit on the design.

Are you referring to the current code or the proposed user space pipe?

Regards
Oliver


2015-07-02 19:02:45

by Jeremy White

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/02/2015 01:46 PM, Oliver Neukum wrote:
> On Thu, 2015-07-02 at 10:57 -0500, Jeremy White wrote:
>> On 07/02/2015 07:10 AM, Oliver Neukum wrote:
>>> On Thu, 2015-07-02 at 13:35 +0200, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 02-07-15 10:45, Oliver Neukum wrote:
>>>>> On Wed, 2015-07-01 at 10:06 +0100, Daniel P. Berrange wrote:
>>>>>
>>>>>> I don't really think it is sensible to be defining & implementing new
>>>>>> network services which can't support strong encryption and authentication.
>>>>>> Rather than passing the file descriptor to the kernel and having it do
>>>>>> the I/O directly, I think it would be better to dissassociate the kernel
>>>>>> from the network transport, and thus leave all sockets layer data I/O
>>>>>> to userspace daemons so they can layer in TLS or SASL or whatever else
>>>>>> is appropriate for the security need.
>>>>>
>>>>> Hi,
>>>>>
>>>>> this hits a fundamental limit. Block IO must be done entirely in kernel
>>>>> space or the system will deadlock. The USB stack is part of the block
>>>>> layer and the SCSI error handling. Thus if you involve user space you
>>>>> cannot honor memory allocation with GFP_NOFS and you break all APIs
>>>>> where we pass GFP_NOIO in the USB stack.
>>>>>
>>>>> Supposed you need to reset a storage device for error handling.
>>>>> Your user space programm does a syscall, which allocates memory
>>>>> and needs to launder pages. It proceeds to write to the storage device
>>>>> you wish to reset.
>>>>>
>>>>> It is the same problem FUSE has with writable mmap. You cannot do
>>>>> block devices in user space sanely.
>>>>
>>>> So how is this dealt with for usbip ?
>>>
>>> As far as I can tell, it isn't. Running a storage device over usbip
>>> is a bit dangerous.
>>
>> I don't follow that analysis. The usbip interactions with the usb stack
>> all seem to be atomic, and never trigger a syscall, as far as I can
>> tell. A port reset will flip a few bits and return. A urb enqueue
>> queues and wakes a different thread, and returns. The alternate thread
>> performs the sendmsg.
>>
>> I'm not suggesting that running a storage device over usbip is
>> especially safe, but I don't see the limit on the design.
>
> Are you referring to the current code or the proposed user space pipe?

I'm referring to current usbip code. But the proposed driver would have
the same behavior.

To be clear, I think the only tangible new proposal is the one Hans put
forth, which would modify the driver I originally posted to use a
netlink socket instead of a passing a file descriptor in via sysfs.
That would allow the user space application responsible for initiating
the request to provide TLS as desired. It comes with the expense of an
extra memcpy, but I suspect Hans is right in saying the network
latencies make that an irrelevant cost.

Cheers,

Jeremy

2015-07-02 20:00:06

by Alan Stern

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Thu, 2 Jul 2015, Jeremy White wrote:

> >> I don't follow that analysis. The usbip interactions with the usb stack
> >> all seem to be atomic, and never trigger a syscall, as far as I can
> >> tell. A port reset will flip a few bits and return. A urb enqueue
> >> queues and wakes a different thread, and returns. The alternate thread
> >> performs the sendmsg.
> >>
> >> I'm not suggesting that running a storage device over usbip is
> >> especially safe, but I don't see the limit on the design.
> >
> > Are you referring to the current code or the proposed user space pipe?
>
> I'm referring to current usbip code. But the proposed driver would have
> the same behavior.
>
> To be clear, I think the only tangible new proposal is the one Hans put
> forth, which would modify the driver I originally posted to use a
> netlink socket instead of a passing a file descriptor in via sysfs.
> That would allow the user space application responsible for initiating
> the request to provide TLS as desired. It comes with the expense of an
> extra memcpy, but I suspect Hans is right in saying the network
> latencies make that an irrelevant cost.

Oliver is talking about the danger of having part of the communication
path for a block device run through userspace.

Imagine a situation where the client uses a USB storage device provided
by the server as a swap device. And suppose a userspace daemon on the
client has to process USB packets as they pass between the client and
the server. If the daemon is idle for some time, parts of its address
space may get stored in the swap area on the server and paged out.

Now consider what happens when those parts of memory need to be paged
back in. The client submits a request to read from the swap area.
The request is transformed into USB packets and sent through the
userspace daemon for transmission to the server. But the daemon can't
process the packets because it is waiting for its missing parts to be
paged back! Result: deadlock.

Alan Stern

2015-07-02 20:06:53

by Jeremy White

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/02/2015 02:59 PM, Alan Stern wrote:
> On Thu, 2 Jul 2015, Jeremy White wrote:
>
>>>> I don't follow that analysis. The usbip interactions with the usb stack
>>>> all seem to be atomic, and never trigger a syscall, as far as I can
>>>> tell. A port reset will flip a few bits and return. A urb enqueue
>>>> queues and wakes a different thread, and returns. The alternate thread
>>>> performs the sendmsg.
>>>>
>>>> I'm not suggesting that running a storage device over usbip is
>>>> especially safe, but I don't see the limit on the design.
>>>
>>> Are you referring to the current code or the proposed user space pipe?
>>
>> I'm referring to current usbip code. But the proposed driver would have
>> the same behavior.
>>
>> To be clear, I think the only tangible new proposal is the one Hans put
>> forth, which would modify the driver I originally posted to use a
>> netlink socket instead of a passing a file descriptor in via sysfs.
>> That would allow the user space application responsible for initiating
>> the request to provide TLS as desired. It comes with the expense of an
>> extra memcpy, but I suspect Hans is right in saying the network
>> latencies make that an irrelevant cost.
>
> Oliver is talking about the danger of having part of the communication
> path for a block device run through userspace.
>
> Imagine a situation where the client uses a USB storage device provided
> by the server as a swap device. And suppose a userspace daemon on the
> client has to process USB packets as they pass between the client and
> the server. If the daemon is idle for some time, parts of its address
> space may get stored in the swap area on the server and paged out.
>
> Now consider what happens when those parts of memory need to be paged
> back in. The client submits a request to read from the swap area.
> The request is transformed into USB packets and sent through the
> userspace daemon for transmission to the server. But the daemon can't
> process the packets because it is waiting for its missing parts to be
> paged back! Result: deadlock.

Right. I followed that. Oliver also asserted that he believed that the
current usbip implementation has this flaw; I do not follow that. The
concept is that the usbip device driver virtualizes the device behavior;
isolating the running kernel from the vagaries of the network transport.
All proposed usbredir implementations, even if they move the network
transport to user space, would retain that behavior.

Cheers,

Jeremy

2015-07-02 20:20:49

by Alan Stern

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Thu, 2 Jul 2015, Jeremy White wrote:

> > Oliver is talking about the danger of having part of the communication
> > path for a block device run through userspace.
> >
> > Imagine a situation where the client uses a USB storage device provided
> > by the server as a swap device. And suppose a userspace daemon on the
> > client has to process USB packets as they pass between the client and
> > the server. If the daemon is idle for some time, parts of its address
> > space may get stored in the swap area on the server and paged out.
> >
> > Now consider what happens when those parts of memory need to be paged
> > back in. The client submits a request to read from the swap area.
> > The request is transformed into USB packets and sent through the
> > userspace daemon for transmission to the server. But the daemon can't
> > process the packets because it is waiting for its missing parts to be
> > paged back! Result: deadlock.
>
> Right. I followed that. Oliver also asserted that he believed that the
> current usbip implementation has this flaw; I do not follow that. The
> concept is that the usbip device driver virtualizes the device behavior;
> isolating the running kernel from the vagaries of the network transport.
> All proposed usbredir implementations, even if they move the network
> transport to user space, would retain that behavior.

The point is that a device driver like usbip _cannot_ isolate the
running kernel from the vagaries of the network transport if part of
that transport occurs in userspace.

If any part of the transport passes through userspace, you can end up
in a situation like what I outlined above, where a message can't be
transported until after its reply has been received. There's no way
for a device driver to prevent a deadlock when this occurs, no matter
what it virtualizes.

Alan Stern

2015-07-03 08:51:26

by Krzysztof Opasiak

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.



On 07/02/2015 10:20 PM, Alan Stern wrote:
> On Thu, 2 Jul 2015, Jeremy White wrote:
>
>>> Oliver is talking about the danger of having part of the communication
>>> path for a block device run through userspace.
>>>
>>> Imagine a situation where the client uses a USB storage device provided
>>> by the server as a swap device. And suppose a userspace daemon on the
>>> client has to process USB packets as they pass between the client and
>>> the server. If the daemon is idle for some time, parts of its address
>>> space may get stored in the swap area on the server and paged out.
>>>
>>> Now consider what happens when those parts of memory need to be paged
>>> back in. The client submits a request to read from the swap area.
>>> The request is transformed into USB packets and sent through the
>>> userspace daemon for transmission to the server. But the daemon can't
>>> process the packets because it is waiting for its missing parts to be
>>> paged back! Result: deadlock.
>>
>> Right. I followed that. Oliver also asserted that he believed that the
>> current usbip implementation has this flaw; I do not follow that. The
>> concept is that the usbip device driver virtualizes the device behavior;
>> isolating the running kernel from the vagaries of the network transport.
>> All proposed usbredir implementations, even if they move the network
>> transport to user space, would retain that behavior.
>
> The point is that a device driver like usbip _cannot_ isolate the
> running kernel from the vagaries of the network transport if part of
> that transport occurs in userspace.
>
> If any part of the transport passes through userspace, you can end up
> in a situation like what I outlined above, where a message can't be
> transported until after its reply has been received. There's no way
> for a device driver to prevent a deadlock when this occurs, no matter
> what it virtualizes.
>

Doesn't we have the same problem with functionfs/gadgetfs and dummy_hcd?
Or with fuse?

It's a very generic problem for all "virtualized devices" and it is
known for quite a long time. This is why many tutorials about swap warns
that swap should be set up only on real block devices which are fully
served in kernel.

--
Krzysztof Opasiak
Samsung R&D Institute Poland
Samsung Electronics

2015-07-03 14:05:01

by Alan Stern

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Fri, 3 Jul 2015, Krzysztof Opasiak wrote:

> > The point is that a device driver like usbip _cannot_ isolate the
> > running kernel from the vagaries of the network transport if part of
> > that transport occurs in userspace.
> >
> > If any part of the transport passes through userspace, you can end up
> > in a situation like what I outlined above, where a message can't be
> > transported until after its reply has been received. There's no way
> > for a device driver to prevent a deadlock when this occurs, no matter
> > what it virtualizes.
> >
>
> Doesn't we have the same problem with functionfs/gadgetfs and dummy_hcd?
> Or with fuse?

Yes indeed.

> It's a very generic problem for all "virtualized devices" and it is
> known for quite a long time. This is why many tutorials about swap warns
> that swap should be set up only on real block devices which are fully
> served in kernel.

That is good advice. :-)

Alan Stern

2015-07-06 08:21:27

by Oliver Neukum

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Fri, 2015-07-03 at 10:51 +0200, Krzysztof Opasiak wrote:
> Doesn't we have the same problem with functionfs/gadgetfs and
> dummy_hcd?
> Or with fuse?
>
> It's a very generic problem for all "virtualized devices" and it is
> known for quite a long time. This is why many tutorials about swap
> warns
> that swap should be set up only on real block devices which are fully
> served in kernel.

Indeed. But the point is that it isn't limited to swap. As you as
a page needs to be laundered the problem exists.
Mmapping a file for write (non private) is enough.

Regards
Oliver

2015-07-06 20:15:07

by Jeremy White

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/06/2015 03:20 AM, Oliver Neukum wrote:
> On Fri, 2015-07-03 at 10:51 +0200, Krzysztof Opasiak wrote:
>> Doesn't we have the same problem with functionfs/gadgetfs and
>> dummy_hcd?
>> Or with fuse?
>>
>> It's a very generic problem for all "virtualized devices" and it is
>> known for quite a long time. This is why many tutorials about swap
>> warns
>> that swap should be set up only on real block devices which are fully
>> served in kernel.
>
> Indeed. But the point is that it isn't limited to swap. As you as
> a page needs to be laundered the problem exists.
> Mmapping a file for write (non private) is enough.

I'm persuaded to avoid user space in the core design.

Anything else fundamental to usbip that should inform the design of a
usbredir driver? usbip appears to be based off a 2004 vintage of
dummy_hcd. I'll look thoughtfully at the current dummy_hcd; please let
me know if there is anything else I should consider.

Cheers,

Jeremy

2015-07-06 20:22:43

by Alan Stern

[permalink] [raw]
Subject: Re: [Spice-devel] [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Mon, 6 Jul 2015, Jeremy White wrote:

> Anything else fundamental to usbip that should inform the design of a
> usbredir driver? usbip appears to be based off a 2004 vintage of
> dummy_hcd. I'll look thoughtfully at the current dummy_hcd; please let
> me know if there is anything else I should consider.

One thing that springs to mind is USB-3 streams. When dummy-hcd was
expanded to include USB-3, that was the major new ingredient.

Alan Stern

2015-07-07 16:47:05

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.


>>
>> Well, the checkpatch.pl reports were all style (and mostly whitespace);
>> roughly 3000 of them against 3000 lines of code :-/. I did review the
>> code, looking for areas where I thought it would badly cram into the
>> kernel, and I adjusted the few I found (and sent changes upstream).
>
> style matters, as it's a thing with your brain. You learn patterns and
> if the patterns change, you have to do more work and don't see the real
> issues involved. So by ignoring our style you are saying you don't want
> anyone else in the kernel community to ever review or work on the code,
> which isn't ok.

Looks like I can't side step this unless Hans is willing to shift the
usbredir project entirely to using kernel style :-/.

I will plan to make changes so that checkpatch runs clean; I lay out my
concerns and my plan below to make sure I'm taking the best path.

My main concern with changing the ~2,500 lines of code from the upstream
usbredir project is that it will increase the odds that I will introduce
errors, both initially, and again later as I review and attempt to relay
patches from the upstream.

To summarize the checkpatch reports: the biggest issue is whitespace,
which shouldn't be a problem; I should be able to automate that without
error. There are also a fair number of one offs; FSF address, space
after '!', etc. I hope to persuade Hans to take a few style only
patches upstream to address those. That leaves a pack of about 60 brace
placement and line length issues.

I will plan to manually change those prior to submission. Any upstream
changes that affect the same code will be manually corrected as well,
prior to submission.

Make sense?

Cheers,

Jeremy

2015-07-08 07:12:18

by Hans de Goede

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

Hi,

On 07-07-15 18:47, Jeremy White wrote:
>
>>>
>>> Well, the checkpatch.pl reports were all style (and mostly whitespace);
>>> roughly 3000 of them against 3000 lines of code :-/. I did review the
>>> code, looking for areas where I thought it would badly cram into the
>>> kernel, and I adjusted the few I found (and sent changes upstream).
>>
>> style matters, as it's a thing with your brain. You learn patterns and
>> if the patterns change, you have to do more work and don't see the real
>> issues involved. So by ignoring our style you are saying you don't want
>> anyone else in the kernel community to ever review or work on the code,
>> which isn't ok.
>
> Looks like I can't side step this unless Hans is willing to shift the
> usbredir project entirely to using kernel style :-/.

I'm fine with moving the usbredir project to the kernel style, the question
is how to do this without causing any hidden breakage.

Can you create a gnu-indent invocation which will do most of the work?

And then a hopefully managable sized patch on top to fix the remaining
style errors in usbredirparser ?

> I will plan to make changes so that checkpatch runs clean; I lay out my
> concerns and my plan below to make sure I'm taking the best path.
>
> My main concern with changing the ~2,500 lines of code from the upstream
> usbredir project is that it will increase the odds that I will introduce
> errors, both initially, and again later as I review and attempt to relay
> patches from the upstream.
>
> To summarize the checkpatch reports: the biggest issue is whitespace,
> which shouldn't be a problem; I should be able to automate that without
> error. There are also a fair number of one offs; FSF address, space
> after '!', etc. I hope to persuade Hans to take a few style only
> patches upstream to address those. That leaves a pack of about 60 brace
> placement and line length issues.
>
> I will plan to manually change those prior to submission. Any upstream
> changes that affect the same code will be manually corrected as well,
> prior to submission.
>
> Make sense?

Sounds good, note that as said I'm fine with moving over the usbredir(parser)
code to the kernel style, as long as the changes are reviewable.

I think it may be best to only convert the usbredirdparser files, as those
are the only ones you need for the kernel. Having a mixed style in usbredir
is not ideal, but something I can live with.

Regards,

Hans

2015-07-09 00:22:08

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/08/2015 02:11 AM, Hans de Goede wrote:
> Hi,
>
> On 07-07-15 18:47, Jeremy White wrote:
>>
>>>>
>>>> Well, the checkpatch.pl reports were all style (and mostly whitespace);
>>>> roughly 3000 of them against 3000 lines of code :-/. I did review the
>>>> code, looking for areas where I thought it would badly cram into the
>>>> kernel, and I adjusted the few I found (and sent changes upstream).
>>>
>>> style matters, as it's a thing with your brain. You learn patterns and
>>> if the patterns change, you have to do more work and don't see the real
>>> issues involved. So by ignoring our style you are saying you don't want
>>> anyone else in the kernel community to ever review or work on the code,
>>> which isn't ok.
>>
>> Looks like I can't side step this unless Hans is willing to shift the
>> usbredir project entirely to using kernel style :-/.
>
> I'm fine with moving the usbredir project to the kernel style, the question
> is how to do this without causing any hidden breakage.
>
> Can you create a gnu-indent invocation which will do most of the work?
>
> And then a hopefully managable sized patch on top to fix the remaining
> style errors in usbredirparser ?

Got it; that's a helpful surprise. I'll work on patches for all but the
whitespace, for only the files needed. I'm hoping whitespace changes
can be limited to a gnu-indent invocation at patch transfer time. I'd
hate to destroy the history like that :-/.

Cheers,

Jeremy

2015-07-22 14:06:12

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/09/2015 05:06 AM, Alex Elsayed wrote:
> Alan Stern wrote:
>
>> On Mon, 6 Jul 2015, Jeremy White wrote:
>>
>>> Anything else fundamental to usbip that should inform the design of a
>>> usbredir driver? usbip appears to be based off a 2004 vintage of
>>> dummy_hcd. I'll look thoughtfully at the current dummy_hcd; please let
>>> me know if there is anything else I should consider.
>>
>> One thing that springs to mind is USB-3 streams. When dummy-hcd was
>> expanded to include USB-3, that was the major new ingredient.
>
> Another thing that comes to mind is that the USB-IF has its own official
> standard for this kind of thing now, called Media-Agnostic USB[1]. In
> November of 2014 a driver[2] was posted, followed by a second version[3],
> and it is apparently being refined inside Intel[4].
>
> [1]
> http://www.usb.org/developers/docs/devclass_docs/Media_Agnostic_USB_v1.0.zip
> [2] http://thread.gmane.org/gmane.linux.kernel/1820297
> [3] http://thread.gmane.org/gmane.linux.drivers.driver-project.devel/60498
> [4] http://article.gmane.org/gmane.linux.drivers.driver-project.devel/60757

Thanks for the pointer, Alex.

I spent some time with the spec and their proposed code. It does seem
plausible that XSpice could use a mausb/usbredir protocol converter. So
if there was a mausb kernel module, I could potentially implement
support in XSpice in user space and not need a usbredir module.

I sent an email to the two developers at Intel to ask if there had been
any further progress and if I could collaborate with them. I have not
heard back.

The MA spec is substantial and seems well thought out. But the usbredir
protocol has the virtue of being relatively mature - it's 5 years old,
with code in daily use.

At this point it seems the best path forward is to continue work on the
usbredir kernel module, which I will do unless I get some new information.

Cheers,

Jeremy

2015-07-22 14:34:12

by Greg KH

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Wed, Jul 22, 2015 at 09:03:53AM -0500, Jeremy White wrote:
> On 07/09/2015 05:06 AM, Alex Elsayed wrote:
> >Alan Stern wrote:
> >
> >>On Mon, 6 Jul 2015, Jeremy White wrote:
> >>
> >>>Anything else fundamental to usbip that should inform the design of a
> >>>usbredir driver? usbip appears to be based off a 2004 vintage of
> >>>dummy_hcd. I'll look thoughtfully at the current dummy_hcd; please let
> >>>me know if there is anything else I should consider.
> >>
> >>One thing that springs to mind is USB-3 streams. When dummy-hcd was
> >>expanded to include USB-3, that was the major new ingredient.
> >
> >Another thing that comes to mind is that the USB-IF has its own official
> >standard for this kind of thing now, called Media-Agnostic USB[1]. In
> >November of 2014 a driver[2] was posted, followed by a second version[3],
> >and it is apparently being refined inside Intel[4].
> >
> >[1]
> >http://www.usb.org/developers/docs/devclass_docs/Media_Agnostic_USB_v1.0.zip
> >[2] http://thread.gmane.org/gmane.linux.kernel/1820297
> >[3] http://thread.gmane.org/gmane.linux.drivers.driver-project.devel/60498
> >[4] http://article.gmane.org/gmane.linux.drivers.driver-project.devel/60757
>
> Thanks for the pointer, Alex.
>
> I spent some time with the spec and their proposed code. It does seem
> plausible that XSpice could use a mausb/usbredir protocol converter. So if
> there was a mausb kernel module, I could potentially implement support in
> XSpice in user space and not need a usbredir module.
>
> I sent an email to the two developers at Intel to ask if there had been any
> further progress and if I could collaborate with them. I have not heard
> back.
>
> The MA spec is substantial and seems well thought out. But the usbredir
> protocol has the virtue of being relatively mature - it's 5 years old, with
> code in daily use.
>
> At this point it seems the best path forward is to continue work on the
> usbredir kernel module, which I will do unless I get some new information.

Please work with the existing people, or with the existing spec, I don't
want to be adding multiple versions of this type of protocol to the
kernel. As it is, I really don't even want to take your code, given
that usbip is already there. Ignoring it isn't ok.

thanks,

greg k-h

2015-07-22 16:55:37

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/22/2015 09:34 AM, Greg KH wrote:
> On Wed, Jul 22, 2015 at 09:03:53AM -0500, Jeremy White wrote:
>> On 07/09/2015 05:06 AM, Alex Elsayed wrote:
>>> Alan Stern wrote:
>>>
>>>> On Mon, 6 Jul 2015, Jeremy White wrote:
>>>>
>>>>> Anything else fundamental to usbip that should inform the design of a
>>>>> usbredir driver? usbip appears to be based off a 2004 vintage of
>>>>> dummy_hcd. I'll look thoughtfully at the current dummy_hcd; please let
>>>>> me know if there is anything else I should consider.
>>>>
>>>> One thing that springs to mind is USB-3 streams. When dummy-hcd was
>>>> expanded to include USB-3, that was the major new ingredient.
>>>
>>> Another thing that comes to mind is that the USB-IF has its own official
>>> standard for this kind of thing now, called Media-Agnostic USB[1]. In
>>> November of 2014 a driver[2] was posted, followed by a second version[3],
>>> and it is apparently being refined inside Intel[4].
>>>
>>> [1]
>>> http://www.usb.org/developers/docs/devclass_docs/Media_Agnostic_USB_v1.0.zip
>>> [2] http://thread.gmane.org/gmane.linux.kernel/1820297
>>> [3] http://thread.gmane.org/gmane.linux.drivers.driver-project.devel/60498
>>> [4] http://article.gmane.org/gmane.linux.drivers.driver-project.devel/60757
>>
>> Thanks for the pointer, Alex.
>>
>> I spent some time with the spec and their proposed code. It does seem
>> plausible that XSpice could use a mausb/usbredir protocol converter. So if
>> there was a mausb kernel module, I could potentially implement support in
>> XSpice in user space and not need a usbredir module.
>>
>> I sent an email to the two developers at Intel to ask if there had been any
>> further progress and if I could collaborate with them. I have not heard
>> back.
>>
>> The MA spec is substantial and seems well thought out. But the usbredir
>> protocol has the virtue of being relatively mature - it's 5 years old, with
>> code in daily use.
>>
>> At this point it seems the best path forward is to continue work on the
>> usbredir kernel module, which I will do unless I get some new information.
>
> Please work with the existing people, or with the existing spec, I don't
> want to be adding multiple versions of this type of protocol to the
> kernel. As it is, I really don't even want to take your code, given
> that usbip is already there. Ignoring it isn't ok.

The usbredir spec predates MA-USB by 4 years; it has greater claim to
the title 'existing' than does MA-USB. I recognize that does not make
it better, and I recognize the value of a spec from a standards body.
But I also respect community standards in production use.

And I did not and am not ignoring the MA-USB patch and spec.

I privately wrote to the Intel authors of that patch a week ago; I've
publicly included them in this thread as well. As far as I can tell,
they've been silent on this front since November; I fear that they may
have moved on, or that Intel is not actively working on this. None of
the Intel authors listed on the MA-USB specification are kernel
contributors, so I did not have a way to reach out to them. If you have
the means to engage others, I would appreciate that.

With no other input, my analysis was that it is better to proceed with
the existing spec. It has a body of useful code, active users and
developers, and I am certain it will solve my problem.

Also, as for usbip, I'll point out that the existence of MA-USB
corroborates Hans rationale for the need to supplant usbip.

As I said, I will respond to any new information I receive. It would be
great to have a kernel module developed (or at least approved) by
seasoned hands at Intel.

But how long should I wait?

Cheers,

Jeremy

2015-07-22 18:04:40

by Sean O. Stalley

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On Wed, Jul 22, 2015 at 11:55:32AM -0500, Jeremy White wrote:
> I privately wrote to the Intel authors of that patch a week ago; I've
> publicly included them in this thread as well. As far as I can tell,
> they've been silent on this front since November; I fear that they may
> have moved on, or that Intel is not actively working on this. None of
> the Intel authors listed on the MA-USB specification are kernel
> contributors, so I did not have a way to reach out to them. If you have
> the means to engage others, I would appreciate that.

Sorry for the delay. The short answer is: Yes, we have been actively working on this driver.
Per Greg KH's request, we have been cleaning up the driver internally.
There was a lot to clean up, which is why we have been silent on LKML.

-Sean

2015-07-23 00:22:49

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

On 07/22/2015 12:59 PM, Sean O. Stalley wrote:
> On Wed, Jul 22, 2015 at 11:55:32AM -0500, Jeremy White wrote:
>> I privately wrote to the Intel authors of that patch a week ago; I've
>> publicly included them in this thread as well. As far as I can tell,
>> they've been silent on this front since November; I fear that they may
>> have moved on, or that Intel is not actively working on this. None of
>> the Intel authors listed on the MA-USB specification are kernel
>> contributors, so I did not have a way to reach out to them. If you have
>> the means to engage others, I would appreciate that.
>
> Sorry for the delay. The short answer is: Yes, we have been actively working on this driver.
> Per Greg KH's request, we have been cleaning up the driver internally.
> There was a lot to clean up, which is why we have been silent on LKML.

Great, thanks. I would appreciate a chance to work with you to make
sure it will work well for XSpice. I'm happy to help as much as I can,
and please don't feel the need to wait for a final version before
reaching out to me.

Cheers,

Jeremy

2015-12-09 22:49:46

by Jeremy White

[permalink] [raw]
Subject: Re: [RFC PATCH 1/1] Add a usbredir kernel module to remotely connect USB devices over IP.

I've parked some working code, and I wanted to leave a pointer to it to
end this thread.

That is, the sense was that usbredir was not appropriate for the linux
kernel, because Intel was working on a driver implementing the Media
Agnostic USB standard, and having a proliferation of drivers didn't make
sense.

Stephanie and Sean of Intel tell me that they continue to progress on
the ma-usb driver, so that all seems reasonable to me.

However, due to time and customer pressure, I made the usbredir kernel
module, and a set of associated utilities, work reasonably well for a
small set of use cases.

I've submitted that as an out of tree module build to the spice-devel
mailing list. [1]

It works reasonably well, and may benefit someone else that cannot wait
for ma-usb.

I felt it would be a courtesy to end this thread with this pointer;
forgive me if it's unwanted noise.

Cheers,

Jeremy

[1]
http://lists.freedesktop.org/archives/spice-devel/2015-December/024779.html