All of lore.kernel.org
 help / color / mirror / Atom feed
From: greg@kroah.com
To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Brian Merrell <bgmerrell@novell.com>,
	Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>,
	gregkh@suse.de
Subject: [patch 03/03] USB: USB/IP: add host driver
Date: Thu, 28 Aug 2008 16:00:38 -0700	[thread overview]
Message-ID: <20080828230038.GD15014@kroah.com> (raw)
In-Reply-To: <20080828230016.GA15014@kroah.com>

[-- Attachment #1: usb-usb-ip-add-host-driver.patch --]
[-- Type: text/plain, Size: 49758 bytes --]

From: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>

This adds the USB IP client driver

Brian Merrell cleaned up a lot of this code and submitted it for
inclusion.  Greg also did a lot of cleanup.

Signed-off-by: Brian G. Merrell <bgmerrell@novell.com>
Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/usb/ip/Kconfig     |   10 
 drivers/usb/ip/Makefile    |    3 
 drivers/usb/ip/stub.h      |   95 ++++++
 drivers/usb/ip/stub_dev.c  |  483 +++++++++++++++++++++++++++++++++++
 drivers/usb/ip/stub_main.c |  300 +++++++++++++++++++++
 drivers/usb/ip/stub_rx.c   |  616 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/ip/stub_tx.c   |  371 +++++++++++++++++++++++++++
 7 files changed, 1878 insertions(+)

--- a/drivers/usb/ip/Kconfig
+++ b/drivers/usb/ip/Kconfig
@@ -22,3 +22,13 @@ config USB_IP_VHCI_HCD
 
 	 To compile this driver as a module, choose M here: the
 	 module will be called vhci_hcd.
+
+config USB_IP_HOST
+	tristate "USB IP host driver"
+	depends on USB_IP_COMMON
+	---help---
+	 This enables the USB IP device driver which will run on the
+	 host machine.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called usbip.
--- a/drivers/usb/ip/Makefile
+++ b/drivers/usb/ip/Makefile
@@ -4,6 +4,9 @@ usbip_common_mod-objs := usbip_common.o 
 obj-$(CONFIG_USB_IP_VHCI_HCD) += vhci-hcd.o
 vhci-hcd-objs := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o
 
+obj-$(CONFIG_USB_IP_HOST) += usbip.o
+usbip-objs := stub_dev.o stub_main.o stub_rx.o stub_tx.o
+
 ifeq ($(CONFIG_USB_DEBUG),y)
 	EXTRA_CFLAGS += -DDEBUG
 endif
--- /dev/null
+++ b/drivers/usb/ip/stub_dev.c
@@ -0,0 +1,483 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "usbip_common.h"
+#include "stub.h"
+
+
+
+static int stub_probe(struct usb_interface *interface,
+				const struct usb_device_id *id);
+static void stub_disconnect(struct usb_interface *interface);
+
+
+/*
+ * Define device IDs here if you want to explicitly limit exportable devices.
+ * In the most cases, wild card matching will be ok because driver binding can
+ * be changed dynamically by a userland program.
+ */
+static struct usb_device_id stub_table[] = {
+#if 0
+	/* just an example */
+	{ USB_DEVICE(0x05ac, 0x0301) },   /* Mac 1 button mouse */
+	{ USB_DEVICE(0x0430, 0x0009) },   /* Plat Home Keyboard */
+	{ USB_DEVICE(0x059b, 0x0001) },   /* Iomega USB Zip 100 */
+	{ USB_DEVICE(0x04b3, 0x4427) },   /* IBM USB CD-ROM */
+	{ USB_DEVICE(0x05a9, 0xa511) },   /* LifeView USB cam */
+	{ USB_DEVICE(0x55aa, 0x0201) },   /* Imation card reader */
+	{ USB_DEVICE(0x046d, 0x0870) },   /* Qcam Express(QV-30) */
+	{ USB_DEVICE(0x04bb, 0x0101) },   /* IO-DATA HD 120GB */
+	{ USB_DEVICE(0x04bb, 0x0904) },   /* IO-DATA USB-ET/TX */
+	{ USB_DEVICE(0x04bb, 0x0201) },   /* IO-DATA USB-ET/TX */
+	{ USB_DEVICE(0x08bb, 0x2702) },   /* ONKYO USB Speaker */
+	{ USB_DEVICE(0x046d, 0x08b2) },   /* Logicool Qcam 4000 Pro */
+#endif
+	/* magic for wild card */
+	{ .driver_info = 1 },
+	{ 0, }                                     /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, stub_table);
+
+struct usb_driver stub_driver = {
+	.name		= "usbip",
+	.probe		= stub_probe,
+	.disconnect	= stub_disconnect,
+	.id_table	= stub_table,
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Define sysfs entries for a usbip-bound device */
+
+
+/*
+ * usbip_status shows status of usbip as long as this driver is bound to the
+ * target device.
+ */
+static ssize_t show_status(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int status;
+
+	if (!sdev) {
+		dev_err(dev, "sdev is null\n");
+		return -ENODEV;
+	}
+
+	spin_lock(&sdev->ud.lock);
+	status = sdev->ud.status;
+	spin_unlock(&sdev->ud.lock);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+static DEVICE_ATTR(usbip_status, S_IRUGO, show_status, NULL);
+
+/*
+ * usbip_sockfd gets a socket descriptor of an established TCP connection that
+ * is used to transfer usbip requests by kernel threads. -1 is a magic number
+ * by which usbip connection is finished.
+ */
+static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	struct stub_device *sdev = dev_get_drvdata(dev);
+	int sockfd = 0;
+	struct socket *socket;
+
+	if (!sdev) {
+		dev_err(dev, "sdev is null\n");
+		return -ENODEV;
+	}
+
+	sscanf(buf, "%d", &sockfd);
+
+	if (sockfd != -1) {
+		dev_info(dev, "stub up\n");
+
+		spin_lock(&sdev->ud.lock);
+
+		if (sdev->ud.status != SDEV_ST_AVAILABLE) {
+			dev_err(dev, "not ready\n");
+			spin_unlock(&sdev->ud.lock);
+			return -EINVAL;
+		}
+
+		socket = sockfd_to_socket(sockfd);
+		if (!socket) {
+			spin_unlock(&sdev->ud.lock);
+			return -EINVAL;
+		}
+
+#if 0
+		setnodelay(socket);
+		setkeepalive(socket);
+		setreuse(socket);
+#endif
+
+		sdev->ud.tcp_socket = socket;
+
+		spin_unlock(&sdev->ud.lock);
+
+		usbip_start_threads(&sdev->ud);
+
+		spin_lock(&sdev->ud.lock);
+		sdev->ud.status = SDEV_ST_USED;
+		spin_unlock(&sdev->ud.lock);
+
+	} else {
+		dev_info(dev, "stub down\n");
+
+		spin_lock(&sdev->ud.lock);
+		if (sdev->ud.status != SDEV_ST_USED) {
+			spin_unlock(&sdev->ud.lock);
+			return -EINVAL;
+		}
+		spin_unlock(&sdev->ud.lock);
+
+		usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
+	}
+
+	return count;
+}
+static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd);
+
+static int stub_add_files(struct device *dev)
+{
+	int err = 0;
+
+	err = device_create_file(dev, &dev_attr_usbip_status);
+	if (err)
+		goto err_status;
+
+	err = device_create_file(dev, &dev_attr_usbip_sockfd);
+	if (err)
+		goto err_sockfd;
+
+	err = device_create_file(dev, &dev_attr_usbip_debug);
+	if (err)
+		goto err_debug;
+
+	return 0;
+
+err_debug:
+	device_remove_file(dev, &dev_attr_usbip_sockfd);
+
+err_sockfd:
+	device_remove_file(dev, &dev_attr_usbip_status);
+
+err_status:
+	return err;
+}
+
+static void stub_remove_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_usbip_status);
+	device_remove_file(dev, &dev_attr_usbip_sockfd);
+	device_remove_file(dev, &dev_attr_usbip_debug);
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Event handler functions called by an event handler thread */
+
+static void stub_shutdown_connection(struct usbip_device *ud)
+{
+	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+
+	/*
+	 * When removing an exported device, kernel panic sometimes occurred
+	 * and then EIP was sk_wait_data of stub_rx thread. Is this because
+	 * sk_wait_data returned though stub_rx thread was already finished by
+	 * step 1?
+	 */
+	if (ud->tcp_socket) {
+		udbg("shutdown tcp_socket %p\n", ud->tcp_socket);
+		kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
+	}
+
+	/* 1. stop threads */
+	usbip_stop_threads(ud);
+
+	/* 2. close the socket */
+	/*
+	 * tcp_socket is freed after threads are killed.
+	 * So usbip_xmit do not touch NULL socket.
+	 */
+	if (ud->tcp_socket) {
+		sock_release(ud->tcp_socket);
+		ud->tcp_socket = NULL;
+	}
+
+	/* 3. free used data */
+	stub_device_cleanup_urbs(sdev);
+
+	/* 4. free stub_unlink */
+	{
+		unsigned long flags;
+		struct stub_unlink *unlink, *tmp;
+
+		spin_lock_irqsave(&sdev->priv_lock, flags);
+
+		list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
+			list_del(&unlink->list);
+			kfree(unlink);
+		}
+
+		list_for_each_entry_safe(unlink, tmp,
+						 &sdev->unlink_free, list) {
+			list_del(&unlink->list);
+			kfree(unlink);
+		}
+
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+	}
+}
+
+static void stub_device_reset(struct usbip_device *ud)
+{
+	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+	struct usb_device *udev = interface_to_usbdev(sdev->interface);
+	int ret;
+
+	udbg("device reset");
+	ret = usb_lock_device_for_reset(udev, sdev->interface);
+	if (ret < 0) {
+		dev_err(&udev->dev, "lock for reset\n");
+
+		spin_lock(&ud->lock);
+		ud->status = SDEV_ST_ERROR;
+		spin_unlock(&ud->lock);
+
+		return;
+	}
+
+	/* try to reset the device */
+	ret = usb_reset_device(udev);
+
+	usb_unlock_device(udev);
+
+	spin_lock(&ud->lock);
+	if (ret) {
+		dev_err(&udev->dev, "device reset\n");
+		ud->status = SDEV_ST_ERROR;
+
+	} else {
+		dev_info(&udev->dev, "device reset\n");
+		ud->status = SDEV_ST_AVAILABLE;
+
+	}
+	spin_unlock(&ud->lock);
+
+	return;
+}
+
+static void stub_device_unusable(struct usbip_device *ud)
+{
+	spin_lock(&ud->lock);
+	ud->status = SDEV_ST_ERROR;
+	spin_unlock(&ud->lock);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * stub_device_alloc - allocate a new stub_device struct
+ * @interface: usb_interface of a new device
+ *
+ * Allocates and initializes a new stub_device struct.
+ */
+static struct stub_device *stub_device_alloc(struct usb_interface *interface)
+{
+	struct stub_device *sdev;
+	int busnum = interface_to_busnum(interface);
+	int devnum = interface_to_devnum(interface);
+
+	dev_dbg(&interface->dev, "allocating stub device");
+
+	/* yes, it's a new device */
+	sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL);
+	if (!sdev) {
+		dev_err(&interface->dev, "no memory for stub_device\n");
+		return NULL;
+	}
+
+	sdev->interface = interface;
+
+	/*
+	 * devid is defined with devnum when this driver is first allocated.
+	 * devnum may change later if a device is reset. However, devid never
+	 * changes during a usbip connection.
+	 */
+	sdev->devid     = (busnum << 16) | devnum;
+
+	usbip_task_init(&sdev->ud.tcp_rx, "stub_rx", stub_rx_loop);
+	usbip_task_init(&sdev->ud.tcp_tx, "stub_tx", stub_tx_loop);
+
+	sdev->ud.side = USBIP_STUB;
+	sdev->ud.status = SDEV_ST_AVAILABLE;
+	/* sdev->ud.lock = SPIN_LOCK_UNLOCKED; */
+	spin_lock_init(&sdev->ud.lock);
+	sdev->ud.tcp_socket = NULL;
+
+	INIT_LIST_HEAD(&sdev->priv_init);
+	INIT_LIST_HEAD(&sdev->priv_tx);
+	INIT_LIST_HEAD(&sdev->priv_free);
+	INIT_LIST_HEAD(&sdev->unlink_free);
+	INIT_LIST_HEAD(&sdev->unlink_tx);
+	/* sdev->priv_lock = SPIN_LOCK_UNLOCKED; */
+	spin_lock_init(&sdev->priv_lock);
+
+	init_waitqueue_head(&sdev->tx_waitq);
+
+	sdev->ud.eh_ops.shutdown = stub_shutdown_connection;
+	sdev->ud.eh_ops.reset    = stub_device_reset;
+	sdev->ud.eh_ops.unusable = stub_device_unusable;
+
+	usbip_start_eh(&sdev->ud);
+
+	udbg("register new interface\n");
+	return sdev;
+}
+
+static int stub_device_free(struct stub_device *sdev)
+{
+	if (!sdev)
+		return -EINVAL;
+
+	kfree(sdev);
+	udbg("kfree udev ok\n");
+
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * If a usb device has multiple active interfaces, this driver is bound to all
+ * the active interfaces. However, usbip exports *a* usb device (i.e., not *an*
+ * active interface). Currently, a userland program must ensure that it
+ * looks at the usbip's sysfs entries of only the first active interface.
+ *
+ * TODO: use "struct usb_device_driver" to bind a usb device.
+ * However, it seems it is not fully supported in mainline kernel yet
+ * (2.6.19.2).
+ */
+static int stub_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct stub_device *sdev = NULL;
+	char *udev_busid = interface->dev.parent->bus_id;
+	int err = 0;
+
+	dev_dbg(&interface->dev, "Enter\n");
+
+	/* check we should claim or not by busid_table */
+	if (match_busid(udev_busid)) {
+		dev_info(&interface->dev,
+			 "this device %s is not in match_busid table. skip!\n",
+			 udev_busid);
+
+		/*
+		 * Return value should be ENODEV or ENOXIO to continue trying
+		 * other matched drivers by the driver core.
+		 * See driver_probe_device() in driver/base/dd.c
+		 */
+		return -ENODEV;
+	}
+
+	if (udev->descriptor.bDeviceClass ==  USB_CLASS_HUB) {
+		udbg("this device %s is a usb hub device. skip!\n",
+								udev_busid);
+		return -ENODEV;
+	}
+
+	if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
+		udbg("this device %s is attached on vhci_hcd. skip!\n",
+								udev_busid);
+		return -ENODEV;
+	}
+
+	/* ok. this is my device. */
+	sdev = stub_device_alloc(interface);
+	if (!sdev)
+		return -ENOMEM;
+
+	dev_info(&interface->dev, "USB/IP Stub: register a new interface "
+		 "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum,
+		 interface->cur_altsetting->desc.bInterfaceNumber);
+
+	/* set private data to usb_interface */
+	usb_set_intfdata(interface, sdev);
+
+	err = stub_add_files(&interface->dev);
+	if (err) {
+		dev_err(&interface->dev, "create sysfs files for %s\n",
+			udev_busid);
+		return err;
+	}
+
+	return 0;
+}
+
+
+/*
+ * called in usb_disconnect() or usb_deregister()
+ * but only if actconfig(active configuration) exists
+ */
+static void stub_disconnect(struct usb_interface *interface)
+{
+	struct stub_device *sdev = usb_get_intfdata(interface);
+
+	udbg("Enter\n");
+
+	/* get stub_device */
+	if (!sdev) {
+		err(" could not get device from inteface data");
+		/* BUG(); */
+		return;
+	}
+
+	usb_set_intfdata(interface, NULL);
+
+
+	/*
+	 * NOTE:
+	 * rx/tx threads are invoked for each usb_device.
+	 */
+	stub_remove_files(&interface->dev);
+
+	/* 1. shutdown the current connection */
+	usbip_event_add(&sdev->ud, SDEV_EVENT_REMOVED);
+
+	/* 2. wait for the stop of the event handler */
+	usbip_stop_eh(&sdev->ud);
+
+	/* 3. free sdev */
+	stub_device_free(sdev);
+
+
+	udbg("bye\n");
+}
--- /dev/null
+++ b/drivers/usb/ip/stub.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/net.h>
+
+struct stub_device {
+	struct usb_interface *interface;
+	struct list_head list;
+
+	struct usbip_device ud;
+	__u32 devid;
+
+	/*
+	 * stub_priv preserves private data of each urb.
+	 * It is allocated as stub_priv_cache and assigned to urb->context.
+	 *
+	 * stub_priv is always linked to any one of 3 lists;
+	 *	priv_init: linked to this until the comletion of a urb.
+	 *	priv_tx  : linked to this after the completion of a urb.
+	 *	priv_free: linked to this after the sending of the result.
+	 *
+	 * Any of these list operations should be locked by priv_lock.
+	 */
+	spinlock_t priv_lock;
+	struct list_head priv_init;
+	struct list_head priv_tx;
+	struct list_head priv_free;
+
+	/* see comments for unlinking in stub_rx.c */
+	struct list_head unlink_tx;
+	struct list_head unlink_free;
+
+
+	wait_queue_head_t tx_waitq;
+};
+
+/* private data into urb->priv */
+struct stub_priv {
+	unsigned long seqnum;
+	struct list_head list;
+	struct stub_device *sdev;
+	struct urb *urb;
+
+	int unlinking;
+};
+
+struct stub_unlink {
+	unsigned long seqnum;
+	struct list_head list;
+	__u32 status;
+};
+
+
+extern struct kmem_cache *stub_priv_cache;
+
+
+/*-------------------------------------------------------------------------*/
+/* prototype declarations */
+
+/* stub_tx.c */
+void stub_complete(struct urb *);
+void stub_tx_loop(struct usbip_task *);
+
+/* stub_dev.c */
+extern struct usb_driver stub_driver;
+
+/* stub_rx.c */
+void stub_rx_loop(struct usbip_task *);
+void stub_enqueue_ret_unlink(struct stub_device *, __u32, __u32);
+
+/* stub_main.c */
+int match_busid(char *busid);
+void stub_device_cleanup_urbs(struct stub_device *sdev);
--- /dev/null
+++ b/drivers/usb/ip/stub_main.c
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+
+#include "usbip_common.h"
+#include "stub.h"
+
+/* Version Information */
+#define DRIVER_VERSION "1.0"
+#define DRIVER_AUTHOR "Takahiro Hirofuchi"
+#define DRIVER_DESC "Stub Driver for USB/IP"
+
+/* stub_priv is allocated from stub_priv_cache */
+struct kmem_cache *stub_priv_cache;
+
+/*-------------------------------------------------------------------------*/
+
+/* Define sysfs entries for the usbip driver */
+
+
+/*
+ * busid_tables defines matching busids that usbip can grab. A user can change
+ * dynamically what device is locally used and what device is exported to a
+ * remote host.
+ */
+#define MAX_BUSID 16
+static char busid_table[MAX_BUSID][BUS_ID_SIZE];
+static spinlock_t busid_table_lock;
+
+
+int match_busid(char *busid)
+{
+	int i;
+
+	spin_lock(&busid_table_lock);
+
+	for (i = 0; i < MAX_BUSID; i++)
+		if (busid_table[i][0])
+			if (!strncmp(busid_table[i], busid, BUS_ID_SIZE)) {
+				/* already registerd */
+				spin_unlock(&busid_table_lock);
+				return 0;
+			}
+
+	spin_unlock(&busid_table_lock);
+
+	return 1;
+}
+
+static ssize_t show_match_busid(struct device_driver *drv, char *buf)
+{
+	int i;
+	char *out = buf;
+
+	spin_lock(&busid_table_lock);
+
+	for (i = 0; i < MAX_BUSID; i++)
+		if (busid_table[i][0])
+			out += sprintf(out, "%s ", busid_table[i]);
+
+	spin_unlock(&busid_table_lock);
+
+	out += sprintf(out, "\n");
+
+	return out - buf;
+}
+
+static int add_match_busid(char *busid)
+{
+	int i;
+
+	if (!match_busid(busid))
+		return 0;
+
+	spin_lock(&busid_table_lock);
+
+	for (i = 0; i < MAX_BUSID; i++)
+		if (!busid_table[i][0]) {
+			strncpy(busid_table[i], busid, BUS_ID_SIZE);
+			spin_unlock(&busid_table_lock);
+			return 0;
+		}
+
+	spin_unlock(&busid_table_lock);
+
+	return -1;
+}
+
+static int del_match_busid(char *busid)
+{
+	int i;
+
+	spin_lock(&busid_table_lock);
+
+	for (i = 0; i < MAX_BUSID; i++)
+		if (!strncmp(busid_table[i], busid, BUS_ID_SIZE)) {
+			/* found */
+			memset(busid_table[i], 0, BUS_ID_SIZE);
+			spin_unlock(&busid_table_lock);
+			return 0;
+		}
+
+	spin_unlock(&busid_table_lock);
+
+	return -1;
+}
+
+static ssize_t store_match_busid(struct device_driver *dev, const char *buf,
+		size_t count)
+{
+	int len;
+	char busid[BUS_ID_SIZE];
+
+	if (count < 5)
+		return -EINVAL;
+
+	/* strnlen() does not include \0 */
+	len = strnlen(buf + 4, BUS_ID_SIZE);
+
+	/* busid needs to include \0 termination */
+	if (!(len < BUS_ID_SIZE))
+		return -EINVAL;
+
+	strncpy(busid, buf + 4, BUS_ID_SIZE);
+
+
+	if (!strncmp(buf, "add ", 4)) {
+		if (add_match_busid(busid) < 0)
+			return -ENOMEM;
+		else {
+			udbg("add busid %s\n", busid);
+			return count;
+		}
+	} else if (!strncmp(buf, "del ", 4)) {
+		if (del_match_busid(busid) < 0)
+			return -ENODEV;
+		else {
+			udbg("del busid %s\n", busid);
+			return count;
+		}
+	} else
+		return -EINVAL;
+}
+
+static DRIVER_ATTR(match_busid, S_IRUSR|S_IWUSR, show_match_busid,
+							store_match_busid);
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Cleanup functions used to free private data */
+
+static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead)
+{
+	struct stub_priv *priv, *tmp;
+
+	list_for_each_entry_safe(priv, tmp, listhead, list) {
+		list_del(&priv->list);
+		return priv;
+	}
+
+	return NULL;
+}
+
+static struct stub_priv *stub_priv_pop(struct stub_device *sdev)
+{
+	unsigned long flags;
+	struct stub_priv *priv;
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	priv = stub_priv_pop_from_listhead(&sdev->priv_init);
+	if (priv) {
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		return priv;
+	}
+
+	priv = stub_priv_pop_from_listhead(&sdev->priv_tx);
+	if (priv) {
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		return priv;
+	}
+
+	priv = stub_priv_pop_from_listhead(&sdev->priv_free);
+	if (priv) {
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		return priv;
+	}
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+	return NULL;
+}
+
+void stub_device_cleanup_urbs(struct stub_device *sdev)
+{
+	struct stub_priv *priv;
+
+	udbg("free sdev %p\n", sdev);
+
+	while ((priv = stub_priv_pop(sdev))) {
+		struct urb *urb = priv->urb;
+
+		udbg("   free urb %p\n", urb);
+		usb_kill_urb(urb);
+
+		kmem_cache_free(stub_priv_cache, priv);
+
+		if (urb->transfer_buffer != NULL)
+			kfree(urb->transfer_buffer);
+
+		if (urb->setup_packet != NULL)
+			kfree(urb->setup_packet);
+
+		usb_free_urb(urb);
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usb_stub_init(void)
+{
+	int ret;
+
+	stub_priv_cache = kmem_cache_create("stub_priv",
+					    sizeof(struct stub_priv), 0,
+					    SLAB_HWCACHE_ALIGN, NULL);
+
+	if (!stub_priv_cache) {
+		printk(KERN_ERR KBUILD_MODNAME
+		       ": create stub_priv_cache error\n");
+		return -ENOMEM;
+	}
+
+	ret = usb_register(&stub_driver);
+	if (ret) {
+		printk(KERN_ERR KBUILD_MODNAME ": usb_register failed %d\n",
+		       ret);
+		goto error_usb_register;
+	}
+
+	printk(KERN_INFO KBUILD_MODNAME ":"
+	       DRIVER_DESC ":" DRIVER_VERSION "\n");
+
+	memset(busid_table, 0, sizeof(busid_table));
+	spin_lock_init(&busid_table_lock);
+
+	ret = driver_create_file(&stub_driver.drvwrap.driver,
+				 &driver_attr_match_busid);
+
+	if (ret) {
+		printk(KERN_ERR KBUILD_MODNAME ": create driver sysfs\n");
+		goto error_create_file;
+	}
+
+	return ret;
+error_create_file:
+	usb_deregister(&stub_driver);
+error_usb_register:
+	kmem_cache_destroy(stub_priv_cache);
+	return ret;
+}
+
+static void __exit usb_stub_exit(void)
+{
+	driver_remove_file(&stub_driver.drvwrap.driver,
+			   &driver_attr_match_busid);
+
+	/*
+	 * deregister() calls stub_disconnect() for all devices. Device
+	 * specific data is cleared in stub_disconnect().
+	 */
+	usb_deregister(&stub_driver);
+
+	kmem_cache_destroy(stub_priv_cache);
+}
+
+module_init(usb_stub_init);
+module_exit(usb_stub_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/drivers/usb/ip/stub_rx.c
@@ -0,0 +1,616 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "usbip_common.h"
+#include "stub.h"
+
+#include HCD_HEADER
+
+
+static int is_clear_halt_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+	 return (req->bRequest == USB_REQ_CLEAR_FEATURE) &&
+		 (req->bRequestType == USB_RECIP_ENDPOINT) &&
+		 (req->wValue == USB_ENDPOINT_HALT);
+}
+
+static int is_set_interface_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+	return (req->bRequest == USB_REQ_SET_INTERFACE) &&
+		   (req->bRequestType == USB_RECIP_INTERFACE);
+}
+
+static int is_set_configuration_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+	return (req->bRequest == USB_REQ_SET_CONFIGURATION) &&
+		   (req->bRequestType == USB_RECIP_DEVICE);
+}
+
+static int is_reset_device_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	__u16 value;
+	__u16 index;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+	value = le16_to_cpu(req->wValue);
+	index = le16_to_cpu(req->wIndex);
+
+	if ((req->bRequest == USB_REQ_SET_FEATURE) &&
+			(req->bRequestType == USB_RT_PORT) &&
+			(value = USB_PORT_FEAT_RESET)) {
+		dbg_stub_rx("reset_device_cmd, port %u\n", index);
+		return 1;
+	} else
+		return 0;
+}
+
+static int tweak_clear_halt_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	int target_endp;
+	int target_dir;
+	int target_pipe;
+	int ret;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+
+	/*
+	 * The stalled endpoint is specified in the wIndex value. The endpoint
+	 * of the urb is the target of this clear_halt request (i.e., control
+	 * endpoint).
+	 */
+	target_endp = le16_to_cpu(req->wIndex) & 0x000f;
+
+	/* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80.  */
+	target_dir = le16_to_cpu(req->wIndex) & 0x0080;
+
+	if (target_dir)
+		target_pipe = usb_rcvctrlpipe(urb->dev, target_endp);
+	else
+		target_pipe = usb_sndctrlpipe(urb->dev, target_endp);
+
+	ret = usb_clear_halt(urb->dev, target_pipe);
+	if (ret < 0)
+		uinfo("clear_halt error: devnum %d endp %d, %d\n",
+				urb->dev->devnum, target_endp, ret);
+	else
+		uinfo("clear_halt done: devnum %d endp %d\n",
+				urb->dev->devnum, target_endp);
+
+	return ret;
+}
+
+static int tweak_set_interface_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	__u16 alternate;
+	__u16 interface;
+	int ret;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+	alternate = le16_to_cpu(req->wValue);
+	interface = le16_to_cpu(req->wIndex);
+
+	dbg_stub_rx("set_interface: inf %u alt %u\n", interface, alternate);
+
+	ret = usb_set_interface(urb->dev, interface, alternate);
+	if (ret < 0)
+		uinfo("set_interface error: inf %u alt %u, %d\n",
+				interface, alternate, ret);
+	else
+		uinfo("set_interface done: inf %u alt %u\n",
+							interface,
+							alternate);
+
+	return ret;
+}
+
+static int tweak_set_configuration_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	__u16 config;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+	config = le16_to_cpu(req->wValue);
+
+	/*
+	 * I have never seen a multi-config device. Very rare.
+	 * For most devices, this will be called to choose a default
+	 * configuration only once in an initialization phase.
+	 *
+	 * set_configuration may change a device configuration and its device
+	 * drivers will be unbound and assigned for a new device configuration.
+	 * This means this usbip driver will be also unbound when called, then
+	 * eventually reassigned to the device as far as driver matching
+	 * condition is kept.
+	 *
+	 * Unfortunatelly, an existing usbip connection will be dropped
+	 * due to this driver unbinding. So, skip here.
+	 * A user may need to set a special configuration value before
+	 * exporting the device.
+	 */
+	uinfo("set_configuration (%d) to %s\n", config, urb->dev->dev.bus_id);
+	uinfo("but, skip!\n");
+
+	return 0;
+	/* return usb_driver_set_configuration(urb->dev, config); */
+}
+
+static int tweak_reset_device_cmd(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	__u16 value;
+	__u16 index;
+	int ret;
+
+	req = (struct usb_ctrlrequest *) urb->setup_packet;
+	value = le16_to_cpu(req->wValue);
+	index = le16_to_cpu(req->wIndex);
+
+	uinfo("reset_device (port %d) to %s\n", index, urb->dev->dev.bus_id);
+
+	/* all interfaces should be owned by usbip driver, so just reset it.  */
+	ret = usb_lock_device_for_reset(urb->dev, NULL);
+	if (ret < 0) {
+		dev_err(&urb->dev->dev, "lock for reset\n");
+		return ret;
+	}
+
+	/* try to reset the device */
+	ret = usb_reset_device(urb->dev);
+	if (ret < 0)
+		dev_err(&urb->dev->dev, "device reset\n");
+
+	usb_unlock_device(urb->dev);
+
+	return ret;
+}
+
+/*
+ * clear_halt, set_interface, and set_configuration require special tricks.
+ */
+static void tweak_special_requests(struct urb *urb)
+{
+	if (!urb || !urb->setup_packet)
+		return;
+
+	if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
+		return;
+
+	if (is_clear_halt_cmd(urb))
+		/* tweak clear_halt */
+		 tweak_clear_halt_cmd(urb);
+
+	else if (is_set_interface_cmd(urb))
+		/* tweak set_interface */
+		tweak_set_interface_cmd(urb);
+
+	else if (is_set_configuration_cmd(urb))
+		/* tweak set_configuration */
+		tweak_set_configuration_cmd(urb);
+
+	else if (is_reset_device_cmd(urb))
+		tweak_reset_device_cmd(urb);
+	else
+		dbg_stub_rx("no need to tweak\n");
+}
+
+/*
+ * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb().
+ * By unlinking the urb asynchronously, stub_rx can continuously
+ * process coming urbs.  Even if the urb is unlinked, its completion
+ * handler will be called and stub_tx will send a return pdu.
+ *
+ * See also comments about unlinking strategy in vhci_hcd.c.
+ */
+static int stub_recv_cmd_unlink(struct stub_device *sdev,
+						struct usbip_header *pdu)
+{
+	struct list_head *listhead = &sdev->priv_init;
+	struct list_head *ptr;
+	unsigned long flags;
+
+	struct stub_priv *priv;
+
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	for (ptr = listhead->next; ptr != listhead; ptr = ptr->next) {
+		priv = list_entry(ptr, struct stub_priv, list);
+		if (priv->seqnum == pdu->u.cmd_unlink.seqnum) {
+			int ret;
+
+			dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
+				 priv->urb);
+
+			/*
+			 * This matched urb is not completed yet (i.e., be in
+			 * flight in usb hcd hardware/driver). Now we are
+			 * cancelling it. The unlinking flag means that we are
+			 * now not going to return the normal result pdu of a
+			 * submission request, but going to return a result pdu
+			 * of the unlink request.
+			 */
+			priv->unlinking = 1;
+
+			/*
+			 * In the case that unlinking flag is on, prev->seqnum
+			 * is changed from the seqnum of the cancelling urb to
+			 * the seqnum of the unlink request. This will be used
+			 * to make the result pdu of the unlink request.
+			 */
+			priv->seqnum = pdu->base.seqnum;
+
+			spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+			/*
+			 * usb_unlink_urb() is now out of spinlocking to avoid
+			 * spinlock recursion since stub_complete() is
+			 * sometimes called in this context but not in the
+			 * interrupt context.  If stub_complete() is executed
+			 * before we call usb_unlink_urb(), usb_unlink_urb()
+			 * will return an error value. In this case, stub_tx
+			 * will return the result pdu of this unlink request
+			 * though submission is completed and actual unlinking
+			 * is not executed. OK?
+			 */
+			/* In the above case, urb->status is not -ECONNRESET,
+			 * so a driver in a client host will know the failure
+			 * of the unlink request ?
+			 */
+			ret = usb_unlink_urb(priv->urb);
+			if (ret != -EINPROGRESS)
+				dev_err(&priv->urb->dev->dev,
+					"failed to unlink a urb %p, ret %d\n",
+					priv->urb, ret);
+			return 0;
+		}
+	}
+
+	dbg_stub_rx("seqnum %d is not pending\n", pdu->u.cmd_unlink.seqnum);
+
+	/*
+	 * The urb of the unlink target is not found in priv_init queue. It was
+	 * already completed and its results is/was going to be sent by a
+	 * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only
+	 * return the completeness of this unlink request to vhci_hcd.
+	 */
+	stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0);
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+
+	return 0;
+}
+
+static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
+{
+	struct usbip_device *ud = &sdev->ud;
+
+	if (pdu->base.devid == sdev->devid) {
+		spin_lock(&ud->lock);
+		if (ud->status == SDEV_ST_USED) {
+			/* A request is valid. */
+			spin_unlock(&ud->lock);
+			return 1;
+		}
+		spin_unlock(&ud->lock);
+	}
+
+	return 0;
+}
+
+static struct stub_priv *stub_priv_alloc(struct stub_device *sdev,
+					 struct usbip_header *pdu)
+{
+	struct stub_priv *priv;
+	struct usbip_device *ud = &sdev->ud;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	priv = kmem_cache_alloc(stub_priv_cache, GFP_ATOMIC);
+	if (!priv) {
+		dev_err(&sdev->interface->dev, "alloc stub_priv\n");
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+		return NULL;
+	}
+
+	memset(priv, 0, sizeof(struct stub_priv));
+
+	priv->seqnum = pdu->base.seqnum;
+	priv->sdev = sdev;
+
+	/*
+	 * After a stub_priv is linked to a list_head,
+	 * our error handler can free allocated data.
+	 */
+	list_add_tail(&priv->list, &sdev->priv_init);
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	return priv;
+}
+
+
+static struct usb_host_endpoint *get_ep_from_epnum(struct usb_device *udev,
+		int epnum0)
+{
+	struct usb_host_config *config;
+	int i = 0, j = 0;
+	struct usb_host_endpoint *ep = NULL;
+	int epnum;
+	int found = 0;
+
+	if (epnum0 == 0)
+		return &udev->ep0;
+
+	config = udev->actconfig;
+	if (!config)
+		return NULL;
+
+	for (i = 0; i < config->desc.bNumInterfaces; i++) {
+		struct usb_host_interface *setting;
+
+		setting = config->interface[i]->cur_altsetting;
+
+		for (j = 0; j < setting->desc.bNumEndpoints; j++) {
+			ep = &setting->endpoint[j];
+			epnum = (ep->desc.bEndpointAddress & 0x7f);
+
+			if (epnum == epnum0) {
+				/* uinfo("found epnum %d\n", epnum0); */
+				found = 1;
+				break;
+			}
+		}
+	}
+
+	if (found)
+		return ep;
+	else
+		return NULL;
+}
+
+
+static int get_pipe(struct stub_device *sdev, int epnum, int dir)
+{
+	struct usb_device *udev = interface_to_usbdev(sdev->interface);
+	struct usb_host_endpoint *ep;
+	struct usb_endpoint_descriptor *epd = NULL;
+
+	ep = get_ep_from_epnum(udev, epnum);
+	if (!ep) {
+		dev_err(&sdev->interface->dev, "no such endpoint?, %d\n",
+			epnum);
+		BUG();
+	}
+
+	epd = &ep->desc;
+
+
+#if 0
+	/* epnum 0 is always control */
+	if (epnum == 0) {
+		if (dir == USBIP_DIR_OUT)
+			return usb_sndctrlpipe(udev, 0);
+		else
+			return usb_rcvctrlpipe(udev, 0);
+	}
+#endif
+
+	if (usb_endpoint_xfer_control(epd)) {
+		if (dir == USBIP_DIR_OUT)
+			return usb_sndctrlpipe(udev, epnum);
+		else
+			return usb_rcvctrlpipe(udev, epnum);
+	}
+
+	if (usb_endpoint_xfer_bulk(epd)) {
+		if (dir == USBIP_DIR_OUT)
+			return usb_sndbulkpipe(udev, epnum);
+		else
+			return usb_rcvbulkpipe(udev, epnum);
+	}
+
+	if (usb_endpoint_xfer_int(epd)) {
+		if (dir == USBIP_DIR_OUT)
+			return usb_sndintpipe(udev, epnum);
+		else
+			return usb_rcvintpipe(udev, epnum);
+	}
+
+	if (usb_endpoint_xfer_isoc(epd)) {
+		if (dir == USBIP_DIR_OUT)
+			return usb_sndisocpipe(udev, epnum);
+		else
+			return usb_rcvisocpipe(udev, epnum);
+	}
+
+	/* NOT REACHED */
+	dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum);
+	return 0;
+}
+
+static void stub_recv_cmd_submit(struct stub_device *sdev,
+				 struct usbip_header *pdu)
+{
+	int ret;
+	struct stub_priv *priv;
+	struct usbip_device *ud = &sdev->ud;
+	struct usb_device *udev = interface_to_usbdev(sdev->interface);
+	int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction);
+
+
+	priv = stub_priv_alloc(sdev, pdu);
+	if (!priv)
+		return;
+
+	/* setup a urb */
+	if (usb_pipeisoc(pipe))
+		priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets,
+								GFP_KERNEL);
+	else
+		priv->urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!priv->urb) {
+		dev_err(&sdev->interface->dev, "malloc urb\n");
+		usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+		return;
+	}
+
+	/* set priv->urb->transfer_buffer */
+	if (pdu->u.cmd_submit.transfer_buffer_length > 0) {
+		priv->urb->transfer_buffer =
+			kzalloc(pdu->u.cmd_submit.transfer_buffer_length,
+								GFP_KERNEL);
+		if (!priv->urb->transfer_buffer) {
+			dev_err(&sdev->interface->dev, "malloc x_buff\n");
+			usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+			return;
+		}
+	}
+
+	/* set priv->urb->setup_packet */
+	priv->urb->setup_packet = kzalloc(8, GFP_KERNEL);
+	if (!priv->urb->setup_packet) {
+		dev_err(&sdev->interface->dev, "allocate setup_packet\n");
+		usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
+		return;
+	}
+	memcpy(priv->urb->setup_packet, &pdu->u.cmd_submit.setup, 8);
+
+	/* set other members from the base header of pdu */
+	priv->urb->context                = (void *) priv;
+	priv->urb->dev                    = udev;
+	priv->urb->pipe                   = pipe;
+	priv->urb->complete               = stub_complete;
+
+	usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0);
+
+
+	if (usbip_recv_xbuff(ud, priv->urb) < 0)
+		return;
+
+	if (usbip_recv_iso(ud, priv->urb) < 0)
+		return;
+
+	/* no need to submit an intercepted request, but harmless? */
+	tweak_special_requests(priv->urb);
+
+	/* urb is now ready to submit */
+	ret = usb_submit_urb(priv->urb, GFP_KERNEL);
+
+	if (ret == 0)
+		dbg_stub_rx("submit urb ok, seqnum %u\n", pdu->base.seqnum);
+	else {
+		dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret);
+		usbip_dump_header(pdu);
+		usbip_dump_urb(priv->urb);
+
+		/*
+		 * Pessimistic.
+		 * This connection will be discarded.
+		 */
+		usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+	}
+
+	dbg_stub_rx("Leave\n");
+	return;
+}
+
+/* recv a pdu */
+static void stub_rx_pdu(struct usbip_device *ud)
+{
+	int ret;
+	struct usbip_header pdu;
+	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+	struct device *dev = &sdev->interface->dev;
+
+	dbg_stub_rx("Enter\n");
+
+	memset(&pdu, 0, sizeof(pdu));
+
+	/* 1. receive a pdu header */
+	ret = usbip_xmit(0, ud->tcp_socket, (char *) &pdu, sizeof(pdu), 0);
+	if (ret != sizeof(pdu)) {
+		dev_err(dev, "recv a header, %d\n", ret);
+		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+		return;
+	}
+
+	usbip_header_correct_endian(&pdu, 0);
+
+	if (dbg_flag_stub_rx)
+		usbip_dump_header(&pdu);
+
+	if (!valid_request(sdev, &pdu)) {
+		dev_err(dev, "recv invalid request\n");
+		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+		return;
+	}
+
+	switch (pdu.base.command) {
+	case USBIP_CMD_UNLINK:
+		stub_recv_cmd_unlink(sdev, &pdu);
+		break;
+
+	case USBIP_CMD_SUBMIT:
+		stub_recv_cmd_submit(sdev, &pdu);
+		break;
+
+	default:
+		/* NOTREACHED */
+		dev_err(dev, "unknown pdu\n");
+		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+		return;
+	}
+
+}
+
+void stub_rx_loop(struct usbip_task *ut)
+{
+	struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_rx);
+
+	while (1) {
+		if (signal_pending(current)) {
+			dbg_stub_rx("signal caught!\n");
+			break;
+		}
+
+		if (usbip_event_happend(ud))
+			break;
+
+		stub_rx_pdu(ud);
+	}
+}
--- /dev/null
+++ b/drivers/usb/ip/stub_tx.c
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "usbip_common.h"
+#include "stub.h"
+
+
+static void stub_free_priv_and_urb(struct stub_priv *priv)
+{
+	struct urb *urb = priv->urb;
+
+	kfree(urb->setup_packet);
+	kfree(urb->transfer_buffer);
+	list_del(&priv->list);
+	kmem_cache_free(stub_priv_cache, priv);
+	usb_free_urb(urb);
+}
+
+/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */
+void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum,
+			     __u32 status)
+{
+	struct stub_unlink *unlink;
+
+	unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC);
+	if (!unlink) {
+		dev_err(&sdev->interface->dev, "alloc stub_unlink\n");
+		usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC);
+		return;
+	}
+
+	unlink->seqnum = seqnum;
+	unlink->status = status;
+
+	list_add_tail(&unlink->list, &sdev->unlink_tx);
+}
+
+/**
+ * stub_complete - completion handler of a usbip urb
+ * @urb: pointer to the urb completed
+ * @regs:
+ *
+ * When a urb has completed, the USB core driver calls this function mostly in
+ * the interrupt context. To return the result of a urb, the completed urb is
+ * linked to the pending list of returning.
+ *
+ */
+void stub_complete(struct urb *urb)
+{
+	struct stub_priv *priv = (struct stub_priv *) urb->context;
+	struct stub_device *sdev = priv->sdev;
+	unsigned long flags;
+
+	dbg_stub_tx("complete! status %d\n", urb->status);
+
+
+	switch (urb->status) {
+	case 0:
+		/* OK */
+		break;
+	case -ENOENT:
+		uinfo("stopped by a call of usb_kill_urb() because of"
+					"cleaning up a virtual connection\n");
+		return;
+	case -ECONNRESET:
+		uinfo("unlinked by a call of usb_unlink_urb()\n");
+		break;
+	case -EPIPE:
+		uinfo("endpoint %d is stalled\n", usb_pipeendpoint(urb->pipe));
+		break;
+	case -ESHUTDOWN:
+		uinfo("device removed?\n");
+		break;
+	default:
+		uinfo("urb completion with non-zero status %d\n", urb->status);
+	}
+
+	/* link a urb to the queue of tx. */
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	if (priv->unlinking) {
+		stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
+		stub_free_priv_and_urb(priv);
+	} else
+		list_move_tail(&priv->list, &sdev->priv_tx);
+
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	/* wake up tx_thread */
+	wake_up(&sdev->tx_waitq);
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* fill PDU */
+
+static inline void setup_base_pdu(struct usbip_header_basic *base,
+		__u32 command, __u32 seqnum)
+{
+	base->command = command;
+	base->seqnum  = seqnum;
+	base->devid   = 0;
+	base->ep      = 0;
+	base->direction   = 0;
+}
+
+static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb)
+{
+	struct stub_priv *priv = (struct stub_priv *) urb->context;
+
+	setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum);
+
+	usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1);
+}
+
+static void setup_ret_unlink_pdu(struct usbip_header *rpdu,
+		struct stub_unlink *unlink)
+{
+	setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum);
+
+	rpdu->u.ret_unlink.status = unlink->status;
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* send RET_SUBMIT */
+
+static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev)
+{
+	unsigned long flags;
+	struct stub_priv *priv, *tmp;
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) {
+		list_move_tail(&priv->list, &sdev->priv_free);
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		return priv;
+	}
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	return NULL;
+}
+
+static int stub_send_ret_submit(struct stub_device *sdev)
+{
+	unsigned long flags;
+	struct stub_priv *priv, *tmp;
+
+	struct msghdr msg;
+	struct kvec iov[3];
+	size_t txsize;
+
+	size_t total_size = 0;
+
+	while ((priv = dequeue_from_priv_tx(sdev)) != NULL) {
+		int ret;
+		struct urb *urb = priv->urb;
+		struct usbip_header pdu_header;
+		void *iso_buffer = NULL;
+
+		txsize = 0;
+		memset(&pdu_header, 0, sizeof(pdu_header));
+		memset(&msg, 0, sizeof(msg));
+		memset(&iov, 0, sizeof(iov));
+
+		dbg_stub_tx("setup txdata urb %p\n", urb);
+
+
+		/* 1. setup usbip_header */
+		setup_ret_submit_pdu(&pdu_header, urb);
+		usbip_header_correct_endian(&pdu_header, 1);
+
+		iov[0].iov_base = &pdu_header;
+		iov[0].iov_len  = sizeof(pdu_header);
+		txsize += sizeof(pdu_header);
+
+		/* 2. setup transfer buffer */
+		if (usb_pipein(urb->pipe) && urb->actual_length > 0) {
+			iov[1].iov_base = urb->transfer_buffer;
+			iov[1].iov_len  = urb->actual_length;
+			txsize += urb->actual_length;
+		}
+
+		/* 3. setup iso_packet_descriptor */
+		if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+			ssize_t len = 0;
+
+			iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len);
+			if (!iso_buffer) {
+				usbip_event_add(&sdev->ud,
+						SDEV_EVENT_ERROR_MALLOC);
+				return -1;
+			}
+
+			iov[2].iov_base = iso_buffer;
+			iov[2].iov_len  = len;
+			txsize += len;
+		}
+
+		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
+				     3, txsize);
+		if (ret != txsize) {
+			dev_err(&sdev->interface->dev,
+				"sendmsg failed!, retval %d for %zd\n",
+				ret, txsize);
+			kfree(iso_buffer);
+			usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
+			return -1;
+		}
+
+		kfree(iso_buffer);
+		dbg_stub_tx("send txdata\n");
+
+		total_size += txsize;
+	}
+
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
+		stub_free_priv_and_urb(priv);
+	}
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	return total_size;
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* send RET_UNLINK */
+
+static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev)
+{
+	unsigned long flags;
+	struct stub_unlink *unlink, *tmp;
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) {
+		list_move_tail(&unlink->list, &sdev->unlink_free);
+		spin_unlock_irqrestore(&sdev->priv_lock, flags);
+		return unlink;
+	}
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	return NULL;
+}
+
+
+static int stub_send_ret_unlink(struct stub_device *sdev)
+{
+	unsigned long flags;
+	struct stub_unlink *unlink, *tmp;
+
+	struct msghdr msg;
+	struct kvec iov[1];
+	size_t txsize;
+
+	size_t total_size = 0;
+
+	while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) {
+		int ret;
+		struct usbip_header pdu_header;
+
+		txsize = 0;
+		memset(&pdu_header, 0, sizeof(pdu_header));
+		memset(&msg, 0, sizeof(msg));
+		memset(&iov, 0, sizeof(iov));
+
+		dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum);
+
+		/* 1. setup usbip_header */
+		setup_ret_unlink_pdu(&pdu_header, unlink);
+		usbip_header_correct_endian(&pdu_header, 1);
+
+		iov[0].iov_base = &pdu_header;
+		iov[0].iov_len  = sizeof(pdu_header);
+		txsize += sizeof(pdu_header);
+
+		ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
+				     1, txsize);
+		if (ret != txsize) {
+			dev_err(&sdev->interface->dev,
+				"sendmsg failed!, retval %d for %zd\n",
+				ret, txsize);
+			usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
+			return -1;
+		}
+
+
+		dbg_stub_tx("send txdata\n");
+
+		total_size += txsize;
+	}
+
+
+	spin_lock_irqsave(&sdev->priv_lock, flags);
+
+	list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) {
+		list_del(&unlink->list);
+		kfree(unlink);
+	}
+
+	spin_unlock_irqrestore(&sdev->priv_lock, flags);
+
+	return total_size;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+void stub_tx_loop(struct usbip_task *ut)
+{
+	struct usbip_device *ud = container_of(ut, struct usbip_device, tcp_tx);
+	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
+
+	while (1) {
+		if (signal_pending(current)) {
+			dbg_stub_tx("signal catched\n");
+			break;
+		}
+
+		if (usbip_event_happend(ud))
+			break;
+
+		/*
+		 * send_ret_submit comes earlier than send_ret_unlink.  stub_rx
+		 * looks at only priv_init queue. If the completion of a URB is
+		 * earlier than the receive of CMD_UNLINK, priv is moved to
+		 * priv_tx queue and stub_rx does not find the target priv. In
+		 * this case, vhci_rx receives the result of the submit request
+		 * and then receives the result of the unlink request. The
+		 * result of the submit is given back to the usbcore as the
+		 * completion of the unlink request. The request of the
+		 * unlink is ignored. This is ok because a driver who calls
+		 * usb_unlink_urb() understands the unlink was too late by
+		 * getting the status of the given-backed URB which has the
+		 * status of usb_submit_urb().
+		 */
+		if (stub_send_ret_submit(sdev) < 0)
+			break;
+
+		if (stub_send_ret_unlink(sdev) < 0)
+			break;
+
+		wait_event_interruptible(sdev->tx_waitq,
+				(!list_empty(&sdev->priv_tx) ||
+				 !list_empty(&sdev->unlink_tx)));
+	}
+}

-- 

  parent reply	other threads:[~2008-08-28 23:06 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20080828225248.758800945@mini.kroah.org>
2008-08-28 23:00 ` [patch 00/03] USB-IP patches greg
2008-08-28 23:00   ` [patch 01/03] USB: USB/IP: add common functions needed greg
2008-08-29  7:29     ` Oliver Neukum
2008-08-29  7:52     ` Alexander E. Patrakov
2008-08-29 14:32       ` Greg KH
2008-08-30  0:14     ` Andrew Morton
2008-08-28 23:00   ` [patch 02/03] USB: USB/IP: add client driver greg
2008-08-29  8:23     ` Oliver Neukum
2008-08-29 16:02     ` Alan Stern
2008-08-28 23:00   ` greg [this message]
2008-08-29  5:43     ` [patch 03/03] USB: USB/IP: add host driver Willy Tarreau
2008-08-28 23:06   ` [patch 00/03] USB-IP patches Arnaldo Carvalho de Melo
2008-08-30  5:50   ` Brian G. Merrell
2008-09-02 17:12     ` Greg KH
2008-09-24  1:28       ` Brian G. Merrell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080828230038.GD15014@kroah.com \
    --to=greg@kroah.com \
    --cc=bgmerrell@novell.com \
    --cc=gregkh@suse.de \
    --cc=hirofuchi@users.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.