diff -purN drivers/usb/Kconfig drivers/usb/Kconfig
--- drivers/usb/Kconfig	2009-12-03 11:51:21.000000000 +0800
+++ drivers/usb/Kconfig	2010-12-31 11:05:10.000000000 +0800
@@ -99,6 +99,20 @@ config USB
 	  To compile this driver as a module, choose M here: the
 	  module will be called usbcore.
 
+config XEN_USB_FRONTEND
+        tristate "Xen pvusb device frontend driver"
+        depends on XEN
+        select XEN_XENBUS_FRONTEND
+        default y
+        help
+          The pvusb device frontend driver allows the kernel to
+          access usb devices exported exported by a virtual
+          machine containing a physical usb device driver. The
+          frontend driver is intended for unprivileged guest domains;
+          if you are compiling a kernel for a Xen guest, you almost
+          certainly want to enable this.
+
+
 source "drivers/usb/core/Kconfig"
 
 source "drivers/usb/mon/Kconfig"
diff -purN drivers/usb/Makefile drivers/usb/Makefile
--- drivers/usb/Makefile	2009-12-03 11:51:21.000000000 +0800
+++ drivers/usb/Makefile	2010-12-31 11:05:10.000000000 +0800
@@ -44,3 +44,5 @@ obj-y				+= early/
 
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= atm/
+
+obj-$(CONFIG_XEN_USB_FRONTEND)	+= xen-usbfront/
diff -purN drivers/usb/xen-usbfront/Makefile drivers/usb/xen-usbfront/Makefile
--- drivers/usb/xen-usbfront/Makefile	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/Makefile	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,11 @@
+obj-$(CONFIG_XEN_USB_FRONTEND) := xen-hcd.o
+
+xen-hcd-y   := usbfront-hcd.o xenbus.o
+
+ifeq ($(CONFIG_XEN_USB_FRONTEND_HCD_STATS),y)
+EXTRA_CFLAGS += -DXENHCD_STATS
+endif
+
+ifeq ($(CONFIG_XEN_USB_FRONTEND_HCD_PM),y)
+EXTRA_CFLAGS += -DXENHCD_PM
+endif
diff -purN drivers/usb/xen-usbfront/usbfront-dbg.c drivers/usb/xen-usbfront/usbfront-dbg.c
--- drivers/usb/xen-usbfront/usbfront-dbg.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/usbfront-dbg.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,101 @@
+/*
+ * usbfront-dbg.c
+ *
+ * Xen USB Virtual Host Controller - debugging
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+static ssize_t show_statistics(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct usb_hcd *hcd;
+	struct usbfront_info *info;
+	unsigned long flags;
+	unsigned temp, size;
+	char *next;
+
+	hcd = dev_get_drvdata(dev);
+	info = hcd_to_info(hcd);
+	next = buf;
+	size = PAGE_SIZE;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	temp = scnprintf(next, size,
+			"bus %s, device %s\n"
+			"%s\n"
+			"xenhcd, hcd state %d\n",
+			hcd->self.controller->bus->name,
+			dev_name(hcd->self.controller),
+			hcd->product_desc,
+			hcd->state);
+	size -= temp;
+	next += temp;
+
+#ifdef XENHCD_STATS
+	temp = scnprintf(next, size,
+		"complete %ld unlink %ld ring_full %ld\n",
+		info->stats.complete, info->stats.unlink,
+		info->stats.ring_full);
+	size -= temp;
+	next += temp;
+#endif
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return PAGE_SIZE - size;
+}
+
+static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
+
+static inline void create_debug_file(struct usbfront_info *info)
+{
+	struct device *dev = info_to_hcd(info)->self.controller;
+	if (device_create_file(dev, &dev_attr_statistics))
+		printk(KERN_WARNING "statistics file not created for %s\n",
+		       info_to_hcd(info)->self.bus_name);
+}
+
+static inline void remove_debug_file(struct usbfront_info *info)
+{
+	struct device *dev = info_to_hcd(info)->self.controller;
+	device_remove_file(dev, &dev_attr_statistics);
+}
diff -purN drivers/usb/xen-usbfront/usbfront-hcd.c drivers/usb/xen-usbfront/usbfront-hcd.c
--- drivers/usb/xen-usbfront/usbfront-hcd.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/usbfront-hcd.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,232 @@
+/*
+ * usbfront-hcd.c
+ *
+ * Xen USB Virtual Host Controller driver
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbfront.h"
+#include "usbfront-dbg.c"
+#include "usbfront-hub.c"
+#include "usbfront-q.c"
+
+static void xenhcd_watchdog(unsigned long param)
+{
+	struct usbfront_info *info = (struct usbfront_info *) param;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (likely(HC_IS_RUNNING(info_to_hcd(info)->state))) {
+		timer_action_done(info, TIMER_RING_WATCHDOG);
+		xenhcd_giveback_unlinked_urbs(info);
+		xenhcd_kick_pending_urbs(info);
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/*
+ * one-time HC init
+ */
+static int xenhcd_setup(struct usb_hcd *hcd)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+
+	spin_lock_init(&info->lock);
+	INIT_LIST_HEAD(&info->pending_submit_list);
+	INIT_LIST_HEAD(&info->pending_unlink_list);
+	INIT_LIST_HEAD(&info->in_progress_list);
+	INIT_LIST_HEAD(&info->giveback_waiting_list);
+	init_timer(&info->watchdog);
+	info->watchdog.function = xenhcd_watchdog;
+	info->watchdog.data = (unsigned long) info;
+	return 0;
+}
+
+/*
+ * start HC running
+ */
+static int xenhcd_run(struct usb_hcd *hcd)
+{
+	hcd->uses_new_polling = 1;
+	hcd->poll_rh = 0;
+	hcd->state = HC_STATE_RUNNING;
+	create_debug_file(hcd_to_info(hcd));
+	return 0;
+}
+
+/*
+ * stop running HC
+ */
+static void xenhcd_stop(struct usb_hcd *hcd)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+
+	del_timer_sync(&info->watchdog);
+	remove_debug_file(info);
+	spin_lock_irq(&info->lock);
+	/* cancel all urbs */
+	hcd->state = HC_STATE_HALT;
+	xenhcd_cancel_all_enqueued_urbs(info);
+	xenhcd_giveback_unlinked_urbs(info);
+	spin_unlock_irq(&info->lock);
+}
+
+/*
+ * called as .urb_enqueue()
+ * non-error returns are promise to giveback the urb later
+ */
+static int xenhcd_urb_enqueue(struct usb_hcd *hcd,
+				    struct urb *urb,
+				    gfp_t mem_flags)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+	struct urb_priv *urbp;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	urbp = alloc_urb_priv(urb);
+	if (!urbp) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	urbp->status = 1;
+
+	ret = xenhcd_submit_urb(info, urbp);
+	if (ret != 0)
+		free_urb_priv(urbp);
+
+done:
+	spin_unlock_irqrestore(&info->lock, flags);
+	return ret;
+}
+
+/*
+ * called as .urb_dequeue()
+ */
+static int xenhcd_urb_dequeue(struct usb_hcd *hcd,
+			      struct urb *urb, int status)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+	struct urb_priv *urbp;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	urbp = urb->hcpriv;
+	if (!urbp)
+		goto done;
+
+	urbp->status = status;
+	ret = xenhcd_unlink_urb(info, urbp);
+
+done:
+	spin_unlock_irqrestore(&info->lock, flags);
+	return ret;
+}
+
+/*
+ * called from usb_get_current_frame_number(),
+ * but, almost all drivers not use such function.
+ */
+static int xenhcd_get_frame(struct usb_hcd *hcd)
+{
+	/* it means error, but probably no problem :-) */
+	return 0;
+}
+
+static const char hcd_name[] = "xen_hcd";
+
+struct hc_driver xen_usb20_hc_driver = {
+	.description = hcd_name,
+	.product_desc = "Xen USB2.0 Virtual Host Controller",
+	.hcd_priv_size = sizeof(struct usbfront_info),
+	.flags = HCD_USB2,
+
+	/* basic HC lifecycle operations */
+	.reset = xenhcd_setup,
+	.start = xenhcd_run,
+	.stop = xenhcd_stop,
+
+	/* managing urb I/O */
+	.urb_enqueue = xenhcd_urb_enqueue,
+	.urb_dequeue = xenhcd_urb_dequeue,
+	.get_frame_number = xenhcd_get_frame,
+
+	/* root hub operations */
+	.hub_status_data = xenhcd_hub_status_data,
+	.hub_control = xenhcd_hub_control,
+#ifdef XENHCD_PM
+#ifdef CONFIG_PM
+	.bus_suspend = xenhcd_bus_suspend,
+	.bus_resume = xenhcd_bus_resume,
+#endif
+#endif
+};
+
+struct hc_driver xen_usb11_hc_driver = {
+	.description = hcd_name,
+	.product_desc = "Xen USB1.1 Virtual Host Controller",
+	.hcd_priv_size = sizeof(struct usbfront_info),
+	.flags = HCD_USB11,
+
+	/* basic HC lifecycle operations */
+	.reset = xenhcd_setup,
+	.start = xenhcd_run,
+	.stop = xenhcd_stop,
+
+	/* managing urb I/O */
+	.urb_enqueue = xenhcd_urb_enqueue,
+	.urb_dequeue = xenhcd_urb_dequeue,
+	.get_frame_number = xenhcd_get_frame,
+
+	/* root hub operations */
+	.hub_status_data = xenhcd_hub_status_data,
+	.hub_control = xenhcd_hub_control,
+#ifdef XENHCD_PM
+#ifdef CONFIG_PM
+	.bus_suspend = xenhcd_bus_suspend,
+	.bus_resume = xenhcd_bus_resume,
+#endif
+#endif
+};
diff -purN drivers/usb/xen-usbfront/usbfront-hub.c drivers/usb/xen-usbfront/usbfront-hub.c
--- drivers/usb/xen-usbfront/usbfront-hub.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/usbfront-hub.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,471 @@
+/*
+ * usbfront-hub.c
+ *
+ * Xen USB Virtual Host Controller - Root Hub Emulations
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * set virtual port connection status
+ */
+void set_connect_state(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	if (info->ports[port].status & USB_PORT_STAT_POWER) {
+		switch (info->devices[port].speed) {
+		case USB_SPEED_UNKNOWN:
+			info->ports[port].status &=
+				~(USB_PORT_STAT_CONNECTION |
+					USB_PORT_STAT_ENABLE |
+					USB_PORT_STAT_LOW_SPEED |
+					USB_PORT_STAT_HIGH_SPEED |
+					USB_PORT_STAT_SUSPEND);
+			break;
+		case USB_SPEED_LOW:
+			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
+			info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
+			break;
+		case USB_SPEED_FULL:
+			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
+			break;
+		case USB_SPEED_HIGH:
+			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
+			info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
+			break;
+		default: /* error */
+			return;
+		}
+		info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
+	}
+}
+
+/*
+ * set virtual device connection status
+ */
+void rhport_connect(struct usbfront_info *info,
+				int portnum, enum usb_device_speed speed)
+{
+	int port;
+
+	if (portnum < 1 || portnum > info->rh_numports)
+		return; /* invalid port number */
+
+	port = portnum - 1;
+	if (info->devices[port].speed != speed) {
+		switch (speed) {
+		case USB_SPEED_UNKNOWN: /* disconnect */
+			info->devices[port].status = USB_STATE_NOTATTACHED;
+			break;
+		case USB_SPEED_LOW:
+		case USB_SPEED_FULL:
+		case USB_SPEED_HIGH:
+			info->devices[port].status = USB_STATE_ATTACHED;
+			break;
+		default: /* error */
+			return;
+		}
+		info->devices[port].speed = speed;
+		info->ports[port].c_connection = 1;
+
+		set_connect_state(info, portnum);
+	}
+}
+
+/*
+ * SetPortFeature(PORT_SUSPENDED)
+ */
+void rhport_suspend(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	info->ports[port].status |= USB_PORT_STAT_SUSPEND;
+	info->devices[port].status = USB_STATE_SUSPENDED;
+}
+
+/*
+ * ClearPortFeature(PORT_SUSPENDED)
+ */
+void rhport_resume(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
+		info->ports[port].resuming = 1;
+		info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
+	}
+}
+
+/*
+ * SetPortFeature(PORT_POWER)
+ */
+void rhport_power_on(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
+		info->ports[port].status |= USB_PORT_STAT_POWER;
+		if (info->devices[port].status != USB_STATE_NOTATTACHED)
+			info->devices[port].status = USB_STATE_POWERED;
+		if (info->ports[port].c_connection)
+			set_connect_state(info, portnum);
+	}
+}
+
+/*
+ * ClearPortFeature(PORT_POWER)
+ * SetConfiguration(non-zero)
+ * Power_Source_Off
+ * Over-current
+ */
+void rhport_power_off(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	if (info->ports[port].status & USB_PORT_STAT_POWER) {
+		info->ports[port].status = 0;
+		if (info->devices[port].status != USB_STATE_NOTATTACHED)
+			info->devices[port].status = USB_STATE_ATTACHED;
+	}
+}
+
+/*
+ * ClearPortFeature(PORT_ENABLE)
+ */
+void rhport_disable(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
+	info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
+	info->ports[port].resuming = 0;
+	if (info->devices[port].status != USB_STATE_NOTATTACHED)
+		info->devices[port].status = USB_STATE_POWERED;
+}
+
+/*
+ * SetPortFeature(PORT_RESET)
+ */
+void rhport_reset(struct usbfront_info *info, int portnum)
+{
+	int port;
+
+	port = portnum - 1;
+	info->ports[port].status &= ~(USB_PORT_STAT_ENABLE
+					| USB_PORT_STAT_LOW_SPEED
+					| USB_PORT_STAT_HIGH_SPEED);
+	info->ports[port].status |= USB_PORT_STAT_RESET;
+
+	if (info->devices[port].status != USB_STATE_NOTATTACHED)
+		info->devices[port].status = USB_STATE_ATTACHED;
+
+	/* 10msec reset signaling */
+	info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
+}
+
+#ifdef XENHCD_PM
+#ifdef CONFIG_PM
+static int xenhcd_bus_suspend(struct usb_hcd *hcd)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+	int ret = 0;
+	int i, ports;
+
+	ports = info->rh_numports;
+
+	spin_lock_irq(&info->lock);
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+		ret = -ESHUTDOWN;
+	else {
+		/* suspend any active ports*/
+		for (i = 1; i <= ports; i++)
+			rhport_suspend(info, i);
+	}
+	spin_unlock_irq(&info->lock);
+
+	del_timer_sync(&info->watchdog);
+
+	return ret;
+}
+
+static int xenhcd_bus_resume(struct usb_hcd *hcd)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+	int ret = 0;
+	int i, ports;
+
+	ports = info->rh_numports;
+
+	spin_lock_irq(&info->lock);
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+		ret = -ESHUTDOWN;
+	else {
+		/* resume any suspended ports*/
+		for (i = 1; i <= ports; i++)
+			rhport_resume(info, i);
+	}
+	spin_unlock_irq(&info->lock);
+
+	return ret;
+}
+#endif
+#endif
+
+static void xenhcd_hub_descriptor(struct usbfront_info *info,
+				  struct usb_hub_descriptor *desc)
+{
+	u16 temp;
+	int ports = info->rh_numports;
+
+	desc->bDescriptorType = 0x29;
+	desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
+	desc->bHubContrCurrent = 0;
+	desc->bNbrPorts = ports;
+
+	/* size of DeviceRemovable and PortPwrCtrlMask fields*/
+	temp = 1 + (ports / 8);
+	desc->bDescLength = 7 + 2 * temp;
+
+	/* bitmaps for DeviceRemovable and PortPwrCtrlMask */
+	memset(&desc->bitmap[0], 0, temp);
+	memset(&desc->bitmap[temp], 0xff, temp);
+
+	/* per-port over current reporting and no power switching */
+	temp = 0x000a;
+	desc->wHubCharacteristics = cpu_to_le16(temp);
+}
+
+/* port status change mask for hub_status_data */
+#define PORT_C_MASK \
+	((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)
+
+/*
+ * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
+ * If port status changed, writes the bitmap to buf and return
+ * that length(number of bytes).
+ * If Nothing changed, return 0.
+ */
+static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+
+	int ports;
+	int i;
+	int length;
+
+	unsigned long flags;
+	int ret = 0;
+
+	int changed = 0;
+
+	if (!HC_IS_RUNNING(hcd->state))
+		return 0;
+
+	/* initialize the status to no-changes */
+	ports = info->rh_numports;
+	length = 1 + (ports / 8);
+	for (i = 0; i < length; i++) {
+		buf[i] = 0;
+		ret++;
+	}
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	for (i = 0; i < ports; i++) {
+		/* check status for each port */
+		if (info->ports[i].status & PORT_C_MASK) {
+			if (i < 7)
+				buf[0] |= 1 << (i + 1);
+			else if (i < 15)
+				buf[1] |= 1 << (i - 7);
+			else if (i < 23)
+				buf[2] |= 1 << (i - 15);
+			else
+				buf[3] |= 1 << (i - 23);
+			changed = 1;
+		}
+	}
+
+	if (!changed)
+		ret = 0;
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return ret;
+}
+
+static int xenhcd_hub_control(struct usb_hcd *hcd,
+			       u16 typeReq,
+			       u16 wValue,
+			       u16 wIndex,
+			       char *buf,
+			       u16 wLength)
+{
+	struct usbfront_info *info = hcd_to_info(hcd);
+	int ports = info->rh_numports;
+	unsigned long flags;
+	int ret = 0;
+	int i;
+	int changed = 0;
+
+	spin_lock_irqsave(&info->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+		/* ignore this request */
+		break;
+	case ClearPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			rhport_resume(info, wIndex);
+			break;
+		case USB_PORT_FEAT_POWER:
+			rhport_power_off(info, wIndex);
+			break;
+		case USB_PORT_FEAT_ENABLE:
+			rhport_disable(info, wIndex);
+			break;
+		case USB_PORT_FEAT_C_CONNECTION:
+			info->ports[wIndex-1].c_connection = 0;
+			/* falling through */
+		default:
+			info->ports[wIndex-1].status &= ~(1 << wValue);
+			break;
+		}
+		break;
+	case GetHubDescriptor:
+		xenhcd_hub_descriptor(info,
+				      (struct usb_hub_descriptor *) buf);
+		break;
+	case GetHubStatus:
+		/* always local power supply good and no over-current exists. */
+		*(__le32 *)buf = cpu_to_le32(0);
+		break;
+	case GetPortStatus:
+		if (!wIndex || wIndex > ports)
+			goto error;
+
+		wIndex--;
+
+		/* resume completion */
+		if (info->ports[wIndex].resuming &&
+			time_after_eq(jiffies, info->ports[wIndex].timeout)) {
+			info->ports[wIndex].status |= (USB_PORT_STAT_C_SUSPEND << 16);
+			info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
+		}
+
+		/* reset completion */
+		if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
+			time_after_eq(jiffies, info->ports[wIndex].timeout)) {
+			info->ports[wIndex].status |= (USB_PORT_STAT_C_RESET << 16);
+			info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
+
+			if (info->devices[wIndex].status != USB_STATE_NOTATTACHED) {
+				info->ports[wIndex].status |= USB_PORT_STAT_ENABLE;
+				info->devices[wIndex].status = USB_STATE_DEFAULT;
+			}
+
+			switch (info->devices[wIndex].speed) {
+			case USB_SPEED_LOW:
+				info->ports[wIndex].status |= USB_PORT_STAT_LOW_SPEED;
+				break;
+			case USB_SPEED_HIGH:
+				info->ports[wIndex].status |= USB_PORT_STAT_HIGH_SPEED;
+				break;
+			default:
+				break;
+			}
+		}
+
+		((u16 *) buf)[0] = cpu_to_le16 (info->ports[wIndex].status);
+		((u16 *) buf)[1] = cpu_to_le16 (info->ports[wIndex].status >> 16);
+		break;
+	case SetHubFeature:
+		/* not supported */
+		goto error;
+	case SetPortFeature:
+		if (!wIndex || wIndex > ports)
+			goto error;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_POWER:
+			rhport_power_on(info, wIndex);
+			break;
+		case USB_PORT_FEAT_RESET:
+			rhport_reset(info, wIndex);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			rhport_suspend(info, wIndex);
+			break;
+		default:
+			if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0)
+				info->ports[wIndex-1].status |= (1 << wValue);
+		}
+		break;
+
+	default:
+error:
+		ret = -EPIPE;
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	/* check status for each port */
+	for (i = 0; i < ports; i++) {
+		if (info->ports[i].status & PORT_C_MASK)
+			changed = 1;
+	}
+	if (changed)
+		usb_hcd_poll_rh_status(hcd);
+
+	return ret;
+}
diff -purN drivers/usb/xen-usbfront/usbfront-q.c drivers/usb/xen-usbfront/usbfront-q.c
--- drivers/usb/xen-usbfront/usbfront-q.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/usbfront-q.c	2010-12-31 18:31:50.000000000 +0800
@@ -0,0 +1,542 @@
+/*
+ * usbfront-q.c
+ *
+ * Xen USB Virtual Host Controller - RING operations.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+struct kmem_cache *xenhcd_urbp_cachep;
+
+static struct urb_priv *alloc_urb_priv(struct urb *urb)
+{
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, GFP_ATOMIC);
+	if (!urbp)
+		return NULL;
+
+	urbp->urb = urb;
+	urb->hcpriv = urbp;
+	urbp->req_id = ~0;
+	urbp->unlink_req_id = ~0;
+	INIT_LIST_HEAD(&urbp->list);
+
+	return urbp;
+}
+
+static void free_urb_priv(struct urb_priv *urbp)
+{
+	urbp->urb->hcpriv = NULL;
+	kmem_cache_free(xenhcd_urbp_cachep, urbp);
+}
+
+static inline int get_id_from_freelist(
+	struct usbfront_info *info)
+{
+	unsigned long free;
+	free = info->shadow_free;
+	BUG_ON(free >= USB_URB_RING_SIZE);
+	info->shadow_free = info->shadow[free].req.id;
+	info->shadow[free].req.id = (unsigned int)0x0fff; /* debug */
+	return free;
+}
+
+static inline void add_id_to_freelist(
+	struct usbfront_info *info, unsigned long id)
+{
+	info->shadow[id].req.id  = info->shadow_free;
+	info->shadow[id].urb = NULL;
+	info->shadow_free = id;
+}
+
+static inline int count_pages(void *addr, int length)
+{
+	unsigned long start = (unsigned long) addr >> PAGE_SHIFT;
+	unsigned long end = (unsigned long) (addr + length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+	return end - start;
+}
+
+static inline void xenhcd_gnttab_map(struct usbfront_info *info,
+		void *addr, int length, grant_ref_t *gref_head,
+		struct usbif_request_segment *seg, int nr_pages, int flags)
+{
+	grant_ref_t ref;
+	struct page *page;
+	unsigned long buffer_mfn;
+	unsigned int offset;
+	unsigned int len;
+	unsigned int bytes;
+	int i;
+
+	len = length;
+
+	for (i = 0; i < nr_pages; i++) {
+		BUG_ON(!len);
+
+		page = virt_to_page(addr);
+		buffer_mfn = pfn_to_mfn(page_to_pfn(page));
+		offset = offset_in_page(addr);
+
+		bytes = PAGE_SIZE - offset;
+		if (bytes > len)
+			bytes = len;
+
+		ref = gnttab_claim_grant_reference(gref_head);
+		BUG_ON(ref == -ENOSPC);
+		gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, buffer_mfn, flags);
+		seg[i].gref = ref;
+		seg[i].offset = (uint16_t)offset;
+		seg[i].length = (uint16_t)bytes;
+
+		addr += bytes;
+		len -= bytes;
+	}
+}
+
+static int map_urb_for_request(struct usbfront_info *info, struct urb *urb,
+		usbif_urb_request_t *req)
+{
+	grant_ref_t gref_head;
+	int nr_buff_pages = 0;
+	int nr_isodesc_pages = 0;
+	int ret = 0;
+
+	if (urb->transfer_buffer_length) {
+		nr_buff_pages = count_pages(urb->transfer_buffer, urb->transfer_buffer_length);
+
+		if (usb_pipeisoc(urb->pipe))
+			nr_isodesc_pages = count_pages(&urb->iso_frame_desc[0],
+					sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets);
+
+		if (nr_buff_pages + nr_isodesc_pages > USBIF_MAX_SEGMENTS_PER_REQUEST)
+			return -E2BIG;
+
+		ret = gnttab_alloc_grant_references(USBIF_MAX_SEGMENTS_PER_REQUEST, &gref_head);
+		if (ret) {
+			printk(KERN_ERR "usbfront: gnttab_alloc_grant_references() error\n");
+			return -ENOMEM;
+		}
+
+		xenhcd_gnttab_map(info, urb->transfer_buffer,
+				urb->transfer_buffer_length,
+				&gref_head, &req->seg[0], nr_buff_pages,
+				usb_pipein(urb->pipe) ? 0 : GTF_readonly);
+
+		if (!usb_pipeisoc(urb->pipe))
+			gnttab_free_grant_references(gref_head);
+	}
+
+	req->pipe = usbif_setportnum_pipe(urb->pipe, urb->dev->portnum);
+	req->transfer_flags = urb->transfer_flags;
+	req->buffer_length = urb->transfer_buffer_length;
+	req->nr_buffer_segs = nr_buff_pages;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		req->u.isoc.interval = urb->interval;
+		req->u.isoc.start_frame = urb->start_frame;
+		req->u.isoc.number_of_packets = urb->number_of_packets;
+		req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages;
+		/* urb->number_of_packets must be > 0 */
+		if (unlikely(urb->number_of_packets <= 0))
+			BUG();
+		xenhcd_gnttab_map(info, &urb->iso_frame_desc[0],
+			sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets,
+			&gref_head, &req->seg[nr_buff_pages], nr_isodesc_pages, 0);
+		gnttab_free_grant_references(gref_head);
+		break;
+	case PIPE_INTERRUPT:
+		req->u.intr.interval = urb->interval;
+		break;
+	case PIPE_CONTROL:
+		if (urb->setup_packet)
+			memcpy(req->u.ctrl, urb->setup_packet, 8);
+		break;
+	case PIPE_BULK:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void xenhcd_gnttab_done(struct usb_shadow *shadow)
+{
+	int nr_segs = 0;
+	int i;
+
+	nr_segs = shadow->req.nr_buffer_segs;
+
+	if (usb_pipeisoc(shadow->req.pipe))
+		nr_segs +=  shadow->req.u.isoc.nr_frame_desc_segs;
+
+	for (i = 0; i < nr_segs; i++)
+		gnttab_end_foreign_access(shadow->req.seg[i].gref, 0, 0UL);
+
+	shadow->req.nr_buffer_segs = 0;
+	shadow->req.u.isoc.nr_frame_desc_segs = 0;
+}
+
+static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb, int status)
+__releases(info->lock)
+__acquires(info->lock)
+{
+	struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+	list_del_init(&urbp->list);
+	free_urb_priv(urbp);
+	switch (urb->status) {
+	case -ECONNRESET:
+	case -ENOENT:
+		COUNT(info->stats.unlink);
+		break;
+	case -EINPROGRESS:
+		urb->status = status;
+		/* falling through */
+	default:
+		COUNT(info->stats.complete);
+	}
+	spin_unlock(&info->lock);
+	usb_hcd_giveback_urb(info_to_hcd(info), urb,
+			     urbp->status <= 0 ? urbp->status : urb->status);
+	spin_lock(&info->lock);
+}
+
+static inline int xenhcd_do_request(struct usbfront_info *info, struct urb_priv *urbp)
+{
+	usbif_urb_request_t *req;
+	struct urb *urb = urbp->urb;
+	uint16_t id;
+	int notify;
+	int ret = 0;
+
+	req = RING_GET_REQUEST(&info->urb_ring, info->urb_ring.req_prod_pvt);
+	id = get_id_from_freelist(info);
+	req->id = id;
+
+	if (unlikely(urbp->unlinked)) {
+		req->u.unlink.unlink_id = urbp->req_id;
+		req->pipe = usbif_setunlink_pipe(usbif_setportnum_pipe(
+				urb->pipe, urb->dev->portnum));
+		urbp->unlink_req_id = id;
+	} else {
+		ret = map_urb_for_request(info, urb, req);
+		if (ret < 0) {
+			add_id_to_freelist(info, id);
+			return ret;
+		}
+		urbp->req_id = id;
+	}
+
+	info->urb_ring.req_prod_pvt++;
+	info->shadow[id].urb = urb;
+	info->shadow[id].req = *req;
+
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify);
+	if (notify)
+		notify_remote_via_irq(info->irq);
+
+	return ret;
+}
+
+static void xenhcd_kick_pending_urbs(struct usbfront_info *info)
+{
+	struct urb_priv *urbp;
+	int ret;
+
+	while (!list_empty(&info->pending_submit_list)) {
+		if (RING_FULL(&info->urb_ring)) {
+			COUNT(info->stats.ring_full);
+			timer_action(info, TIMER_RING_WATCHDOG);
+			goto done;
+		}
+
+		urbp = list_entry(info->pending_submit_list.next, struct urb_priv, list);
+		ret = xenhcd_do_request(info, urbp);
+		if (ret == 0)
+			list_move_tail(&urbp->list, &info->in_progress_list);
+		else
+			xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
+	}
+	timer_action_done(info, TIMER_SCAN_PENDING_URBS);
+
+done:
+	return;
+}
+
+/*
+ * caller must lock info->lock
+ */
+static void xenhcd_cancel_all_enqueued_urbs(struct usbfront_info *info)
+{
+	struct urb_priv *urbp, *tmp;
+
+	list_for_each_entry_safe(urbp, tmp, &info->in_progress_list, list) {
+		if (!urbp->unlinked) {
+			xenhcd_gnttab_done(&info->shadow[urbp->req_id]);
+			barrier();
+			if (urbp->urb->status == -EINPROGRESS)	/* not dequeued */
+				xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
+			else					/* dequeued */
+				xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status);
+		}
+		info->shadow[urbp->req_id].urb = NULL;
+	}
+
+	list_for_each_entry_safe(urbp, tmp, &info->pending_submit_list, list) {
+		xenhcd_giveback_urb(info, urbp->urb, -ESHUTDOWN);
+	}
+
+	return;
+}
+
+/*
+ * caller must lock info->lock
+ */
+static void xenhcd_giveback_unlinked_urbs(struct usbfront_info *info)
+{
+	struct urb_priv *urbp, *tmp;
+
+	list_for_each_entry_safe(urbp, tmp, &info->giveback_waiting_list, list) {
+		xenhcd_giveback_urb(info, urbp->urb, urbp->urb->status);
+	}
+}
+
+static int xenhcd_submit_urb(struct usbfront_info *info, struct urb_priv *urbp)
+{
+	int ret = 0;
+
+	if (RING_FULL(&info->urb_ring)) {
+		list_add_tail(&urbp->list, &info->pending_submit_list);
+		COUNT(info->stats.ring_full);
+		timer_action(info, TIMER_RING_WATCHDOG);
+		goto done;
+	}
+
+	if (!list_empty(&info->pending_submit_list)) {
+		list_add_tail(&urbp->list, &info->pending_submit_list);
+		timer_action(info, TIMER_SCAN_PENDING_URBS);
+		goto done;
+	}
+
+	ret = xenhcd_do_request(info, urbp);
+	if (ret == 0)
+		list_add_tail(&urbp->list, &info->in_progress_list);
+
+done:
+	return ret;
+}
+
+static int xenhcd_unlink_urb(struct usbfront_info *info, struct urb_priv *urbp)
+{
+	int ret = 0;
+
+	/* already unlinked? */
+	if (urbp->unlinked)
+		return -EBUSY;
+
+	urbp->unlinked = 1;
+
+	/* the urb is still in pending_submit queue */
+	if (urbp->req_id == ~0) {
+		list_move_tail(&urbp->list, &info->giveback_waiting_list);
+		timer_action(info, TIMER_SCAN_PENDING_URBS);
+		goto done;
+	}
+
+	/* send unlink request to backend */
+	if (RING_FULL(&info->urb_ring)) {
+		list_move_tail(&urbp->list, &info->pending_unlink_list);
+		COUNT(info->stats.ring_full);
+		timer_action(info, TIMER_RING_WATCHDOG);
+		goto done;
+	}
+
+	if (!list_empty(&info->pending_unlink_list)) {
+		list_move_tail(&urbp->list, &info->pending_unlink_list);
+		timer_action(info, TIMER_SCAN_PENDING_URBS);
+		goto done;
+	}
+
+	ret = xenhcd_do_request(info, urbp);
+	if (ret == 0)
+		list_move_tail(&urbp->list, &info->in_progress_list);
+
+done:
+	return ret;
+}
+
+static int xenhcd_urb_request_done(struct usbfront_info *info)
+{
+	usbif_urb_response_t *res;
+	struct urb *urb;
+
+	RING_IDX i, rp;
+	uint16_t id;
+	int more_to_do = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	rp = info->urb_ring.sring->rsp_prod;
+	rmb(); /* ensure we see queued responses up to "rp" */
+
+	for (i = info->urb_ring.rsp_cons; i != rp; i++) {
+		res = RING_GET_RESPONSE(&info->urb_ring, i);
+		id = res->id;
+
+		if (likely(usbif_pipesubmit(info->shadow[id].req.pipe))) {
+			xenhcd_gnttab_done(&info->shadow[id]);
+			urb = info->shadow[id].urb;
+			barrier();
+			if (likely(urb)) {
+				urb->actual_length = res->actual_length;
+				urb->error_count = res->error_count;
+				urb->start_frame = res->start_frame;
+				barrier();
+				xenhcd_giveback_urb(info, urb, res->status);
+			}
+		}
+
+		add_id_to_freelist(info, id);
+	}
+	info->urb_ring.rsp_cons = i;
+
+	if (i != info->urb_ring.req_prod_pvt)
+		RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do);
+	else
+		info->urb_ring.sring->rsp_event = i + 1;
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	cond_resched();
+
+	return more_to_do;
+}
+
+static int xenhcd_conn_notify(struct usbfront_info *info)
+{
+	usbif_conn_response_t *res;
+	usbif_conn_request_t *req;
+	RING_IDX rc, rp;
+	uint16_t id;
+	uint8_t portnum, speed;
+	int more_to_do = 0;
+	int notify;
+	int port_changed = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	rc = info->conn_ring.rsp_cons;
+	rp = info->conn_ring.sring->rsp_prod;
+	rmb(); /* ensure we see queued responses up to "rp" */
+
+	while (rc != rp) {
+		res = RING_GET_RESPONSE(&info->conn_ring, rc);
+		id = res->id;
+		portnum = res->portnum;
+		speed = res->speed;
+		info->conn_ring.rsp_cons = ++rc;
+
+		rhport_connect(info, portnum, speed);
+		if (info->ports[portnum-1].c_connection)
+			port_changed = 1;
+
+		barrier();
+
+		req = RING_GET_REQUEST(&info->conn_ring, info->conn_ring.req_prod_pvt);
+		req->id = id;
+		info->conn_ring.req_prod_pvt++;
+	}
+
+	if (rc != info->conn_ring.req_prod_pvt)
+		RING_FINAL_CHECK_FOR_RESPONSES(&info->conn_ring, more_to_do);
+	else
+		info->conn_ring.sring->rsp_event = rc + 1;
+
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
+	if (notify)
+		notify_remote_via_irq(info->irq);
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (port_changed)
+		usb_hcd_poll_rh_status(info_to_hcd(info));
+
+	cond_resched();
+
+	return more_to_do;
+}
+
+int xenhcd_schedule(void *arg)
+{
+	struct usbfront_info *info = (struct usbfront_info *) arg;
+
+	while (!kthread_should_stop()) {
+		wait_event_interruptible(
+				info->wq,
+				info->waiting_resp || kthread_should_stop());
+		info->waiting_resp = 0;
+		smp_mb();
+
+		if (xenhcd_urb_request_done(info))
+			info->waiting_resp = 1;
+
+		if (xenhcd_conn_notify(info))
+			info->waiting_resp = 1;
+	}
+
+	return 0;
+}
+
+static void xenhcd_notify_work(struct usbfront_info *info)
+{
+	info->waiting_resp = 1;
+	wake_up(&info->wq);
+}
+
+irqreturn_t xenhcd_int(int irq, void *dev_id)
+{
+	xenhcd_notify_work((struct usbfront_info *) dev_id);
+	return IRQ_HANDLED;
+}
diff -purN drivers/usb/xen-usbfront/usbfront.h drivers/usb/xen-usbfront/usbfront.h
--- drivers/usb/xen-usbfront/usbfront.h	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/usbfront.h	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,206 @@
+/*
+ * usbfront.h
+ *
+ * This file is part of Xen USB Virtual Host Controller driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_USBFRONT_H__
+#define __XEN_USBFRONT_H__
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include <xen/xenbus.h>
+#include <xen/evtchn.h>
+#include <xen/events.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/io/usbif.h>
+
+/*
+ * usbfront needs USB HCD headers,
+ * drivers/usb/core/hcd.h and drivers/usb/core/hub.h,
+ * but, they are not in public include path.
+ */
+#include "../../usb/core/hcd.h"
+#include "../../usb/core/hub.h"
+
+static inline struct usbfront_info *hcd_to_info(struct usb_hcd *hcd)
+{
+	return (struct usbfront_info *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *info_to_hcd(struct usbfront_info *info)
+{
+	return container_of((void *) info, struct usb_hcd, hcd_priv);
+}
+
+/* Private per-URB data */
+struct urb_priv {
+	struct list_head list;
+	struct urb *urb;
+	int req_id;	/* RING_REQUEST id for submitting */
+	int unlink_req_id; /* RING_REQUEST id for unlinking */
+	int status;
+	unsigned unlinked:1; /* dequeued marker */
+};
+
+/* virtual roothub port status */
+struct rhport_status {
+	u32 status;
+	unsigned resuming:1; /* in resuming */
+	unsigned c_connection:1; /* connection changed */
+	unsigned long timeout;
+};
+
+/* status of attached device */
+struct vdevice_status {
+	int devnum;
+	enum usb_device_state status;
+	enum usb_device_speed speed;
+};
+
+/* RING request shadow */
+struct usb_shadow {
+	usbif_urb_request_t req;
+	struct urb *urb;
+};
+
+/* statistics for tuning, monitoring, ... */
+struct xenhcd_stats {
+	unsigned long ring_full; /* RING_FULL conditions */
+	unsigned long complete; /* normal givebacked urbs */
+	unsigned long unlink; /* unlinked urbs */
+};
+
+struct usbfront_info {
+	/* Virtual Host Controller has 4 urb queues */
+	struct list_head pending_submit_list;
+	struct list_head pending_unlink_list;
+	struct list_head in_progress_list;
+	struct list_head giveback_waiting_list;
+
+	spinlock_t lock;
+
+	/* timer that kick pending and giveback waiting urbs */
+	struct timer_list watchdog;
+	unsigned long actions;
+
+	/* virtual root hub */
+	int rh_numports;
+	struct rhport_status ports[USB_MAXCHILDREN];
+	struct vdevice_status devices[USB_MAXCHILDREN];
+
+	/* Xen related staff */
+	struct xenbus_device *xbdev;
+	int urb_ring_ref;
+	int conn_ring_ref;
+	struct usbif_urb_front_ring urb_ring;
+	struct usbif_conn_front_ring conn_ring;
+
+	unsigned int evtchn, irq; /* event channel */
+	struct usb_shadow shadow[USB_URB_RING_SIZE];
+	unsigned long shadow_free;
+
+	/* RING_RESPONSE thread */
+	struct task_struct *kthread;
+	wait_queue_head_t wq;
+	unsigned int waiting_resp;
+
+	/* xmit statistics */
+#ifdef XENHCD_STATS
+	struct xenhcd_stats stats;
+#define COUNT(x) do { (x)++; } while (0)
+#else
+#define COUNT(x) do {} while (0)
+#endif
+};
+
+#define XENHCD_RING_JIFFIES (HZ/200)
+#define XENHCD_SCAN_JIFFIES 1
+
+enum xenhcd_timer_action {
+	TIMER_RING_WATCHDOG,
+	TIMER_SCAN_PENDING_URBS,
+};
+
+static inline void
+timer_action_done(struct usbfront_info *info, enum xenhcd_timer_action action)
+{
+	clear_bit(action, &info->actions);
+}
+
+static inline void
+timer_action(struct usbfront_info *info, enum xenhcd_timer_action action)
+{
+	if (timer_pending(&info->watchdog)
+			&& test_bit(TIMER_SCAN_PENDING_URBS, &info->actions))
+		return;
+
+	if (!test_and_set_bit(action, &info->actions)) {
+		unsigned long t;
+
+		switch (action) {
+		case TIMER_RING_WATCHDOG:
+			t = XENHCD_RING_JIFFIES;
+			break;
+		default:
+			t = XENHCD_SCAN_JIFFIES;
+			break;
+		}
+		mod_timer(&info->watchdog, t + jiffies);
+	}
+}
+
+extern struct kmem_cache *xenhcd_urbp_cachep;
+extern struct hc_driver xen_usb20_hc_driver;
+extern struct hc_driver xen_usb11_hc_driver;
+irqreturn_t xenhcd_int(int irq, void *dev_id);
+void xenhcd_rhport_state_change(struct usbfront_info *info,
+				int port, enum usb_device_speed speed);
+int xenhcd_schedule(void *arg);
+
+#endif /* __XEN_USBFRONT_H__ */
diff -purN drivers/usb/xen-usbfront/xenbus.c drivers/usb/xen-usbfront/xenbus.c
--- drivers/usb/xen-usbfront/xenbus.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/usb/xen-usbfront/xenbus.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,420 @@
+/*
+ * xenbus.c
+ *
+ * Xenbus interface for Xen USB Virtual Host Controller
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbfront.h"
+
+#define GRANT_INVALID_REF 0
+
+static void destroy_rings(struct usbfront_info *info)
+{
+	if (info->irq)
+		unbind_from_irqhandler(info->irq, info);
+	info->evtchn = info->irq = 0;
+
+	if (info->urb_ring_ref != GRANT_INVALID_REF) {
+		gnttab_end_foreign_access(info->urb_ring_ref, 0,
+					  (unsigned long)info->urb_ring.sring);
+		info->urb_ring_ref = GRANT_INVALID_REF;
+	}
+	info->urb_ring.sring = NULL;
+
+	if (info->conn_ring_ref != GRANT_INVALID_REF) {
+		gnttab_end_foreign_access(info->conn_ring_ref, 0,
+					  (unsigned long)info->conn_ring.sring);
+		info->conn_ring_ref = GRANT_INVALID_REF;
+	}
+	info->conn_ring.sring = NULL;
+}
+
+static int setup_rings(struct xenbus_device *dev,
+			   struct usbfront_info *info)
+{
+	struct usbif_urb_sring *urb_sring;
+	struct usbif_conn_sring *conn_sring;
+	int err;
+
+	info->urb_ring_ref = GRANT_INVALID_REF;
+	info->conn_ring_ref = GRANT_INVALID_REF;
+
+	urb_sring = (struct usbif_urb_sring *)get_zeroed_page(GFP_NOIO|__GFP_HIGH);
+	if (!urb_sring) {
+		xenbus_dev_fatal(dev, -ENOMEM, "allocating urb ring");
+		return -ENOMEM;
+	}
+	SHARED_RING_INIT(urb_sring);
+	FRONT_RING_INIT(&info->urb_ring, urb_sring, PAGE_SIZE);
+
+	err = xenbus_grant_ring(dev, virt_to_mfn(info->urb_ring.sring));
+	if (err < 0) {
+		free_page((unsigned long)urb_sring);
+		info->urb_ring.sring = NULL;
+		goto fail;
+	}
+	info->urb_ring_ref = err;
+
+	conn_sring = (struct usbif_conn_sring *)get_zeroed_page(GFP_NOIO|__GFP_HIGH);
+	if (!conn_sring) {
+		xenbus_dev_fatal(dev, -ENOMEM, "allocating conn ring");
+		return -ENOMEM;
+	}
+	SHARED_RING_INIT(conn_sring);
+	FRONT_RING_INIT(&info->conn_ring, conn_sring, PAGE_SIZE);
+
+	err = xenbus_grant_ring(dev, virt_to_mfn(info->conn_ring.sring));
+	if (err < 0) {
+		free_page((unsigned long)conn_sring);
+		info->conn_ring.sring = NULL;
+		goto fail;
+	}
+	info->conn_ring_ref = err;
+
+	err = xenbus_alloc_evtchn(dev, &info->evtchn);
+	if (err)
+		goto fail;
+
+	err = bind_evtchn_to_irqhandler(info->evtchn,
+		xenhcd_int, IRQF_SAMPLE_RANDOM, "usbif", info);
+	if (err <= 0) {
+		xenbus_dev_fatal(dev, err,
+				 "bind_listening_port_to_irqhandler");
+		goto fail;
+	}
+	info->irq = err;
+
+	return 0;
+fail:
+	destroy_rings(info);
+	return err;
+}
+
+static int talk_to_backend(struct xenbus_device *dev,
+			   struct usbfront_info *info)
+{
+	const char *message;
+	struct xenbus_transaction xbt;
+	int err;
+
+	err = setup_rings(dev, info);
+	if (err)
+		goto out;
+
+again:
+	err = xenbus_transaction_start(&xbt);
+	if (err) {
+		xenbus_dev_fatal(dev, err, "starting transaction");
+		goto destroy_ring;
+	}
+
+	err = xenbus_printf(xbt, dev->nodename, "urb-ring-ref", "%u",
+			    info->urb_ring_ref);
+	if (err) {
+		message = "writing urb-ring-ref";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, dev->nodename, "conn-ring-ref", "%u",
+			    info->conn_ring_ref);
+	if (err) {
+		message = "writing conn-ring-ref";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+			    info->evtchn);
+	if (err) {
+		message = "writing event-channel";
+		goto abort_transaction;
+	}
+
+	err = xenbus_transaction_end(xbt, 0);
+	if (err) {
+		if (err == -EAGAIN)
+			goto again;
+		xenbus_dev_fatal(dev, err, "completing transaction");
+		goto destroy_ring;
+	}
+
+	return 0;
+
+abort_transaction:
+	xenbus_transaction_end(xbt, 1);
+	xenbus_dev_fatal(dev, err, "%s", message);
+
+destroy_ring:
+	destroy_rings(info);
+
+out:
+	return err;
+}
+
+static int connect(struct xenbus_device *dev)
+{
+	struct usbfront_info *info = dev_get_drvdata(&dev->dev);
+
+	usbif_conn_request_t *req;
+	int i, idx, err;
+	int notify;
+	char name[TASK_COMM_LEN];
+	struct usb_hcd *hcd;
+
+	hcd = info_to_hcd(info);
+	snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum);
+
+	err = talk_to_backend(dev, info);
+	if (err)
+		return err;
+
+	info->kthread = kthread_run(xenhcd_schedule, info, name);
+	if (IS_ERR(info->kthread)) {
+		err = PTR_ERR(info->kthread);
+		info->kthread = NULL;
+		xenbus_dev_fatal(dev, err, "Error creating thread");
+		return err;
+	}
+	/* prepare ring for hotplug notification */
+	for (idx = 0, i = 0; i < USB_CONN_RING_SIZE; i++) {
+		req = RING_GET_REQUEST(&info->conn_ring, idx);
+		req->id = idx;
+		idx++;
+	}
+	info->conn_ring.req_prod_pvt = idx;
+
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->conn_ring, notify);
+	if (notify)
+		notify_remote_via_irq(info->irq);
+
+	return 0;
+}
+
+static struct usb_hcd *create_hcd(struct xenbus_device *dev)
+{
+	int i;
+	int err = 0;
+	int num_ports;
+	int usb_ver;
+	struct usb_hcd *hcd = NULL;
+	struct usbfront_info *info = NULL;
+
+	err = xenbus_scanf(XBT_NIL, dev->otherend,
+					"num-ports", "%d", &num_ports);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, err, "reading num-ports");
+		return ERR_PTR(-EINVAL);
+	}
+	if (num_ports < 1 || num_ports > USB_MAXCHILDREN) {
+		xenbus_dev_fatal(dev, err, "invalid num-ports");
+		return ERR_PTR(-EINVAL);
+	}
+
+	err = xenbus_scanf(XBT_NIL, dev->otherend,
+					"usb-ver", "%d", &usb_ver);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, err, "reading usb-ver");
+		return ERR_PTR(-EINVAL);
+	}
+	switch (usb_ver) {
+	case USB_VER_USB11:
+		hcd = usb_create_hcd(&xen_usb11_hc_driver, &dev->dev, dev_name(&dev->dev));
+		break;
+	case USB_VER_USB20:
+		hcd = usb_create_hcd(&xen_usb20_hc_driver, &dev->dev, dev_name(&dev->dev));
+		break;
+	default:
+		xenbus_dev_fatal(dev, err, "invalid usb-ver");
+		return ERR_PTR(-EINVAL);
+	}
+	if (!hcd) {
+		xenbus_dev_fatal(dev, err,
+				"fail to allocate USB host controller");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	info = hcd_to_info(hcd);
+	info->xbdev = dev;
+	info->rh_numports = num_ports;
+
+	for (i = 0; i < USB_URB_RING_SIZE; i++) {
+		info->shadow[i].req.id = i + 1;
+		info->shadow[i].urb = NULL;
+	}
+	info->shadow[USB_URB_RING_SIZE-1].req.id = 0x0fff;
+
+	return hcd;
+}
+
+static int usbfront_probe(struct xenbus_device *dev,
+			  const struct xenbus_device_id *id)
+{
+	int err;
+	struct usb_hcd *hcd;
+	struct usbfront_info *info;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	hcd = create_hcd(dev);
+	if (IS_ERR(hcd)) {
+		err = PTR_ERR(hcd);
+		xenbus_dev_fatal(dev, err,
+				"fail to create usb host controller");
+		goto fail;
+	}
+
+	info = hcd_to_info(hcd);
+	dev_set_drvdata(&dev->dev, info);
+
+	err = usb_add_hcd(hcd, 0, 0);
+	if (err != 0) {
+		xenbus_dev_fatal(dev, err,
+				"fail to adding USB host controller");
+		goto fail;
+	}
+
+	init_waitqueue_head(&info->wq);
+
+	return 0;
+
+fail:
+	usb_put_hcd(hcd);
+	dev_set_drvdata(&dev->dev, NULL);
+	return err;
+}
+
+static void usbfront_disconnect(struct xenbus_device *dev)
+{
+	struct usbfront_info *info = dev_get_drvdata(&dev->dev);
+	struct usb_hcd *hcd = info_to_hcd(info);
+
+	usb_remove_hcd(hcd);
+	if (info->kthread) {
+		kthread_stop(info->kthread);
+		info->kthread = NULL;
+	}
+	xenbus_frontend_closed(dev);
+}
+
+static void backend_changed(struct xenbus_device *dev,
+				     enum xenbus_state backend_state)
+{
+	switch (backend_state) {
+	case XenbusStateInitialising:
+	case XenbusStateInitialised:
+	case XenbusStateConnected:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+	case XenbusStateUnknown:
+	case XenbusStateClosed:
+		break;
+
+	case XenbusStateInitWait:
+		if (dev->state != XenbusStateInitialising)
+			break;
+		if (!connect(dev))
+			xenbus_switch_state(dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		usbfront_disconnect(dev);
+		break;
+
+	default:
+		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+				 backend_state);
+		break;
+	}
+}
+
+static int usbfront_remove(struct xenbus_device *dev)
+{
+	struct usbfront_info *info = dev_get_drvdata(&dev->dev);
+	struct usb_hcd *hcd = info_to_hcd(info);
+
+	destroy_rings(info);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static const struct xenbus_device_id usbfront_ids[] = {
+	{ "vusb" },
+	{ "" },
+};
+MODULE_ALIAS("xen:vusb");
+
+static struct xenbus_driver usbfront_driver = {
+	.name = "vusb",
+	.ids = usbfront_ids,
+	.probe = usbfront_probe,
+	.otherend_changed = backend_changed,
+	.remove = usbfront_remove,
+};
+
+static int __init usbfront_init(void)
+{
+	if (!xen_domain())
+		return -ENODEV;
+
+	xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv",
+			sizeof(struct urb_priv), 0, 0, NULL);
+	if (!xenhcd_urbp_cachep) {
+		printk(KERN_ERR "usbfront failed to create kmem cache\n");
+		return -ENOMEM;
+	}
+
+	return xenbus_register_frontend(&usbfront_driver);
+}
+
+static void __exit usbfront_exit(void)
+{
+	kmem_cache_destroy(xenhcd_urbp_cachep);
+	xenbus_unregister_driver(&usbfront_driver);
+}
+
+module_init(usbfront_init);
+module_exit(usbfront_exit);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Xen USB Virtual Host Controller driver (usbfront)");
+MODULE_LICENSE("Dual BSD/GPL");
diff -purN drivers/xen/Kconfig drivers/xen/Kconfig
--- drivers/xen/Kconfig	2010-12-31 18:55:42.000000000 +0800
+++ drivers/xen/Kconfig	2010-12-31 11:05:10.000000000 +0800
@@ -51,7 +51,14 @@ config XEN_BLKDEV_BACKEND
 	  block devices to other guests via a high-performance shared-memory
 	  interface.
 
- 
+config XEN_USB_BACKEND
+	tristate "PVUSB backend driver"
+	depends on XEN_BACKEND && USB
+	help
+	  The pvusb backend driver allows the kernel to export its
+	  usb to other guests via a high-performance shared-memory
+	  interface.
+
 config XEN_BLKDEV_TAP
 	tristate "Block-device tap backend driver"
 	depends on XEN_BACKEND && BLOCK
diff -purN drivers/xen/Makefile drivers/xen/Makefile
--- drivers/xen/Makefile	2010-12-31 18:55:42.000000000 +0800
+++ drivers/xen/Makefile	2010-12-31 11:05:10.000000000 +0800
@@ -14,6 +14,7 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND)	+= pcib
 obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= blkback/
 obj-$(CONFIG_XEN_BLKDEV_TAP)            += blktap/
 obj-$(CONFIG_XEN_NETDEV_BACKEND)	+= netback/
+obj-$(CONFIG_XEN_USB_BACKEND)		+= usbback/
 obj-$(CONFIG_XENFS)			+= xenfs/
 obj-$(CONFIG_XEN_SYS_HYPERVISOR)	+= sys-hypervisor.o
 obj-$(CONFIG_XEN_MCE)		+= mce.o
diff -purN drivers/xen/usbback/Makefile drivers/xen/usbback/Makefile
--- drivers/xen/usbback/Makefile	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/Makefile	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,4 @@
+obj-$(CONFIG_XEN_USB_BACKEND) := usbbk.o
+
+usbbk-y   := usbstub.o xenbus.o interface.o usbback.o
+
diff -purN drivers/xen/usbback/interface.c drivers/xen/usbback/interface.c
--- drivers/xen/usbback/interface.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/interface.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,250 @@
+/*
+ * interface.c
+ *
+ * Xen USB backend interface management.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/delay.h>
+#include "usbback.h"
+
+static LIST_HEAD(usbif_list);
+static DEFINE_SPINLOCK(usbif_list_lock);
+
+usbif_t *find_usbif(domid_t domid, unsigned int handle)
+{
+	usbif_t *usbif;
+	int found = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&usbif_list_lock, flags);
+	list_for_each_entry(usbif, &usbif_list, usbif_list) {
+		if (usbif->domid == domid
+			&& usbif->handle == handle) {
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&usbif_list_lock, flags);
+
+	if (found)
+		return usbif;
+
+	return NULL;
+}
+
+usbif_t *usbif_alloc(domid_t domid, unsigned int handle)
+{
+	usbif_t *usbif;
+	unsigned long flags;
+	int i;
+
+	usbif = kzalloc(sizeof(usbif_t), GFP_KERNEL);
+	if (!usbif)
+		return NULL;
+
+	usbif->domid = domid;
+	usbif->handle = handle;
+	spin_lock_init(&usbif->urb_ring_lock);
+	spin_lock_init(&usbif->conn_ring_lock);
+	atomic_set(&usbif->refcnt, 0);
+	init_waitqueue_head(&usbif->wq);
+	init_waitqueue_head(&usbif->waiting_to_free);
+	spin_lock_init(&usbif->stub_lock);
+	INIT_LIST_HEAD(&usbif->stub_list);
+	spin_lock_init(&usbif->addr_lock);
+	for (i = 0; i < USB_DEV_ADDR_SIZE; i++)
+		usbif->addr_table[i] = NULL;
+
+	spin_lock_irqsave(&usbif_list_lock, flags);
+	list_add(&usbif->usbif_list, &usbif_list);
+	spin_unlock_irqrestore(&usbif_list_lock, flags);
+
+	return usbif;
+}
+
+static int map_frontend_pages(usbif_t *usbif,
+				grant_ref_t urb_ring_ref,
+				grant_ref_t conn_ring_ref)
+{
+	struct gnttab_map_grant_ref op;
+
+	gnttab_set_map_op(&op, (unsigned long)usbif->urb_ring_area->addr,
+			  GNTMAP_host_map, urb_ring_ref, usbif->domid);
+
+
+	if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
+		BUG();
+
+	if (op.status) {
+		printk(KERN_ERR "grant table failure mapping urb_ring_ref\n");
+		return op.status;
+	}
+
+	usbif->urb_shmem_ref = urb_ring_ref;
+	usbif->urb_shmem_handle = op.handle;
+
+	gnttab_set_map_op(&op, (unsigned long)usbif->conn_ring_area->addr,
+			  GNTMAP_host_map, conn_ring_ref, usbif->domid);
+
+	if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
+		BUG();
+
+	if (op.status) {
+		struct gnttab_unmap_grant_ref unop;
+		gnttab_set_unmap_op(&unop,
+				(unsigned long) usbif->urb_ring_area->addr,
+				GNTMAP_host_map, usbif->urb_shmem_handle);
+		HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unop, 1);
+		printk(KERN_ERR "grant table failure mapping conn_ring_ref\n");
+		return op.status;
+	}
+
+	usbif->conn_shmem_ref = conn_ring_ref;
+	usbif->conn_shmem_handle = op.handle;
+
+	return 0;
+}
+
+static void unmap_frontend_pages(usbif_t *usbif)
+{
+	struct gnttab_unmap_grant_ref op;
+
+	gnttab_set_unmap_op(&op, (unsigned long)usbif->urb_ring_area->addr,
+			    GNTMAP_host_map, usbif->urb_shmem_handle);
+
+	if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
+		BUG();
+
+	gnttab_set_unmap_op(&op, (unsigned long)usbif->conn_ring_area->addr,
+			    GNTMAP_host_map, usbif->conn_shmem_handle);
+
+	if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
+		BUG();
+}
+
+int usbif_map(usbif_t *usbif, unsigned long urb_ring_ref,
+		unsigned long conn_ring_ref, unsigned int evtchn)
+{
+	int err = -ENOMEM;
+
+	struct usbif_urb_sring *urb_sring;
+	struct usbif_conn_sring *conn_sring;
+
+	if (usbif->irq)
+		return 0;
+
+	if ((usbif->urb_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
+		return err;
+	if ((usbif->conn_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
+		goto fail_alloc;
+
+	err = map_frontend_pages(usbif, urb_ring_ref, conn_ring_ref);
+	if (err)
+		goto fail_map;
+
+	err = bind_interdomain_evtchn_to_irqhandler(
+			usbif->domid, evtchn, usbbk_be_int, 0,
+			"usbif-backend", usbif);
+	if (err < 0)
+		goto fail_evtchn;
+	usbif->irq = err;
+
+	urb_sring = (struct usbif_urb_sring *) usbif->urb_ring_area->addr;
+	BACK_RING_INIT(&usbif->urb_ring, urb_sring, PAGE_SIZE);
+
+	conn_sring = (struct usbif_conn_sring *) usbif->conn_ring_area->addr;
+	BACK_RING_INIT(&usbif->conn_ring, conn_sring, PAGE_SIZE);
+
+	return 0;
+
+fail_evtchn:
+	unmap_frontend_pages(usbif);
+fail_map:
+	free_vm_area(usbif->conn_ring_area);
+fail_alloc:
+	free_vm_area(usbif->urb_ring_area);
+
+	return err;
+}
+
+void usbif_disconnect(usbif_t *usbif)
+{
+	struct usbstub *stub, *tmp;
+	unsigned long flags;
+
+	if (usbif->xenusbd) {
+		kthread_stop(usbif->xenusbd);
+		usbif->xenusbd = NULL;
+	}
+
+	spin_lock_irqsave(&usbif->stub_lock, flags);
+	list_for_each_entry_safe(stub, tmp, &usbif->stub_list, dev_list) {
+		usbbk_unlink_urbs(stub);
+		detach_device_without_lock(usbif, stub);
+	}
+	spin_unlock_irqrestore(&usbif->stub_lock, flags);
+
+	wait_event(usbif->waiting_to_free, atomic_read(&usbif->refcnt) == 0);
+
+	if (usbif->irq) {
+		unbind_from_irqhandler(usbif->irq, usbif);
+		usbif->irq = 0;
+	}
+
+	if (usbif->urb_ring.sring) {
+		unmap_frontend_pages(usbif);
+		free_vm_area(usbif->urb_ring_area);
+		free_vm_area(usbif->conn_ring_area);
+		usbif->urb_ring.sring = NULL;
+		usbif->conn_ring.sring = NULL;
+	}
+}
+
+void usbif_free(usbif_t *usbif)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&usbif_list_lock, flags);
+	list_del(&usbif->usbif_list);
+	spin_unlock_irqrestore(&usbif_list_lock, flags);
+	kfree(usbif);
+}
diff -purN drivers/xen/usbback/usbback.c drivers/xen/usbback/usbback.c
--- drivers/xen/usbback/usbback.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/usbback.c	2010-12-31 18:52:25.000000000 +0800
@@ -0,0 +1,1200 @@
+/*
+ * usbback.c
+ *
+ * Xen USB backend driver
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/mm.h>
+#include <xen/balloon.h>
+#include "usbback.h"
+
+#if 0
+#include "../../usb/core/hub.h"
+#endif
+
+int usbif_reqs = USBIF_BACK_MAX_PENDING_REQS;
+module_param_named(reqs, usbif_reqs, int, 0);
+MODULE_PARM_DESC(reqs, "Number of usbback requests to allocate");
+
+struct pending_req_segment {
+	uint16_t offset;
+	uint16_t length;
+};
+
+typedef struct {
+	usbif_t *usbif;
+
+	uint16_t id; /* request id */
+
+	struct usbstub *stub;
+	struct list_head urb_list;
+
+	/* urb */
+	struct urb *urb;
+	void *buffer;
+	dma_addr_t transfer_dma;
+	struct usb_ctrlrequest *setup;
+	dma_addr_t setup_dma;
+
+	/* request segments */
+	uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */
+	uint16_t nr_extra_segs; /* number of iso_frame_desc segments (ISO) */
+	struct pending_req_segment *seg;
+
+	struct list_head free_list;
+} pending_req_t;
+
+static pending_req_t *pending_reqs;
+static struct list_head pending_free;
+static DEFINE_SPINLOCK(pending_free_lock);
+static LIST_HEAD(pending_urb_free);
+static DEFINE_SPINLOCK(urb_free_lock);
+static DECLARE_WAIT_QUEUE_HEAD(pending_free_wq);
+
+#define USBBACK_INVALID_HANDLE (~0)
+
+static struct page **pending_pages;
+static grant_handle_t *pending_grant_handles;
+
+static inline int vaddr_pagenr(pending_req_t *req, int seg)
+{
+	return (req - pending_reqs) * USBIF_MAX_SEGMENTS_PER_REQUEST + seg;
+}
+
+static inline unsigned long vaddr(pending_req_t *req, int seg)
+{
+	unsigned long pfn = page_to_pfn(pending_pages[vaddr_pagenr(req, seg)]);
+	return (unsigned long)pfn_to_kaddr(pfn);
+}
+
+#define pending_handle(_req, _seg) \
+	(pending_grant_handles[vaddr_pagenr(_req, _seg)])
+
+static pending_req_t *alloc_req(void)
+{
+	pending_req_t *req = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pending_free_lock, flags);
+	if (!list_empty(&pending_free)) {
+		req = list_entry(pending_free.next, pending_req_t, free_list);
+		list_del(&req->free_list);
+	}
+	spin_unlock_irqrestore(&pending_free_lock, flags);
+	return req;
+}
+
+static void free_req(pending_req_t *req)
+{
+	unsigned long flags;
+	int was_empty;
+
+	spin_lock_irqsave(&pending_free_lock, flags);
+	was_empty = list_empty(&pending_free);
+	list_add(&req->free_list, &pending_free);
+	spin_unlock_irqrestore(&pending_free_lock, flags);
+	if (was_empty)
+		wake_up(&pending_free_wq);
+}
+
+static inline void add_req_to_submitting_list(struct usbstub *stub, pending_req_t *pending_req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&stub->submitting_lock, flags);
+	list_add_tail(&pending_req->urb_list, &stub->submitting_list);
+	spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+static inline void remove_req_from_submitting_list(struct usbstub *stub, pending_req_t *pending_req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&stub->submitting_lock, flags);
+	list_del_init(&pending_req->urb_list);
+	spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+void usbbk_unlink_urbs(struct usbstub *stub)
+{
+	pending_req_t *req, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&stub->submitting_lock, flags);
+	list_for_each_entry_safe(req, tmp, &stub->submitting_list, urb_list) {
+		usb_unlink_urb(req->urb);
+	}
+	spin_unlock_irqrestore(&stub->submitting_lock, flags);
+}
+
+static void fast_flush_area(pending_req_t *pending_req)
+{
+	struct gnttab_unmap_grant_ref unmap[USBIF_MAX_SEGMENTS_PER_REQUEST];
+	unsigned int i, nr_segs, invcount = 0;
+	grant_handle_t handle;
+	int ret;
+
+	nr_segs = pending_req->nr_buffer_segs + pending_req->nr_extra_segs;
+
+	if (nr_segs) {
+		for (i = 0; i < nr_segs; i++) {
+			handle = pending_handle(pending_req, i);
+			if (handle == USBBACK_INVALID_HANDLE)
+				continue;
+			gnttab_set_unmap_op(&unmap[invcount], vaddr(pending_req, i),
+					    GNTMAP_host_map, handle);
+			pending_handle(pending_req, i) = USBBACK_INVALID_HANDLE;
+			invcount++;
+		}
+
+		ret = HYPERVISOR_grant_table_op(
+			GNTTABOP_unmap_grant_ref, unmap, invcount);
+		BUG_ON(ret);
+
+		kfree(pending_req->seg);
+	}
+
+	return;
+}
+
+static void copy_buff_to_pages(void *buff, pending_req_t *pending_req,
+		int start, int nr_pages)
+{
+	unsigned long copied = 0;
+	int i;
+
+	for (i = start; i < start + nr_pages; i++) {
+		memcpy((void *) vaddr(pending_req, i) + pending_req->seg[i].offset,
+			buff + copied,
+			pending_req->seg[i].length);
+		copied += pending_req->seg[i].length;
+	}
+}
+
+static void copy_pages_to_buff(void *buff, pending_req_t *pending_req,
+		int start, int nr_pages)
+{
+	unsigned long copied = 0;
+	int i;
+
+	for (i = start; i < start + nr_pages; i++) {
+		memcpy(buff + copied,
+			(void *) vaddr(pending_req, i) + pending_req->seg[i].offset,
+			pending_req->seg[i].length);
+		copied += pending_req->seg[i].length;
+	}
+}
+
+static int usbbk_alloc_urb(usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+	int ret;
+
+	if (usb_pipeisoc(req->pipe))
+		pending_req->urb = usb_alloc_urb(req->u.isoc.number_of_packets, GFP_KERNEL);
+	else
+		pending_req->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pending_req->urb) {
+		printk(KERN_ERR "usbback: can't alloc urb\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	if (req->buffer_length) {
+		pending_req->buffer = usb_buffer_alloc(pending_req->stub->udev,
+				req->buffer_length, GFP_KERNEL,
+				&pending_req->transfer_dma);
+		if (!pending_req->buffer) {
+			printk(KERN_ERR "usbback: can't alloc urb buffer\n");
+			ret = -ENOMEM;
+			goto fail_free_urb;
+		}
+	}
+
+	if (usb_pipecontrol(req->pipe)) {
+		pending_req->setup = usb_buffer_alloc(pending_req->stub->udev,
+				sizeof(struct usb_ctrlrequest), GFP_KERNEL,
+				&pending_req->setup_dma);
+		if (!pending_req->setup) {
+			printk(KERN_ERR "usbback: can't alloc usb_ctrlrequest\n");
+			ret = -ENOMEM;
+			goto fail_free_buffer;
+		}
+	}
+
+	return 0;
+
+fail_free_buffer:
+	if (req->buffer_length)
+		usb_buffer_free(pending_req->stub->udev, req->buffer_length,
+				pending_req->buffer, pending_req->transfer_dma);
+fail_free_urb:
+	usb_free_urb(pending_req->urb);
+fail:
+	return ret;
+}
+
+static void usbbk_free_urb(struct urb *urb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb_free_lock, flags);
+	list_add(&urb->urb_list, &pending_urb_free);
+	spin_unlock_irqrestore(&urb_free_lock, flags);
+}
+
+static void _usbbk_free_urb(struct urb *urb)
+{
+	if (usb_pipecontrol(urb->pipe))
+		usb_buffer_free(urb->dev, sizeof(struct usb_ctrlrequest),
+				urb->setup_packet, urb->setup_dma);
+	if (urb->transfer_buffer_length)
+		usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+				urb->transfer_buffer, urb->transfer_dma);
+	barrier();
+	usb_free_urb(urb);
+}
+
+static void usbbk_free_urbs(void)
+{
+	unsigned long flags;
+	struct list_head tmp_list;
+
+	if (list_empty(&pending_urb_free))
+		return;
+
+	INIT_LIST_HEAD(&tmp_list);
+
+	spin_lock_irqsave(&urb_free_lock, flags);
+	list_splice_init(&pending_urb_free, &tmp_list);
+	spin_unlock_irqrestore(&urb_free_lock, flags);
+
+	while (!list_empty(&tmp_list)) {
+		struct urb *next_urb = list_first_entry(&tmp_list, struct urb,
+							urb_list);
+
+		list_del(&next_urb->urb_list);
+		_usbbk_free_urb(next_urb);
+	}
+}
+
+static void usbbk_notify_work(usbif_t *usbif)
+{
+	usbif->waiting_reqs = 1;
+	wake_up(&usbif->wq);
+}
+
+irqreturn_t usbbk_be_int(int irq, void *dev_id)
+{
+	usbbk_notify_work(dev_id);
+	return IRQ_HANDLED;
+}
+
+static void usbbk_do_response(pending_req_t *pending_req, int32_t status,
+					int32_t actual_length, int32_t error_count, uint16_t start_frame)
+{
+	usbif_t *usbif = pending_req->usbif;
+	usbif_urb_response_t *res;
+	unsigned long flags;
+	int notify;
+
+	spin_lock_irqsave(&usbif->urb_ring_lock, flags);
+	res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt);
+	res->id = pending_req->id;
+	res->status = status;
+	res->actual_length = actual_length;
+	res->error_count = error_count;
+	res->start_frame = start_frame;
+	usbif->urb_ring.rsp_prod_pvt++;
+	barrier();
+	RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify);
+	spin_unlock_irqrestore(&usbif->urb_ring_lock, flags);
+
+	if (notify)
+		notify_remote_via_irq(usbif->irq);
+}
+
+static void usbbk_urb_complete(struct urb *urb)
+{
+	pending_req_t *pending_req = (pending_req_t *)urb->context;
+
+	if (usb_pipein(urb->pipe) && urb->status == 0 && urb->actual_length > 0)
+		copy_buff_to_pages(pending_req->buffer, pending_req,
+					0, pending_req->nr_buffer_segs);
+
+	if (usb_pipeisoc(urb->pipe))
+		copy_buff_to_pages(&urb->iso_frame_desc[0], pending_req,
+					pending_req->nr_buffer_segs, pending_req->nr_extra_segs);
+
+	barrier();
+
+	fast_flush_area(pending_req);
+
+	usbbk_do_response(pending_req, urb->status, urb->actual_length,
+					urb->error_count, urb->start_frame);
+
+	remove_req_from_submitting_list(pending_req->stub, pending_req);
+
+	barrier();
+	usbbk_free_urb(urb);
+	usbif_put(pending_req->usbif);
+	free_req(pending_req);
+}
+
+static int usbbk_gnttab_map(usbif_t *usbif,
+			usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+	int i, ret;
+	unsigned int nr_segs;
+	uint32_t flags;
+	struct gnttab_map_grant_ref map[USBIF_MAX_SEGMENTS_PER_REQUEST];
+
+	nr_segs = pending_req->nr_buffer_segs + pending_req->nr_extra_segs;
+
+	if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) {
+		printk(KERN_ERR "Bad number of segments in request\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (nr_segs) {
+		pending_req->seg = kmalloc(sizeof(struct pending_req_segment)
+				* nr_segs, GFP_KERNEL);
+		if (!pending_req->seg) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		if (pending_req->nr_buffer_segs) {
+			flags = GNTMAP_host_map;
+			if (usb_pipeout(req->pipe))
+				flags |= GNTMAP_readonly;
+			for (i = 0; i < pending_req->nr_buffer_segs; i++)
+				gnttab_set_map_op(&map[i], vaddr(
+						pending_req, i), flags,
+						req->seg[i].gref,
+						usbif->domid);
+		}
+
+		if (pending_req->nr_extra_segs) {
+			flags = GNTMAP_host_map;
+			for (i = req->nr_buffer_segs; i < nr_segs; i++)
+				gnttab_set_map_op(&map[i], vaddr(
+						pending_req, i), flags,
+						req->seg[i].gref,
+						usbif->domid);
+		}
+
+		ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
+					map, nr_segs);
+		BUG_ON(ret);
+/*
+		for( i = 0; i < nr_segs; i++) {
+			ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
+							&map[i], 1);
+			BUG_ON(ret);
+		}
+*/
+		for (i = 0; i < nr_segs; i++) {
+			if (unlikely(map[i].status != 0)) {
+				printk(KERN_ERR "usbback: invalid buffer -- could not remap it\n");
+				map[i].handle = USBBACK_INVALID_HANDLE;
+				ret |= 1;
+			}
+
+			pending_handle(pending_req, i) = map[i].handle;
+
+			if (ret)
+				continue;
+
+			set_phys_to_machine(__pa(vaddr(
+				pending_req, i)) >> PAGE_SHIFT,
+				FOREIGN_FRAME(map[i].dev_bus_addr >> PAGE_SHIFT));
+
+			pending_req->seg[i].offset = req->seg[i].offset;
+			pending_req->seg[i].length = req->seg[i].length;
+
+			barrier();
+
+			if (pending_req->seg[i].offset >= PAGE_SIZE ||
+					pending_req->seg[i].length > PAGE_SIZE ||
+					pending_req->seg[i].offset + pending_req->seg[i].length > PAGE_SIZE)
+					ret |= 1;
+		}
+
+		if (ret)
+			goto fail_flush;
+	}
+
+	return 0;
+
+fail_flush:
+	fast_flush_area(pending_req);
+	ret = -ENOMEM;
+
+fail:
+	return ret;
+}
+
+static void usbbk_init_urb(usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+	unsigned int pipe;
+	struct usb_device *udev = pending_req->stub->udev;
+	struct urb *urb = pending_req->urb;
+
+	switch (usb_pipetype(req->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		if (usb_pipein(req->pipe))
+			pipe = usb_rcvisocpipe(udev, usb_pipeendpoint(req->pipe));
+		else
+			pipe = usb_sndisocpipe(udev, usb_pipeendpoint(req->pipe));
+
+		urb->dev = udev;
+		urb->pipe = pipe;
+		urb->transfer_flags = req->transfer_flags;
+		urb->transfer_flags |= URB_ISO_ASAP;
+		urb->transfer_buffer = pending_req->buffer;
+		urb->transfer_buffer_length = req->buffer_length;
+		urb->complete = usbbk_urb_complete;
+		urb->context = pending_req;
+		urb->interval = req->u.isoc.interval;
+		urb->start_frame = req->u.isoc.start_frame;
+		urb->number_of_packets = req->u.isoc.number_of_packets;
+
+		break;
+	case PIPE_INTERRUPT:
+		if (usb_pipein(req->pipe))
+			pipe = usb_rcvintpipe(udev, usb_pipeendpoint(req->pipe));
+		else
+			pipe = usb_sndintpipe(udev, usb_pipeendpoint(req->pipe));
+
+		usb_fill_int_urb(urb, udev, pipe,
+				pending_req->buffer, req->buffer_length,
+				usbbk_urb_complete,
+				pending_req, req->u.intr.interval);
+		/*
+		 * high speed interrupt endpoints use a logarithmic encoding of
+		 * the endpoint interval, and usb_fill_int_urb() initializes a
+		 * interrupt urb with the encoded interval value.
+		 *
+		 * req->u.intr.interval is the interval value that already
+		 * encoded in the frontend part, and the above usb_fill_int_urb()
+		 * initializes the urb->interval with double encoded value.
+		 *
+		 * so, simply overwrite the urb->interval with original value.
+		 */
+		urb->interval = req->u.intr.interval;
+		urb->transfer_flags = req->transfer_flags;
+
+		break;
+	case PIPE_CONTROL:
+		if (usb_pipein(req->pipe))
+			pipe = usb_rcvctrlpipe(udev, 0);
+		else
+			pipe = usb_sndctrlpipe(udev, 0);
+
+		usb_fill_control_urb(urb, udev, pipe,
+				(unsigned char *) pending_req->setup,
+				pending_req->buffer, req->buffer_length,
+				usbbk_urb_complete, pending_req);
+		memcpy(pending_req->setup, req->u.ctrl, 8);
+		urb->setup_dma = pending_req->setup_dma;
+		urb->transfer_flags = req->transfer_flags;
+		urb->transfer_flags |= URB_NO_SETUP_DMA_MAP;
+
+		break;
+	case PIPE_BULK:
+		if (usb_pipein(req->pipe))
+			pipe = usb_rcvbulkpipe(udev, usb_pipeendpoint(req->pipe));
+		else
+			pipe = usb_sndbulkpipe(udev, usb_pipeendpoint(req->pipe));
+
+		usb_fill_bulk_urb(urb, udev, pipe,
+				pending_req->buffer, req->buffer_length,
+				usbbk_urb_complete, pending_req);
+		urb->transfer_flags = req->transfer_flags;
+
+		break;
+	default:
+		break;
+	}
+
+	if (req->buffer_length) {
+		urb->transfer_dma = pending_req->transfer_dma;
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	}
+}
+
+struct set_interface_request {
+	pending_req_t *pending_req;
+	int interface;
+	int alternate;
+	struct work_struct work;
+};
+
+static void usbbk_set_interface_work(struct work_struct *arg)
+{
+	struct set_interface_request *req
+		= container_of(arg, struct set_interface_request, work);
+	pending_req_t *pending_req = req->pending_req;
+	struct usb_device *udev = req->pending_req->stub->udev;
+
+	int ret;
+
+	usb_lock_device(udev);
+	ret = usb_set_interface(udev, req->interface, req->alternate);
+	usb_unlock_device(udev);
+	usb_put_dev(udev);
+
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(pending_req->usbif);
+	free_req(pending_req);
+	kfree(req);
+}
+
+static int usbbk_set_interface(pending_req_t *pending_req, int interface, int alternate)
+{
+	struct set_interface_request *req;
+	struct usb_device *udev = pending_req->stub->udev;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+	req->pending_req = pending_req;
+	req->interface = interface;
+	req->alternate = alternate;
+	INIT_WORK(&req->work, usbbk_set_interface_work);
+	usb_get_dev(udev);
+	schedule_work(&req->work);
+	return 0;
+}
+
+struct clear_halt_request {
+	pending_req_t *pending_req;
+	int pipe;
+	struct work_struct work;
+};
+
+static void usbbk_clear_halt_work(struct work_struct *arg)
+{
+	struct clear_halt_request *req
+		= container_of(arg, struct clear_halt_request, work);
+	pending_req_t *pending_req = req->pending_req;
+	struct usb_device *udev = req->pending_req->stub->udev;
+	int ret;
+
+	usb_lock_device(udev);
+	ret = usb_clear_halt(req->pending_req->stub->udev, req->pipe);
+	usb_unlock_device(udev);
+	usb_put_dev(udev);
+
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(pending_req->usbif);
+	free_req(pending_req);
+	kfree(req);
+}
+
+static int usbbk_clear_halt(pending_req_t *pending_req, int pipe)
+{
+	struct clear_halt_request *req;
+	struct usb_device *udev = pending_req->stub->udev;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+	req->pending_req = pending_req;
+	req->pipe = pipe;
+	INIT_WORK(&req->work, usbbk_clear_halt_work);
+
+	usb_get_dev(udev);
+	schedule_work(&req->work);
+	return 0;
+}
+
+#if 0
+struct port_reset_request {
+	pending_req_t *pending_req;
+	struct work_struct work;
+};
+
+static void usbbk_port_reset_work(struct work_struct *arg)
+{
+	struct port_reset_request *req
+		= container_of(arg, struct port_reset_request, work);
+	pending_req_t *pending_req = req->pending_req;
+	struct usb_device *udev = pending_req->stub->udev;
+	int ret, ret_lock;
+
+	ret = ret_lock = usb_lock_device_for_reset(udev, NULL);
+	if (ret_lock >= 0) {
+		ret = usb_reset_device(udev);
+		if (ret_lock)
+			usb_unlock_device(udev);
+	}
+	usb_put_dev(udev);
+
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(pending_req->usbif);
+	free_req(pending_req);
+	kfree(req);
+}
+
+static int usbbk_port_reset(pending_req_t *pending_req)
+{
+	struct port_reset_request *req;
+	struct usb_device *udev = pending_req->stub->udev;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	req->pending_req = pending_req;
+	INIT_WORK(&req->work, usbbk_port_reset_work);
+
+	usb_get_dev(udev);
+	schedule_work(&req->work);
+	return 0;
+}
+#endif
+
+static void usbbk_set_address(usbif_t *usbif, struct usbstub *stub, int cur_addr, int new_addr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&usbif->addr_lock, flags);
+	if (cur_addr)
+		usbif->addr_table[cur_addr] = NULL;
+	if (new_addr)
+		usbif->addr_table[new_addr] = stub;
+	stub->addr = new_addr;
+	spin_unlock_irqrestore(&usbif->addr_lock, flags);
+}
+
+struct usbstub *find_attached_device(usbif_t *usbif, int portnum)
+{
+	struct usbstub *stub;
+	int found = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&usbif->stub_lock, flags);
+	list_for_each_entry(stub, &usbif->stub_list, dev_list) {
+		if (stub->portid->portnum == portnum) {
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&usbif->stub_lock, flags);
+
+	if (found)
+		return stub;
+
+	return NULL;
+}
+
+static void process_unlink_req(usbif_t *usbif,
+		usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+	pending_req_t *unlink_req = NULL;
+	int devnum;
+	int ret = 0;
+	unsigned long flags;
+
+	devnum = usb_pipedevice(req->pipe);
+	if (unlikely(devnum == 0)) {
+		pending_req->stub = find_attached_device(usbif, usbif_pipeportnum(req->pipe));
+		if (unlikely(!pending_req->stub)) {
+			ret = -ENODEV;
+			goto fail_response;
+		}
+	} else {
+		if (unlikely(!usbif->addr_table[devnum])) {
+			ret = -ENODEV;
+			goto fail_response;
+		}
+		pending_req->stub = usbif->addr_table[devnum];
+	}
+
+	spin_lock_irqsave(&pending_req->stub->submitting_lock, flags);
+	list_for_each_entry(unlink_req, &pending_req->stub->submitting_list, urb_list) {
+		if (unlink_req->id == req->u.unlink.unlink_id) {
+			ret = usb_unlink_urb(unlink_req->urb);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&pending_req->stub->submitting_lock, flags);
+
+fail_response:
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(usbif);
+	free_req(pending_req);
+	return;
+}
+
+static int check_and_submit_special_ctrlreq(usbif_t *usbif,
+		usbif_urb_request_t *req, pending_req_t *pending_req)
+{
+	int devnum;
+	struct usbstub *stub = NULL;
+	struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *) req->u.ctrl;
+	int ret;
+	int done = 0;
+
+	devnum = usb_pipedevice(req->pipe);
+
+	/*
+	 * When the device is first connected or reseted, USB device has no address.
+	 * In this initial state, following requests are send to device address (#0),
+	 *
+	 *  1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is send,
+	 *     and OS knows what device is connected to.
+	 *
+	 *  2. SET_ADDRESS is send, and then, device has its address.
+	 *
+	 * In the next step, SET_CONFIGURATION is send to addressed device, and then,
+	 * the device is finally ready to use.
+	 */
+	if (unlikely(devnum == 0)) {
+		stub = find_attached_device(usbif, usbif_pipeportnum(req->pipe));
+		if (unlikely(!stub)) {
+			ret = -ENODEV;
+			goto fail_response;
+		}
+
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_DESCRIPTOR:
+			/*
+			 * GET_DESCRIPTOR request to device #0.
+			 * through to normal urb transfer.
+			 */
+			pending_req->stub = stub;
+			return 0;
+			break;
+		case USB_REQ_SET_ADDRESS:
+			/*
+			 * SET_ADDRESS request to device #0.
+			 * add attached device to addr_table.
+			 */
+			{
+				__u16 addr = le16_to_cpu(ctrl->wValue);
+				usbbk_set_address(usbif, stub, 0, addr);
+			}
+			ret = 0;
+			goto fail_response;
+			break;
+		default:
+			ret = -EINVAL;
+			goto fail_response;
+		}
+	} else {
+		if (unlikely(!usbif->addr_table[devnum])) {
+			ret = -ENODEV;
+			goto fail_response;
+		}
+		pending_req->stub = usbif->addr_table[devnum];
+	}
+
+	/*
+	 * Check special request
+	 */
+	switch (ctrl->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		/*
+		 * SET_ADDRESS request to addressed device.
+		 * change addr or remove from addr_table.
+		 */
+		{
+			__u16 addr = le16_to_cpu(ctrl->wValue);
+			usbbk_set_address(usbif, stub, devnum, addr);
+		}
+		ret = 0;
+		goto fail_response;
+		break;
+#if 0
+	case USB_REQ_SET_CONFIGURATION:
+		/*
+		 * linux 2.6.27 or later version only!
+		 */
+		if (ctrl->RequestType == USB_RECIP_DEVICE) {
+			__u16 config = le16_to_cpu(ctrl->wValue);
+			usb_driver_set_configuration(pending_req->stub->udev, config);
+			done = 1;
+		}
+		break;
+#endif
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType == USB_RECIP_INTERFACE) {
+			__u16 alt = le16_to_cpu(ctrl->wValue);
+			__u16 intf = le16_to_cpu(ctrl->wIndex);
+			usbbk_set_interface(pending_req, intf, alt);
+			done = 1;
+		}
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		if (ctrl->bRequestType == USB_RECIP_ENDPOINT
+			&& ctrl->wValue == USB_ENDPOINT_HALT) {
+			int pipe;
+			int ep = le16_to_cpu(ctrl->wIndex) & 0x0f;
+			int dir = le16_to_cpu(ctrl->wIndex)
+					& USB_DIR_IN;
+			if (dir)
+				pipe = usb_rcvctrlpipe(pending_req->stub->udev, ep);
+			else
+				pipe = usb_sndctrlpipe(pending_req->stub->udev, ep);
+			usbbk_clear_halt(pending_req, pipe);
+			done = 1;
+		}
+		break;
+#if 0 /* not tested yet */
+	case USB_REQ_SET_FEATURE:
+		if (ctrl->bRequestType == USB_RT_PORT) {
+			__u16 feat = le16_to_cpu(ctrl->wValue);
+			if (feat == USB_PORT_FEAT_RESET) {
+				usbbk_port_reset(pending_req);
+				done = 1;
+			}
+		}
+		break;
+#endif
+	default:
+		break;
+	}
+
+	return done;
+
+fail_response:
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(usbif);
+	free_req(pending_req);
+	return 1;
+}
+
+static void dispatch_request_to_pending_reqs(usbif_t *usbif,
+		usbif_urb_request_t *req,
+		pending_req_t *pending_req)
+{
+	int ret;
+
+	pending_req->id = req->id;
+	pending_req->usbif = usbif;
+
+	barrier();
+
+	usbif_get(usbif);
+
+	/* unlink request */
+	if (unlikely(usbif_pipeunlink(req->pipe))) {
+		process_unlink_req(usbif, req, pending_req);
+		return;
+	}
+
+	if (usb_pipecontrol(req->pipe)) {
+		if (check_and_submit_special_ctrlreq(usbif, req, pending_req))
+			return;
+	} else {
+		int devnum = usb_pipedevice(req->pipe);
+		if (unlikely(!usbif->addr_table[devnum])) {
+			ret = -ENODEV;
+			goto fail_response;
+		}
+		pending_req->stub = usbif->addr_table[devnum];
+	}
+
+	barrier();
+
+	ret = usbbk_alloc_urb(req, pending_req);
+	if (ret) {
+		ret = -ESHUTDOWN;
+		goto fail_response;
+	}
+
+	add_req_to_submitting_list(pending_req->stub, pending_req);
+
+	barrier();
+
+	usbbk_init_urb(req, pending_req);
+
+	barrier();
+
+	pending_req->nr_buffer_segs = req->nr_buffer_segs;
+	if (usb_pipeisoc(req->pipe))
+		pending_req->nr_extra_segs = req->u.isoc.nr_frame_desc_segs;
+	else
+		pending_req->nr_extra_segs = 0;
+
+	barrier();
+
+	ret = usbbk_gnttab_map(usbif, req, pending_req);
+	if (ret) {
+		printk(KERN_ERR "usbback: invalid buffer\n");
+		ret = -ESHUTDOWN;
+		goto fail_free_urb;
+	}
+
+	barrier();
+
+	if (usb_pipeout(req->pipe) && req->buffer_length)
+		copy_pages_to_buff(pending_req->buffer,
+					pending_req,
+					0,
+					pending_req->nr_buffer_segs);
+	if (usb_pipeisoc(req->pipe)) {
+		copy_pages_to_buff(&pending_req->urb->iso_frame_desc[0],
+			pending_req,
+			pending_req->nr_buffer_segs,
+			pending_req->nr_extra_segs);
+	}
+
+	barrier();
+
+	ret = usb_submit_urb(pending_req->urb, GFP_KERNEL);
+	if (ret) {
+		printk(KERN_ERR "usbback: failed submitting urb, error %d\n", ret);
+		ret = -ESHUTDOWN;
+		goto fail_flush_area;
+	}
+	return;
+
+fail_flush_area:
+	fast_flush_area(pending_req);
+fail_free_urb:
+	remove_req_from_submitting_list(pending_req->stub, pending_req);
+	barrier();
+	usbbk_free_urb(pending_req->urb);
+fail_response:
+	usbbk_do_response(pending_req, ret, 0, 0, 0);
+	usbif_put(usbif);
+	free_req(pending_req);
+}
+
+static int usbbk_start_submit_urb(usbif_t *usbif)
+{
+	struct usbif_urb_back_ring *urb_ring = &usbif->urb_ring;
+	usbif_urb_request_t *req;
+	pending_req_t *pending_req;
+	RING_IDX rc, rp;
+	int more_to_do = 0;
+
+	rc = urb_ring->req_cons;
+	rp = urb_ring->sring->req_prod;
+	rmb();
+
+	while (rc != rp) {
+		if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) {
+			printk(KERN_WARNING "RING_REQUEST_CONS_OVERFLOW\n");
+			break;
+		}
+
+		pending_req = alloc_req();
+		if (NULL == pending_req) {
+			more_to_do = 1;
+			break;
+		}
+
+		req = RING_GET_REQUEST(urb_ring, rc);
+		urb_ring->req_cons = ++rc;
+
+		dispatch_request_to_pending_reqs(usbif, req,
+							pending_req);
+	}
+
+	RING_FINAL_CHECK_FOR_REQUESTS(&usbif->urb_ring, more_to_do);
+
+	cond_resched();
+
+	return more_to_do;
+}
+
+void usbbk_hotplug_notify(usbif_t *usbif, int portnum, int speed)
+{
+	struct usbif_conn_back_ring *ring = &usbif->conn_ring;
+	usbif_conn_request_t *req;
+	usbif_conn_response_t *res;
+	unsigned long flags;
+	u16 id;
+	int notify;
+
+	spin_lock_irqsave(&usbif->conn_ring_lock, flags);
+
+	req = RING_GET_REQUEST(ring, ring->req_cons);;
+	id = req->id;
+	ring->req_cons++;
+	ring->sring->req_event = ring->req_cons + 1;
+
+	res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt);
+	res->id = id;
+	res->portnum = portnum;
+	res->speed = speed;
+	ring->rsp_prod_pvt++;
+	RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
+
+	spin_unlock_irqrestore(&usbif->conn_ring_lock, flags);
+
+	if (notify)
+		notify_remote_via_irq(usbif->irq);
+}
+
+int usbbk_schedule(void *arg)
+{
+	usbif_t *usbif = (usbif_t *) arg;
+
+	usbif_get(usbif);
+
+	while (!kthread_should_stop()) {
+		wait_event_interruptible(
+			usbif->wq,
+			usbif->waiting_reqs || kthread_should_stop());
+		wait_event_interruptible(
+			pending_free_wq,
+			!list_empty(&pending_free) || kthread_should_stop());
+		usbif->waiting_reqs = 0;
+		smp_mb();
+
+		if (usbbk_start_submit_urb(usbif))
+			usbif->waiting_reqs = 1;
+
+		usbbk_free_urbs();
+	}
+
+	usbbk_free_urbs();
+	usbif->xenusbd = NULL;
+	usbif_put(usbif);
+
+	return 0;
+}
+
+/*
+ * attach usbstub device to usbif.
+ */
+void usbbk_attach_device(usbif_t *usbif, struct usbstub *stub)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&usbif->stub_lock, flags);
+	list_add(&stub->dev_list, &usbif->stub_list);
+	spin_unlock_irqrestore(&usbif->stub_lock, flags);
+	stub->usbif = usbif;
+}
+
+/*
+ * detach usbstub device from usbif.
+ */
+void usbbk_detach_device(usbif_t *usbif, struct usbstub *stub)
+{
+	unsigned long flags;
+
+	if (stub->addr)
+		usbbk_set_address(usbif, stub, stub->addr, 0);
+	spin_lock_irqsave(&usbif->stub_lock, flags);
+	list_del(&stub->dev_list);
+	spin_unlock_irqrestore(&usbif->stub_lock, flags);
+	stub->usbif = NULL;
+}
+
+void detach_device_without_lock(usbif_t *usbif, struct usbstub *stub)
+{
+	if (stub->addr)
+		usbbk_set_address(usbif, stub, stub->addr, 0);
+	list_del(&stub->dev_list);
+	stub->usbif = NULL;
+}
+
+static int __init usbback_init(void)
+{
+	int i, mmap_pages;
+	int err = 0;
+
+	if (!xen_domain())
+		return -ENODEV;
+
+	mmap_pages = usbif_reqs * USBIF_MAX_SEGMENTS_PER_REQUEST;
+	pending_reqs = kzalloc(sizeof(pending_reqs[0]) *
+			usbif_reqs, GFP_KERNEL);
+	pending_grant_handles = kmalloc(sizeof(pending_grant_handles[0]) *
+			mmap_pages, GFP_KERNEL);
+	pending_pages = alloc_empty_pages_and_pagevec(mmap_pages);
+
+	if (!pending_reqs || !pending_grant_handles || !pending_pages) {
+		err = -ENOMEM;
+		goto out_mem;
+	}
+
+	for (i = 0; i < mmap_pages; i++)
+		pending_grant_handles[i] = USBBACK_INVALID_HANDLE;
+
+	INIT_LIST_HEAD(&pending_free);
+
+	for (i = 0; i < usbif_reqs; i++)
+		list_add_tail(&pending_reqs[i].free_list, &pending_free);
+
+	err = usbstub_init();
+	if (err)
+		goto out_mem;
+
+	err = usbback_xenbus_init();
+	if (err)
+		goto out_xenbus;
+
+	return 0;
+
+out_xenbus:
+	usbstub_exit();
+out_mem:
+	kfree(pending_reqs);
+	kfree(pending_grant_handles);
+	free_empty_pages_and_pagevec(pending_pages, mmap_pages);
+	return err;
+}
+
+static void __exit usbback_exit(void)
+{
+	usbback_xenbus_exit();
+	usbstub_exit();
+	kfree(pending_reqs);
+	kfree(pending_grant_handles);
+	free_empty_pages_and_pagevec(pending_pages, usbif_reqs * USBIF_MAX_SEGMENTS_PER_REQUEST);
+}
+
+module_init(usbback_init);
+module_exit(usbback_exit);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Xen USB backend driver (usbback)");
+MODULE_LICENSE("Dual BSD/GPL");
diff -purN drivers/xen/usbback/usbback.h drivers/xen/usbback/usbback.h
--- drivers/xen/usbback/usbback.h	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/usbback.h	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,182 @@
+/*
+ * usbback.h
+ *
+ * This file is part of Xen USB backend driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_USBBACK_H__
+#define __XEN_USBBACK_H__
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <asm/hypervisor.h>
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/evtchn.h>
+#include <xen/interface/xen.h>
+#include <xen/xenbus.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/io/usbif.h>
+
+struct usbstub;
+
+#ifndef BUS_ID_SIZE
+#define USBBACK_BUS_ID_SIZE 20
+#else
+#define USBBACK_BUS_ID_SIZE BUS_ID_SIZE
+#endif
+
+#define USB_DEV_ADDR_SIZE 128
+
+typedef struct usbif_st {
+	domid_t domid;
+	unsigned int handle;
+	int num_ports;
+	enum usb_spec_version usb_ver;
+
+	struct xenbus_device *xbdev;
+	struct list_head usbif_list;
+
+	unsigned int      irq;
+
+	struct usbif_urb_back_ring urb_ring;
+	struct usbif_conn_back_ring conn_ring;
+	struct vm_struct *urb_ring_area;
+	struct vm_struct *conn_ring_area;
+
+	spinlock_t urb_ring_lock;
+	spinlock_t conn_ring_lock;
+	atomic_t refcnt;
+
+	grant_handle_t urb_shmem_handle;
+	grant_ref_t urb_shmem_ref;
+	grant_handle_t conn_shmem_handle;
+	grant_ref_t conn_shmem_ref;
+
+	struct xenbus_watch backend_watch;
+
+	/* device address lookup table */
+	struct usbstub *addr_table[USB_DEV_ADDR_SIZE];
+	spinlock_t addr_lock;
+
+	/* connected device list */
+	struct list_head stub_list;
+	spinlock_t stub_lock;
+
+	/* request schedule */
+	struct task_struct *xenusbd;
+	unsigned int waiting_reqs;
+	wait_queue_head_t waiting_to_free;
+	wait_queue_head_t wq;
+} usbif_t;
+
+struct vusb_port_id {
+	struct list_head id_list;
+
+	char phys_bus[USBBACK_BUS_ID_SIZE];
+	domid_t domid;
+	unsigned int handle;
+	int portnum;
+	unsigned is_connected:1;
+};
+
+struct usbstub {
+	struct kref kref;
+	struct list_head dev_list;
+
+	struct vusb_port_id *portid;
+	struct usb_device *udev;
+	usbif_t *usbif;
+	int addr;
+
+	struct list_head submitting_list;
+	spinlock_t submitting_lock;
+};
+
+usbif_t *usbif_alloc(domid_t domid, unsigned int handle);
+void usbif_disconnect(usbif_t *usbif);
+void usbif_free(usbif_t *usbif);
+int usbif_map(usbif_t *usbif, unsigned long urb_ring_ref,
+		unsigned long conn_ring_ref, unsigned int evtchn);
+
+#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
+#define usbif_put(_b) \
+	do { \
+		if (atomic_dec_and_test(&(_b)->refcnt)) \
+			wake_up(&(_b)->waiting_to_free); \
+	} while (0)
+
+usbif_t *find_usbif(domid_t domid, unsigned int handle);
+int usbback_xenbus_init(void);
+void usbback_xenbus_exit(void);
+struct vusb_port_id *find_portid_by_busid(const char *busid);
+struct vusb_port_id *find_portid(const domid_t domid,
+						const unsigned int handle,
+						const int portnum);
+int portid_add(const char *busid,
+					const domid_t domid,
+					const unsigned int handle,
+					const int portnum);
+int portid_remove(const domid_t domid,
+					const unsigned int handle,
+					const int portnum);
+irqreturn_t usbbk_be_int(int irq, void *dev_id);
+int usbbk_schedule(void *arg);
+struct usbstub *find_attached_device(usbif_t *usbif, int port);
+void usbbk_attach_device(usbif_t *usbif, struct usbstub *stub);
+void usbbk_detach_device(usbif_t *usbif, struct usbstub *stub);
+void usbbk_hotplug_notify(usbif_t *usbif, int portnum, int speed);
+void detach_device_without_lock(usbif_t *usbif, struct usbstub *stub);
+void usbbk_unlink_urbs(struct usbstub *stub);
+
+int usbstub_init(void);
+void usbstub_exit(void);
+
+#endif /* __XEN_USBBACK_H__ */
diff -purN drivers/xen/usbback/usbstub.c drivers/xen/usbback/usbstub.c
--- drivers/xen/usbback/usbstub.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/usbstub.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,325 @@
+/*
+ * usbstub.c
+ *
+ * USB stub driver - grabbing and managing USB devices.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbback.h"
+
+static LIST_HEAD(port_list);
+static DEFINE_SPINLOCK(port_list_lock);
+
+struct vusb_port_id *find_portid_by_busid(const char *busid)
+{
+	struct vusb_port_id *portid;
+	int found = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_list_lock, flags);
+	list_for_each_entry(portid, &port_list, id_list) {
+		if (!(strncmp(portid->phys_bus, busid, USBBACK_BUS_ID_SIZE))) {
+			found = 1;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&port_list_lock, flags);
+
+	if (found)
+		return portid;
+
+	return NULL;
+}
+
+struct vusb_port_id *find_portid(const domid_t domid,
+						const unsigned int handle,
+						const int portnum)
+{
+	struct vusb_port_id *portid;
+	int found = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_list_lock, flags);
+	list_for_each_entry(portid, &port_list, id_list) {
+		if ((portid->domid == domid)
+				&& (portid->handle == handle)
+				&& (portid->portnum == portnum)) {
+				found = 1;
+				break;
+		}
+	}
+	spin_unlock_irqrestore(&port_list_lock, flags);
+
+	if (found)
+		return portid;
+
+	return NULL;
+}
+
+int portid_add(const char *busid,
+					const domid_t domid,
+					const unsigned int handle,
+					const int portnum)
+{
+	struct vusb_port_id *portid;
+	unsigned long flags;
+
+	portid = kzalloc(sizeof(*portid), GFP_KERNEL);
+	if (!portid)
+		return -ENOMEM;
+
+	portid->domid = domid;
+	portid->handle = handle;
+	portid->portnum = portnum;
+
+	strncpy(portid->phys_bus, busid, USBBACK_BUS_ID_SIZE);
+
+	spin_lock_irqsave(&port_list_lock, flags);
+	list_add(&portid->id_list, &port_list);
+	spin_unlock_irqrestore(&port_list_lock, flags);
+
+	return 0;
+}
+
+int portid_remove(const domid_t domid,
+					const unsigned int handle,
+					const int portnum)
+{
+	struct vusb_port_id *portid, *tmp;
+	int err = -ENOENT;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_list_lock, flags);
+	list_for_each_entry_safe(portid, tmp, &port_list, id_list) {
+		if (portid->domid == domid
+				&& portid->handle == handle
+				&& portid->portnum == portnum) {
+			list_del(&portid->id_list);
+			kfree(portid);
+
+			err = 0;
+		}
+	}
+	spin_unlock_irqrestore(&port_list_lock, flags);
+
+	return err;
+}
+
+static struct usbstub *usbstub_alloc(struct usb_device *udev,
+						struct vusb_port_id *portid)
+{
+	struct usbstub *stub;
+
+	stub = kzalloc(sizeof(*stub), GFP_KERNEL);
+	if (!stub) {
+		printk(KERN_ERR "no memory for alloc usbstub\n");
+		return NULL;
+	}
+	kref_init(&stub->kref);
+	stub->udev = usb_get_dev(udev);
+	stub->portid = portid;
+	spin_lock_init(&stub->submitting_lock);
+	INIT_LIST_HEAD(&stub->submitting_list);
+
+	return stub;
+}
+
+static void usbstub_release(struct kref *kref)
+{
+	struct usbstub *stub;
+
+	stub = container_of(kref, struct usbstub, kref);
+
+	usb_put_dev(stub->udev);
+	stub->udev = NULL;
+	stub->portid = NULL;
+	kfree(stub);
+}
+
+static inline void usbstub_get(struct usbstub *stub)
+{
+	kref_get(&stub->kref);
+}
+
+static inline void usbstub_put(struct usbstub *stub)
+{
+	kref_put(&stub->kref, usbstub_release);
+}
+
+static int usbstub_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	const char *busid = dev_name(intf->dev.parent);
+	struct vusb_port_id *portid = NULL;
+	struct usbstub *stub = NULL;
+	usbif_t *usbif = NULL;
+	int retval = -ENODEV;
+
+	/* hub currently not supported, so skip. */
+	if (udev->descriptor.bDeviceClass ==  USB_CLASS_HUB)
+		goto out;
+
+	portid = find_portid_by_busid(busid);
+	if (!portid)
+		goto out;
+
+	usbif = find_usbif(portid->domid, portid->handle);
+	if (!usbif)
+		goto out;
+
+	switch (udev->speed) {
+	case USB_SPEED_LOW:
+	case USB_SPEED_FULL:
+		break;
+	case USB_SPEED_HIGH:
+		if (usbif->usb_ver >= USB_VER_USB20)
+			break;
+		/* fall through */
+	default:
+		goto out;
+	}
+
+	stub = find_attached_device(usbif, portid->portnum);
+	if (!stub) {
+		/* new connection */
+		stub = usbstub_alloc(udev, portid);
+		if (!stub)
+			return -ENOMEM;
+		usbbk_attach_device(usbif, stub);
+		usbbk_hotplug_notify(usbif, portid->portnum, udev->speed);
+	} else {
+		/* maybe already called and connected by other intf */
+		if (strncmp(stub->portid->phys_bus, busid, USBBACK_BUS_ID_SIZE))
+			goto out; /* invalid call */
+	}
+
+	usbstub_get(stub);
+	usb_set_intfdata(intf, stub);
+	retval = 0;
+
+out:
+	return retval;
+}
+
+static void usbstub_disconnect(struct usb_interface *intf)
+{
+	struct usbstub *stub
+		= (struct usbstub *) usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (!stub)
+		return;
+
+	if (stub->usbif) {
+		usbbk_hotplug_notify(stub->usbif, stub->portid->portnum, 0);
+		usbbk_detach_device(stub->usbif, stub);
+	}
+	usbbk_unlink_urbs(stub);
+	usbstub_put(stub);
+}
+
+static ssize_t usbstub_show_portids(struct device_driver *driver,
+		char *buf)
+{
+	struct vusb_port_id *portid;
+	size_t count = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_list_lock, flags);
+	list_for_each_entry(portid, &port_list, id_list) {
+		if (count >= PAGE_SIZE)
+			break;
+		count += scnprintf((char *)buf + count, PAGE_SIZE - count,
+				"%s:%d:%d:%d\n",
+				&portid->phys_bus[0],
+				portid->domid,
+				portid->handle,
+				portid->portnum);
+	}
+	spin_unlock_irqrestore(&port_list_lock, flags);
+
+	return count;
+}
+
+DRIVER_ATTR(port_ids, S_IRUSR, usbstub_show_portids, NULL);
+
+/* table of devices that matches any usbdevice */
+static const struct usb_device_id usbstub_table[] = {
+		{ .driver_info = 1 }, /* wildcard, see usb_match_id() */
+		{ } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, usbstub_table);
+
+static struct usb_driver usbback_usb_driver = {
+		.name = "usbback",
+		.probe = usbstub_probe,
+		.disconnect = usbstub_disconnect,
+		.id_table = usbstub_table,
+		.no_dynamic_id = 1,
+};
+
+int __init usbstub_init(void)
+{
+	int err;
+
+	err = usb_register(&usbback_usb_driver);
+	if (err < 0) {
+		printk(KERN_ERR "usbback: usb_register failed (error %d)\n", err);
+		goto out;
+	}
+
+	err = driver_create_file(&usbback_usb_driver.drvwrap.driver,
+				&driver_attr_port_ids);
+	if (err)
+		usb_deregister(&usbback_usb_driver);
+
+out:
+	return err;
+}
+
+void usbstub_exit(void)
+{
+	driver_remove_file(&usbback_usb_driver.drvwrap.driver,
+				&driver_attr_port_ids);
+	usb_deregister(&usbback_usb_driver);
+}
diff -purN drivers/xen/usbback/xenbus.c drivers/xen/usbback/xenbus.c
--- drivers/xen/usbback/xenbus.c	1970-01-01 08:00:00.000000000 +0800
+++ drivers/xen/usbback/xenbus.c	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,337 @@
+/*
+ * xenbus.c
+ *
+ * Xenbus interface for USB backend driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * This program 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 program 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, see <http://www.gnu.org/licenses/>.
+ *
+ * or, by your choice,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbback.h"
+
+static int start_xenusbd(usbif_t *usbif)
+{
+	int err = 0;
+	char name[TASK_COMM_LEN];
+
+	snprintf(name, TASK_COMM_LEN, "usbback.%d.%d", usbif->domid,
+			usbif->handle);
+	usbif->xenusbd = kthread_run(usbbk_schedule, usbif, name);
+	if (IS_ERR(usbif->xenusbd)) {
+		err = PTR_ERR(usbif->xenusbd);
+		usbif->xenusbd = NULL;
+		xenbus_dev_error(usbif->xbdev, err, "start xenusbd");
+	}
+
+	return err;
+}
+
+static void backend_changed(struct xenbus_watch *watch,
+			const char **vec, unsigned int len)
+{
+	struct xenbus_transaction xbt;
+	int err;
+	int i;
+	char node[8];
+	char *busid;
+	struct vusb_port_id *portid = NULL;
+
+	usbif_t *usbif = container_of(watch, usbif_t, backend_watch);
+	struct xenbus_device *dev = usbif->xbdev;
+
+again:
+	err = xenbus_transaction_start(&xbt);
+	if (err) {
+		xenbus_dev_fatal(dev, err, "starting transaction");
+		return;
+	}
+
+	for (i = 1; i <= usbif->num_ports; i++) {
+		sprintf(node, "port/%d", i);
+		busid = xenbus_read(xbt, dev->nodename, node, NULL);
+		if (IS_ERR(busid)) {
+			err = PTR_ERR(busid);
+			xenbus_dev_fatal(dev, err, "reading port/%d", i);
+			goto abort;
+		}
+
+		/*
+		 * remove portid, if the port is not connected,
+		 */
+		if (strlen(busid) == 0) {
+			portid = find_portid(usbif->domid, usbif->handle, i);
+			if (portid) {
+				if (portid->is_connected)
+					xenbus_dev_fatal(dev, err,
+						"can't remove port/%d, unbind first", i);
+				else
+					portid_remove(usbif->domid, usbif->handle, i);
+			}
+			continue; /* never configured, ignore */
+		}
+
+		/*
+		 * add portid,
+		 * if the port is not configured and not used from other usbif.
+		 */
+		portid = find_portid(usbif->domid, usbif->handle, i);
+		if (portid) {
+			if ((strncmp(portid->phys_bus, busid, USBBACK_BUS_ID_SIZE)))
+				xenbus_dev_fatal(dev, err,
+					"can't add port/%d, remove first", i);
+			else
+				continue; /* already configured, ignore */
+		} else {
+			if (find_portid_by_busid(busid))
+				xenbus_dev_fatal(dev, err,
+					"can't add port/%d, busid already used", i);
+			else
+				portid_add(busid, usbif->domid, usbif->handle, i);
+		}
+	}
+
+	err = xenbus_transaction_end(xbt, 0);
+	if (err == -EAGAIN)
+		goto again;
+	if (err)
+		xenbus_dev_fatal(dev, err, "completing transaction");
+
+	return;
+
+abort:
+	xenbus_transaction_end(xbt, 1);
+
+	return;
+}
+
+static int usbback_remove(struct xenbus_device *dev)
+{
+	usbif_t *usbif = dev_get_drvdata(&dev->dev);
+	int i;
+
+	if (usbif->backend_watch.node) {
+		unregister_xenbus_watch(&usbif->backend_watch);
+		kfree(usbif->backend_watch.node);
+		usbif->backend_watch.node = NULL;
+	}
+
+	if (usbif) {
+		/* remove all ports */
+		for (i = 1; i <= usbif->num_ports; i++)
+			portid_remove(usbif->domid, usbif->handle, i);
+		usbif_disconnect(usbif);
+		usbif_free(usbif);;
+	}
+	dev_set_drvdata(&dev->dev, NULL);
+
+	return 0;
+}
+
+static int usbback_probe(struct xenbus_device *dev,
+			  const struct xenbus_device_id *id)
+{
+	usbif_t *usbif;
+	unsigned int handle;
+	int num_ports;
+	int usb_ver;
+	int err;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	handle = simple_strtoul(strrchr(dev->otherend, '/') + 1, NULL, 0);
+	usbif = usbif_alloc(dev->otherend_id, handle);
+	if (!usbif) {
+		xenbus_dev_fatal(dev, -ENOMEM, "allocating backend interface");
+		return -ENOMEM;
+	}
+	usbif->xbdev = dev;
+	dev_set_drvdata(&dev->dev, usbif);
+
+	err = xenbus_scanf(XBT_NIL, dev->nodename,
+				"num-ports", "%d", &num_ports);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, err, "reading num-ports");
+		goto fail;
+	}
+	if (num_ports < 1 || num_ports > USB_MAXCHILDREN) {
+		xenbus_dev_fatal(dev, err, "invalid num-ports");
+		goto fail;
+	}
+	usbif->num_ports = num_ports;
+
+	err = xenbus_scanf(XBT_NIL, dev->nodename,
+				"usb-ver", "%d", &usb_ver);
+	if (err != 1) {
+		xenbus_dev_fatal(dev, err, "reading usb-ver");
+		goto fail;
+	}
+	switch (usb_ver) {
+	case USB_VER_USB11:
+	case USB_VER_USB20:
+		usbif->usb_ver = usb_ver;
+		break;
+	default:
+		xenbus_dev_fatal(dev, err, "invalid usb-ver");
+		goto fail;
+	}
+
+	err = xenbus_switch_state(dev, XenbusStateInitWait);
+	if (err)
+		goto fail;
+
+	return 0;
+
+fail:
+	usbback_remove(dev);
+	return err;
+}
+
+static int connect_rings(usbif_t *usbif)
+{
+	struct xenbus_device *dev = usbif->xbdev;
+	unsigned long urb_ring_ref;
+	unsigned long conn_ring_ref;
+	unsigned int evtchn;
+	int err;
+
+	err = xenbus_gather(XBT_NIL, dev->otherend,
+			    "urb-ring-ref", "%lu", &urb_ring_ref,
+			    "conn-ring-ref", "%lu", &conn_ring_ref,
+			    "event-channel", "%u", &evtchn, NULL);
+	if (err) {
+		xenbus_dev_fatal(dev, err,
+				 "reading %s/ring-ref and event-channel",
+				 dev->otherend);
+		return err;
+	}
+
+	printk("usbback: urb-ring-ref %ld, conn-ring-ref %ld, event-channel %d\n",
+	       urb_ring_ref, conn_ring_ref, evtchn);
+
+	err = usbif_map(usbif, urb_ring_ref, conn_ring_ref, evtchn);
+	if (err) {
+		xenbus_dev_fatal(dev, err,
+				"mapping urb-ring-ref %lu conn-ring-ref %lu port %u",
+				urb_ring_ref, conn_ring_ref, evtchn);
+		return err;
+	}
+
+	return 0;
+}
+
+static void frontend_changed(struct xenbus_device *dev,
+				     enum xenbus_state frontend_state)
+{
+	usbif_t *usbif = dev_get_drvdata(&dev->dev);
+	int err;
+
+	switch (frontend_state) {
+	case XenbusStateInitialised:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+		break;
+
+	case XenbusStateInitialising:
+		if (dev->state == XenbusStateClosed) {
+			printk("%s: %s: prepare for reconnect\n",
+			       __FUNCTION__, dev->nodename);
+			xenbus_switch_state(dev, XenbusStateInitWait);
+		}
+		break;
+
+	case XenbusStateConnected:
+		if (dev->state == XenbusStateConnected)
+			break;
+		err = connect_rings(usbif);
+		if (err)
+			break;
+		err = start_xenusbd(usbif);
+		if (err)
+			break;
+		err = xenbus_watch_pathfmt(dev, &usbif->backend_watch,
+		    backend_changed,  "%s/%s", dev->nodename, "port");
+		if (err)
+			break;
+		xenbus_switch_state(dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		usbif_disconnect(usbif);
+		xenbus_switch_state(dev, XenbusStateClosing);
+		break;
+
+	case XenbusStateClosed:
+		xenbus_switch_state(dev, XenbusStateClosed);
+		if (xenbus_dev_is_online(dev))
+			break;
+		/* fall through if not online */
+	case XenbusStateUnknown:
+		device_unregister(&dev->dev);
+		break;
+
+	default:
+		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+				 frontend_state);
+		break;
+	}
+}
+
+static const struct xenbus_device_id usbback_ids[] = {
+	{ "vusb" },
+	{ "" },
+};
+
+static struct xenbus_driver usbback_driver = {
+	.name = "vusb",
+	.ids = usbback_ids,
+	.probe = usbback_probe,
+	.otherend_changed = frontend_changed,
+	.remove = usbback_remove,
+};
+
+int __init usbback_xenbus_init(void)
+{
+	return xenbus_register_backend(&usbback_driver);
+}
+
+void __exit usbback_xenbus_exit(void)
+{
+	xenbus_unregister_driver(&usbback_driver);
+}
diff -purN include/xen/interface/io/ring.h include/xen/interface/io/ring.h
--- include/xen/interface/io/ring.h	2010-12-31 18:55:42.000000000 +0800
+++ include/xen/interface/io/ring.h	2010-12-31 11:05:10.000000000 +0800
@@ -24,8 +24,15 @@ typedef unsigned int RING_IDX;
  * A ring contains as many entries as will fit, rounded down to the nearest
  * power of two (so we can mask with (size-1) to loop around).
  */
-#define __RING_SIZE(_s, _sz) \
-    (__RD32(((_sz) - (long)&(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0])))
+#define __CONST_RING_SIZE(_s, _sz)				\
+	(__RD32(((_sz) - offsetof(struct _s##_sring, ring)) /	\
+		sizeof(((struct _s##_sring *)0)->ring[0])))
+
+/*
+ * The same for passing in an actual pointer instead of a name tag.
+ */
+#define __RING_SIZE(_s, _sz)						\
+	(__RD32(((_sz) - (long)&(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0])))
 
 /*
  * Macros to make the correct C datatypes for a new kind of ring.
diff -purN include/xen/interface/io/usbif.h include/xen/interface/io/usbif.h
--- include/xen/interface/io/usbif.h	1970-01-01 08:00:00.000000000 +0800
+++ include/xen/interface/io/usbif.h	2010-12-31 11:05:10.000000000 +0800
@@ -0,0 +1,151 @@
+/*
+ * usbif.h
+ *
+ * USB I/O interface for Xen guest OSes.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_PUBLIC_IO_USBIF_H__
+#define __XEN_PUBLIC_IO_USBIF_H__
+
+#include "ring.h"
+#include "../grant_table.h"
+
+enum usb_spec_version {
+	USB_VER_UNKNOWN = 0,
+	USB_VER_USB11,
+	USB_VER_USB20,
+	USB_VER_USB30,	/* not supported yet */
+};
+
+/*
+ *  USB pipe in usbif_request
+ *
+ *  bits 0-5 are specific bits for virtual USB driver.
+ *  bits 7-31 are standard urb pipe.
+ *
+ *  - port number(NEW):	bits 0-4
+ *  				(USB_MAXCHILDREN is 31)
+ *
+ *  - operation flag(NEW):	bit 5
+ *  				(0 = submit urb,
+ *  				 1 = unlink urb)
+ *
+ *  - direction:		bit 7
+ *  				(0 = Host-to-Device [Out]
+ *                           1 = Device-to-Host [In])
+ *
+ *  - device address:	bits 8-14
+ *
+ *  - endpoint:		bits 15-18
+ *
+ *  - pipe type:		bits 30-31
+ *  				(00 = isochronous, 01 = interrupt,
+ *                           10 = control, 11 = bulk)
+ */
+#define usbif_pipeportnum(pipe) ((pipe) & 0x1f)
+#define usbif_setportnum_pipe(pipe, portnum) \
+	((pipe)|(portnum))
+
+#define usbif_pipeunlink(pipe) ((pipe) & 0x20)
+#define usbif_pipesubmit(pipe) (!usbif_pipeunlink(pipe))
+#define usbif_setunlink_pipe(pipe) ((pipe)|(0x20))
+
+#define USBIF_BACK_MAX_PENDING_REQS (128)
+#define USBIF_MAX_SEGMENTS_PER_REQUEST (16)
+
+/*
+ * RING for transferring urbs.
+ */
+struct usbif_request_segment {
+	grant_ref_t gref;
+	uint16_t offset;
+	uint16_t length;
+};
+
+struct usbif_urb_request {
+	uint16_t id; /* request id */
+	uint16_t nr_buffer_segs; /* number of urb->transfer_buffer segments */
+
+	/* basic urb parameter */
+	uint32_t pipe;
+	uint16_t transfer_flags;
+	uint16_t buffer_length;
+	union {
+		uint8_t ctrl[8]; /* setup_packet (Ctrl) */
+
+		struct {
+			uint16_t interval; /* maximum (1024*8) in usb core */
+			uint16_t start_frame; /* start frame */
+			uint16_t number_of_packets; /* number of ISO packet */
+			uint16_t nr_frame_desc_segs; /* number of iso_frame_desc segments */
+		} isoc;
+
+		struct {
+			uint16_t interval; /* maximum (1024*8) in usb core */
+			uint16_t pad[3];
+		} intr;
+
+		struct {
+			uint16_t unlink_id; /* unlink request id */
+			uint16_t pad[3];
+		} unlink;
+
+	} u;
+
+	/* urb data segments */
+	struct usbif_request_segment seg[USBIF_MAX_SEGMENTS_PER_REQUEST];
+};
+typedef struct usbif_urb_request usbif_urb_request_t;
+
+struct usbif_urb_response {
+	uint16_t id; /* request id */
+	uint16_t start_frame;  /* start frame (ISO) */
+	int32_t status; /* status (non-ISO) */
+	int32_t actual_length; /* actual transfer length */
+	int32_t error_count; /* number of ISO errors */
+};
+typedef struct usbif_urb_response usbif_urb_response_t;
+
+DEFINE_RING_TYPES(usbif_urb, struct usbif_urb_request, struct usbif_urb_response);
+#define USB_URB_RING_SIZE __CONST_RING_SIZE(usbif_urb, PAGE_SIZE)
+
+/*
+ * RING for notifying connect/disconnect events to frontend
+ */
+struct usbif_conn_request {
+	uint16_t id;
+};
+typedef struct usbif_conn_request usbif_conn_request_t;
+
+struct usbif_conn_response {
+	uint16_t id; /* request id */
+	uint8_t portnum; /* port number */
+	uint8_t speed; /* usb_device_speed */
+};
+typedef struct usbif_conn_response usbif_conn_response_t;
+
+DEFINE_RING_TYPES(usbif_conn, struct usbif_conn_request, struct usbif_conn_response);
+#define USB_CONN_RING_SIZE __CONST_RING_SIZE(usbif_conn, PAGE_SIZE)
+
+#endif /* __XEN_PUBLIC_IO_USBIF_H__ */
