* Re: pvusb drivers for pvops 2.6.32.x kernel
@ 2011-01-03 11:41 Pasi Kärkkäinen
2011-01-04 9:33 ` Ian Campbell
0 siblings, 1 reply; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-01-03 11:41 UTC (permalink / raw)
To: xen-devel; +Cc: Jeremy Fitzhardinge
[-- Attachment #1: Type: text/plain, Size: 2711 bytes --]
Hello,
Jeremy: See the included patch. If it's OK it'd be nice
to get it into xen/stable-2.6.32.x branch.
Thanks Nathanael!
-- Pasi
----- Forwarded message from Nathanael Rensen <nathanael@polymorpheus.com> -----
From: Nathanael Rensen <nathanael@polymorpheus.com>
To: Pasi Kärkkäinen <pasik@iki.fi>
Cc: n_iwamatsu@jp.fujitsu.com
Date: Mon, 3 Jan 2011 19:32:23 +0800
Subject: Re: pvusb for pvops
On 1 January 2011 23:19, Nathanael Rensen <nathanael@polymorpheus.com> wrote:
> On 1 January 2011 22:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
>> On Sat, Jan 01, 2011 at 03:10:58AM +0800, Nathanael Rensen wrote:
>>> Hi Noboru,
>>>
>>> In case it is helpful ...
>>>
>>> I've attached a diff that adds the pvusb drivers to the debian squeeze
>>> 2.6.32 pvops kernel.
>>> So far I've only tested with a USB drive on xen 4.0.1 between debian
>>> squeeze pvops dom0 and
>>> debian squeeze pvops domU.
>>>
>>> The diff is based on the SLES SP1 diffs posted here:
>>> http://code.google.com/p/gentoo-xen-kernel/downloads/list
>>> It almost applies cleanly to xen/stable-2.6.32.x except for the patch to
>>> include/xen/interface/io/ring.h which is not required for xen/stable-2.6.32.x.
>>> I haven't tested the xen/stable-2.6.32.x kernel though.
>>>
>>
>> Great, thanks!
>> Is it OK to forward this email to xen-devel mailinglist?
>>
>> Btw did you check from novell's kernel git tree if there
>> has been fixes to the pvusb code after that patch on
>> code.google.com was posted?
>>
>> -- Pasi
>
> For sure, you're welcome to forward it, but maybe hold off a bit because
> I've refactored the pvusb frontend code to be consistent with the way the
> other frontend drivers are within the pvops tree. I'll also take a look at
> Novell's tree to check for updates.
>
> I'll do some testing on a kernel built from the pvops stable-2.6.32.x tree
> and send an updated diff against that tree in the next couple of days.
>
> Nathanael
>
I've attached an updated diff against the pvops xen/stable-2.6.32.x tree.
I've tested by attaching an af9015 USB TV tuner to a pvops domu from
a pvops dom0 and recording a few hours of TV.
I also tried to attach various USB devices to a WinXP domu with GPLPV
drivers, but the domu kept crashing. That needs more investigation, but
I suspect a problem with the GPLPV drivers. I'm keen for that to be fixed,
but I don't have time immediately to dig into it.
I took a look at the patches at
http://gitorious.org/opensuse/kernel-source/trees/SLE11-SP1/patches.xen
and as far as I can tell, everything that relates to pvusb is included in the
attached patch.
Nathanael
----- End forwarded message -----
[-- Attachment #2: pvusb-pvops-2.6.32.x.diff --]
[-- Type: text/x-diff, Size: 113750 bytes --]
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 9b43b22..498d8c5 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -381,3 +381,16 @@ config USB_HWA_HCD
To compile this driver a module, choose M here: the module
will be called "hwa-hc".
+
+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.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index f58b249..bc46412 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
+obj-$(CONFIG_XEN_USB_FRONTEND) += xen-usbfront.o
diff --git a/drivers/usb/host/xen-usbfront.c b/drivers/usb/host/xen-usbfront.c
new file mode 100644
index 0000000..8186863
--- /dev/null
+++ b/drivers/usb/host/xen-usbfront.c
@@ -0,0 +1,1740 @@
+/*
+ * xen-usbfront.c
+ *
+ * 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.
+ */
+
+#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);
+
+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);
+}
+
+/*
+ * 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;
+}
+
+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;
+}
+
+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
+};
+
+#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 --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index fa9982e..b4a5665 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -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 --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index ef1ea63..d64d2c6 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/
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 --git a/drivers/xen/usbback/Makefile b/drivers/xen/usbback/Makefile
new file mode 100644
index 0000000..a7548cb
--- /dev/null
+++ b/drivers/xen/usbback/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_XEN_USB_BACKEND) := usbbk.o
+
+usbbk-y := usbstub.o xenbus.o interface.o usbback.o
+
diff --git a/drivers/xen/usbback/interface.c b/drivers/xen/usbback/interface.c
new file mode 100644
index 0000000..99f7483
--- /dev/null
+++ b/drivers/xen/usbback/interface.c
@@ -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 --git a/drivers/xen/usbback/usbback.c b/drivers/xen/usbback/usbback.c
new file mode 100644
index 0000000..b8c671d
--- /dev/null
+++ b/drivers/xen/usbback/usbback.c
@@ -0,0 +1,1194 @@
+/*
+ * 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++) {
+ 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 --git a/drivers/xen/usbback/usbback.h b/drivers/xen/usbback/usbback.h
new file mode 100644
index 0000000..701117f
--- /dev/null
+++ b/drivers/xen/usbback/usbback.h
@@ -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 --git a/drivers/xen/usbback/usbstub.c b/drivers/xen/usbback/usbstub.c
new file mode 100644
index 0000000..3a1a440
--- /dev/null
+++ b/drivers/xen/usbback/usbstub.c
@@ -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 --git a/drivers/xen/usbback/xenbus.c b/drivers/xen/usbback/xenbus.c
new file mode 100644
index 0000000..a8dca3c
--- /dev/null
+++ b/drivers/xen/usbback/xenbus.c
@@ -0,0 +1,338 @@
+/*
+ * 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",
+ .owner = THIS_MODULE,
+ .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 --git a/include/xen/interface/io/usbif.h b/include/xen/interface/io/usbif.h
new file mode 100644
index 0000000..6099c29
--- /dev/null
+++ b/include/xen/interface/io/usbif.h
@@ -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__ */
[-- Attachment #3: Type: text/plain, Size: 138 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-03 11:41 pvusb drivers for pvops 2.6.32.x kernel Pasi Kärkkäinen
@ 2011-01-04 9:33 ` Ian Campbell
2011-01-04 9:52 ` Pasi Kärkkäinen
0 siblings, 1 reply; 14+ messages in thread
From: Ian Campbell @ 2011-01-04 9:33 UTC (permalink / raw)
To: Pasi Kärkkäinen
Cc: Fitzhardinge, xen-devel@lists.xensource.com, Jeremy
On Mon, 2011-01-03 at 11:41 +0000, Pasi Kärkkäinen wrote:
> Hello,
>
> Jeremy: See the included patch. If it's OK it'd be nice
> to get it into xen/stable-2.6.32.x branch.
>
> Thanks Nathanael!
Yes thanks!
I think the correct path for this functionality is to first get it
accepted into the upstream kernel by working with the USB subsystem
maintainer+list, fixing the issue arising from their review etc.
The stuff necessary to get the frontend upstream has been upstream for
ages. For the backend basic dom0 boot support is in 2.6.37 and the
generic scaffolding for backends is currently in linux-next (via
Konrad's tree) and is intended to be in the next merge window.
I think the backend just looks like a regular USB driver to the host
system so it probably belongs in drivers/usb/<something>/xen-usbback/
and not drivers/xen. (I'm not sure what the <something> should be,
perhaps "misc").
IIRC when the PV USB drivers were originally submitted there was no
toolstack support included. Has this since been implemented? Seems to be
missing from libxl at least. Is someone looking into this?
Ian.
>
> -- Pasi
>
> ----- Forwarded message from Nathanael Rensen <nathanael@polymorpheus.com> -----
>
> From: Nathanael Rensen <nathanael@polymorpheus.com>
> To: Pasi Kärkkäinen <pasik@iki.fi>
> Cc: n_iwamatsu@jp.fujitsu.com
> Date: Mon, 3 Jan 2011 19:32:23 +0800
> Subject: Re: pvusb for pvops
>
> On 1 January 2011 23:19, Nathanael Rensen <nathanael@polymorpheus.com> wrote:
> > On 1 January 2011 22:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> >> On Sat, Jan 01, 2011 at 03:10:58AM +0800, Nathanael Rensen wrote:
> >>> Hi Noboru,
> >>>
> >>> In case it is helpful ...
> >>>
> >>> I've attached a diff that adds the pvusb drivers to the debian squeeze
> >>> 2.6.32 pvops kernel.
> >>> So far I've only tested with a USB drive on xen 4.0.1 between debian
> >>> squeeze pvops dom0 and
> >>> debian squeeze pvops domU.
> >>>
> >>> The diff is based on the SLES SP1 diffs posted here:
> >>> http://code.google.com/p/gentoo-xen-kernel/downloads/list
> >>> It almost applies cleanly to xen/stable-2.6.32.x except for the patch to
> >>> include/xen/interface/io/ring.h which is not required for xen/stable-2.6.32.x.
> >>> I haven't tested the xen/stable-2.6.32.x kernel though.
> >>>
> >>
> >> Great, thanks!
> >> Is it OK to forward this email to xen-devel mailinglist?
> >>
> >> Btw did you check from novell's kernel git tree if there
> >> has been fixes to the pvusb code after that patch on
> >> code.google.com was posted?
> >>
> >> -- Pasi
> >
> > For sure, you're welcome to forward it, but maybe hold off a bit because
> > I've refactored the pvusb frontend code to be consistent with the way the
> > other frontend drivers are within the pvops tree. I'll also take a look at
> > Novell's tree to check for updates.
> >
> > I'll do some testing on a kernel built from the pvops stable-2.6.32.x tree
> > and send an updated diff against that tree in the next couple of days.
> >
> > Nathanael
> >
>
> I've attached an updated diff against the pvops xen/stable-2.6.32.x tree.
> I've tested by attaching an af9015 USB TV tuner to a pvops domu from
> a pvops dom0 and recording a few hours of TV.
>
> I also tried to attach various USB devices to a WinXP domu with GPLPV
> drivers, but the domu kept crashing. That needs more investigation, but
> I suspect a problem with the GPLPV drivers. I'm keen for that to be fixed,
> but I don't have time immediately to dig into it.
>
> I took a look at the patches at
> http://gitorious.org/opensuse/kernel-source/trees/SLE11-SP1/patches.xen
> and as far as I can tell, everything that relates to pvusb is included in the
> attached patch.
>
> Nathanael
>
>
>
> ----- End forwarded message -----
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-04 9:33 ` Ian Campbell
@ 2011-01-04 9:52 ` Pasi Kärkkäinen
2011-01-05 13:07 ` Nathanael Rensen
0 siblings, 1 reply; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-01-04 9:52 UTC (permalink / raw)
To: Ian Campbell
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com,
Nathanael Rensen
On Tue, Jan 04, 2011 at 09:33:39AM +0000, Ian Campbell wrote:
> On Mon, 2011-01-03 at 11:41 +0000, Pasi Kärkkäinen wrote:
> > Hello,
> >
> > Jeremy: See the included patch. If it's OK it'd be nice
> > to get it into xen/stable-2.6.32.x branch.
> >
> > Thanks Nathanael!
>
> Yes thanks!
>
Added Nathanael to CC, I forgot him earlier.. doh.
> I think the correct path for this functionality is to first get it
> accepted into the upstream kernel by working with the USB subsystem
> maintainer+list, fixing the issue arising from their review etc.
>
Ok.
> The stuff necessary to get the frontend upstream has been upstream for
> ages. For the backend basic dom0 boot support is in 2.6.37 and the
> generic scaffolding for backends is currently in linux-next (via
> Konrad's tree) and is intended to be in the next merge window.
>
> I think the backend just looks like a regular USB driver to the host
> system so it probably belongs in drivers/usb/<something>/xen-usbback/
> and not drivers/xen. (I'm not sure what the <something> should be,
> perhaps "misc").
>
Yeah, maybe the USB maintainers can suggest proper location.
> IIRC when the PV USB drivers were originally submitted there was no
> toolstack support included. Has this since been implemented? Seems to be
> missing from libxl at least. Is someone looking into this?
>
Xen 4.0.0 added pvusb toolstack support to xm/xend.
This driver is ported (by Nathanael) from Novell SLES11SP1 2.6.32 Xenlinux kernel,
where it works with xm/xend.
Some info here:
http://wiki.xen.org/xenwiki/XenUSBPassthrough
and:
http://wiki.xen.org/xenwiki/Xen4.0
-- Pasi
> Ian.
>
> >
> > -- Pasi
> >
> > ----- Forwarded message from Nathanael Rensen <nathanael@polymorpheus.com> -----
> >
> > From: Nathanael Rensen <nathanael@polymorpheus.com>
> > To: Pasi Kärkkäinen <pasik@iki.fi>
> > Cc: n_iwamatsu@jp.fujitsu.com
> > Date: Mon, 3 Jan 2011 19:32:23 +0800
> > Subject: Re: pvusb for pvops
> >
> > On 1 January 2011 23:19, Nathanael Rensen <nathanael@polymorpheus.com> wrote:
> > > On 1 January 2011 22:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> > >> On Sat, Jan 01, 2011 at 03:10:58AM +0800, Nathanael Rensen wrote:
> > >>> Hi Noboru,
> > >>>
> > >>> In case it is helpful ...
> > >>>
> > >>> I've attached a diff that adds the pvusb drivers to the debian squeeze
> > >>> 2.6.32 pvops kernel.
> > >>> So far I've only tested with a USB drive on xen 4.0.1 between debian
> > >>> squeeze pvops dom0 and
> > >>> debian squeeze pvops domU.
> > >>>
> > >>> The diff is based on the SLES SP1 diffs posted here:
> > >>> http://code.google.com/p/gentoo-xen-kernel/downloads/list
> > >>> It almost applies cleanly to xen/stable-2.6.32.x except for the patch to
> > >>> include/xen/interface/io/ring.h which is not required for xen/stable-2.6.32.x.
> > >>> I haven't tested the xen/stable-2.6.32.x kernel though.
> > >>>
> > >>
> > >> Great, thanks!
> > >> Is it OK to forward this email to xen-devel mailinglist?
> > >>
> > >> Btw did you check from novell's kernel git tree if there
> > >> has been fixes to the pvusb code after that patch on
> > >> code.google.com was posted?
> > >>
> > >> -- Pasi
> > >
> > > For sure, you're welcome to forward it, but maybe hold off a bit because
> > > I've refactored the pvusb frontend code to be consistent with the way the
> > > other frontend drivers are within the pvops tree. I'll also take a look at
> > > Novell's tree to check for updates.
> > >
> > > I'll do some testing on a kernel built from the pvops stable-2.6.32.x tree
> > > and send an updated diff against that tree in the next couple of days.
> > >
> > > Nathanael
> > >
> >
> > I've attached an updated diff against the pvops xen/stable-2.6.32.x tree.
> > I've tested by attaching an af9015 USB TV tuner to a pvops domu from
> > a pvops dom0 and recording a few hours of TV.
> >
> > I also tried to attach various USB devices to a WinXP domu with GPLPV
> > drivers, but the domu kept crashing. That needs more investigation, but
> > I suspect a problem with the GPLPV drivers. I'm keen for that to be fixed,
> > but I don't have time immediately to dig into it.
> >
> > I took a look at the patches at
> > http://gitorious.org/opensuse/kernel-source/trees/SLE11-SP1/patches.xen
> > and as far as I can tell, everything that relates to pvusb is included in the
> > attached patch.
> >
> > Nathanael
> >
> >
> >
> > ----- End forwarded message -----
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-04 9:52 ` Pasi Kärkkäinen
@ 2011-01-05 13:07 ` Nathanael Rensen
2011-01-06 18:55 ` Pasi Kärkkäinen
0 siblings, 1 reply; 14+ messages in thread
From: Nathanael Rensen @ 2011-01-05 13:07 UTC (permalink / raw)
To: Ian Campbell; +Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com
On Tue, Jan 04, 2011 at 09:33:39AM +0000, Ian Campbell wrote:
>
> I think the correct path for this functionality is to first get it
> accepted into the upstream kernel by working with the USB subsystem
> maintainer+list, fixing the issue arising from their review etc.
I'm happy to have a shot at that. I don't imagine it will be a quick
process so in the meantime I think there is value in supporting pvusb
in stable/2.6.32.x to encourage people to test and identify issues,
and also to make it easier to track the maintenance.
> The stuff necessary to get the frontend upstream has been upstream for
> ages. For the backend basic dom0 boot support is in 2.6.37 and the
> generic scaffolding for backends is currently in linux-next (via
> Konrad's tree) and is intended to be in the next merge window.
>
> I think the backend just looks like a regular USB driver to the host
> system so it probably belongs in drivers/usb/<something>/xen-usbback/
> and not drivers/xen. (I'm not sure what the <something> should be,
> perhaps "misc").
I agree that the frontend driver looks like a regular USB host
controller driver and belongs in drivers/usb/host. From the dom0
perspective the backend driver is a consumer of USB services rather
than a provider so my inclination is that the proper place for the
backend is drivers/xen along with blkback, netback and pciback. Much
the same way as I would expect to find a USB TV tuner driver in
drivers/media rather than drivers/usb. I would prefer to remain
consistent with the model established by the block, net and pci
drivers, but I am happy to adopt whatever convention is acceptable to
upstream.
Nathanael
> Ian.
>> > ----- Forwarded message from Nathanael Rensen <nathanael@polymorpheus.com> -----
>> >
>> > From: Nathanael Rensen <nathanael@polymorpheus.com>
>> > To: Pasi Kärkkäinen <pasik@iki.fi>
>> > Cc: n_iwamatsu@jp.fujitsu.com
>> > Date: Mon, 3 Jan 2011 19:32:23 +0800
>> > Subject: Re: pvusb for pvops
>> >
>> > On 1 January 2011 23:19, Nathanael Rensen <nathanael@polymorpheus.com> wrote:
>> > > On 1 January 2011 22:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
>> > >> On Sat, Jan 01, 2011 at 03:10:58AM +0800, Nathanael Rensen wrote:
>> > >>> Hi Noboru,
>> > >>>
>> > >>> In case it is helpful ...
>> > >>>
>> > >>> I've attached a diff that adds the pvusb drivers to the debian squeeze
>> > >>> 2.6.32 pvops kernel.
>> > >>> So far I've only tested with a USB drive on xen 4.0.1 between debian
>> > >>> squeeze pvops dom0 and
>> > >>> debian squeeze pvops domU.
>> > >>>
>> > >>> The diff is based on the SLES SP1 diffs posted here:
>> > >>> http://code.google.com/p/gentoo-xen-kernel/downloads/list
>> > >>> It almost applies cleanly to xen/stable-2.6.32.x except for the patch to
>> > >>> include/xen/interface/io/ring.h which is not required for xen/stable-2.6.32.x.
>> > >>> I haven't tested the xen/stable-2.6.32.x kernel though.
>> > >>>
>> > >>
>> > >> Great, thanks!
>> > >> Is it OK to forward this email to xen-devel mailinglist?
>> > >>
>> > >> Btw did you check from novell's kernel git tree if there
>> > >> has been fixes to the pvusb code after that patch on
>> > >> code.google.com was posted?
>> > >>
>> > >> -- Pasi
>> > >
>> > > For sure, you're welcome to forward it, but maybe hold off a bit because
>> > > I've refactored the pvusb frontend code to be consistent with the way the
>> > > other frontend drivers are within the pvops tree. I'll also take a look at
>> > > Novell's tree to check for updates.
>> > >
>> > > I'll do some testing on a kernel built from the pvops stable-2.6.32.x tree
>> > > and send an updated diff against that tree in the next couple of days.
>> > >
>> > > Nathanael
>> > >
>> >
>> > I've attached an updated diff against the pvops xen/stable-2.6.32.x tree.
>> > I've tested by attaching an af9015 USB TV tuner to a pvops domu from
>> > a pvops dom0 and recording a few hours of TV.
>> >
>> > I also tried to attach various USB devices to a WinXP domu with GPLPV
>> > drivers, but the domu kept crashing. That needs more investigation, but
>> > I suspect a problem with the GPLPV drivers. I'm keen for that to be fixed,
>> > but I don't have time immediately to dig into it.
>> >
>> > I took a look at the patches at
>> > http://gitorious.org/opensuse/kernel-source/trees/SLE11-SP1/patches.xen
>> > and as far as I can tell, everything that relates to pvusb is included in the
>> > attached patch.
>> >
>> > Nathanael
>> >
>> >
>> >
>> > ----- End forwarded message -----
>>
>>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-05 13:07 ` Nathanael Rensen
@ 2011-01-06 18:55 ` Pasi Kärkkäinen
2011-01-07 9:56 ` Nathanael Rensen
2011-01-07 10:04 ` [SPAM] Re: Re: pvusb drivers for pvops 2.6.32.x kernel Ian Campbell
0 siblings, 2 replies; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-01-06 18:55 UTC (permalink / raw)
To: Nathanael Rensen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com, Ian Campbell
On Wed, Jan 05, 2011 at 09:07:49PM +0800, Nathanael Rensen wrote:
> On Tue, Jan 04, 2011 at 09:33:39AM +0000, Ian Campbell wrote:
> >
> > I think the correct path for this functionality is to first get it
> > accepted into the upstream kernel by working with the USB subsystem
> > maintainer+list, fixing the issue arising from their review etc.
>
> I'm happy to have a shot at that. I don't imagine it will be a quick
> process so in the meantime I think there is value in supporting pvusb
> in stable/2.6.32.x to encourage people to test and identify issues,
> and also to make it easier to track the maintenance.
>
Yeah, at least earlier Jeremy said he's happy to take most patches for xen/stable-2.6.32.x :)
> > The stuff necessary to get the frontend upstream has been upstream for
> > ages. For the backend basic dom0 boot support is in 2.6.37 and the
> > generic scaffolding for backends is currently in linux-next (via
> > Konrad's tree) and is intended to be in the next merge window.
> >
> > I think the backend just looks like a regular USB driver to the host
> > system so it probably belongs in drivers/usb/<something>/xen-usbback/
> > and not drivers/xen. (I'm not sure what the <something> should be,
> > perhaps "misc").
>
> I agree that the frontend driver looks like a regular USB host
> controller driver and belongs in drivers/usb/host. From the dom0
> perspective the backend driver is a consumer of USB services rather
> than a provider so my inclination is that the proper place for the
> backend is drivers/xen along with blkback, netback and pciback. Much
> the same way as I would expect to find a USB TV tuner driver in
> drivers/media rather than drivers/usb. I would prefer to remain
> consistent with the model established by the block, net and pci
> drivers, but I am happy to adopt whatever convention is acceptable to
> upstream.
>
Something I noticed about the pvusb backend..
Should the name of the driver be xen-usbbk instead of just usbbk ?
The other backend driver modules seem to have xen- prefix.
-- Pasi
> Nathanael
>
> > Ian.
>
> >> > ----- Forwarded message from Nathanael Rensen <nathanael@polymorpheus.com> -----
> >> >
> >> > From: Nathanael Rensen <nathanael@polymorpheus.com>
> >> > To: Pasi Kärkkäinen <pasik@iki.fi>
> >> > Cc: n_iwamatsu@jp.fujitsu.com
> >> > Date: Mon, 3 Jan 2011 19:32:23 +0800
> >> > Subject: Re: pvusb for pvops
> >> >
> >> > On 1 January 2011 23:19, Nathanael Rensen <nathanael@polymorpheus.com> wrote:
> >> > > On 1 January 2011 22:43, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> >> > >> On Sat, Jan 01, 2011 at 03:10:58AM +0800, Nathanael Rensen wrote:
> >> > >>> Hi Noboru,
> >> > >>>
> >> > >>> In case it is helpful ...
> >> > >>>
> >> > >>> I've attached a diff that adds the pvusb drivers to the debian squeeze
> >> > >>> 2.6.32 pvops kernel.
> >> > >>> So far I've only tested with a USB drive on xen 4.0.1 between debian
> >> > >>> squeeze pvops dom0 and
> >> > >>> debian squeeze pvops domU.
> >> > >>>
> >> > >>> The diff is based on the SLES SP1 diffs posted here:
> >> > >>> http://code.google.com/p/gentoo-xen-kernel/downloads/list
> >> > >>> It almost applies cleanly to xen/stable-2.6.32.x except for the patch to
> >> > >>> include/xen/interface/io/ring.h which is not required for xen/stable-2.6.32.x.
> >> > >>> I haven't tested the xen/stable-2.6.32.x kernel though.
> >> > >>>
> >> > >>
> >> > >> Great, thanks!
> >> > >> Is it OK to forward this email to xen-devel mailinglist?
> >> > >>
> >> > >> Btw did you check from novell's kernel git tree if there
> >> > >> has been fixes to the pvusb code after that patch on
> >> > >> code.google.com was posted?
> >> > >>
> >> > >> -- Pasi
> >> > >
> >> > > For sure, you're welcome to forward it, but maybe hold off a bit because
> >> > > I've refactored the pvusb frontend code to be consistent with the way the
> >> > > other frontend drivers are within the pvops tree. I'll also take a look at
> >> > > Novell's tree to check for updates.
> >> > >
> >> > > I'll do some testing on a kernel built from the pvops stable-2.6.32.x tree
> >> > > and send an updated diff against that tree in the next couple of days.
> >> > >
> >> > > Nathanael
> >> > >
> >> >
> >> > I've attached an updated diff against the pvops xen/stable-2.6.32.x tree.
> >> > I've tested by attaching an af9015 USB TV tuner to a pvops domu from
> >> > a pvops dom0 and recording a few hours of TV.
> >> >
> >> > I also tried to attach various USB devices to a WinXP domu with GPLPV
> >> > drivers, but the domu kept crashing. That needs more investigation, but
> >> > I suspect a problem with the GPLPV drivers. I'm keen for that to be fixed,
> >> > but I don't have time immediately to dig into it.
> >> >
> >> > I took a look at the patches at
> >> > http://gitorious.org/opensuse/kernel-source/trees/SLE11-SP1/patches.xen
> >> > and as far as I can tell, everything that relates to pvusb is included in the
> >> > attached patch.
> >> >
> >> > Nathanael
> >> >
> >> >
> >> >
> >> > ----- End forwarded message -----
> >>
> >>
> >
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-06 18:55 ` Pasi Kärkkäinen
@ 2011-01-07 9:56 ` Nathanael Rensen
2011-01-07 10:28 ` Pasi Kärkkäinen
2011-01-07 10:04 ` [SPAM] Re: Re: pvusb drivers for pvops 2.6.32.x kernel Ian Campbell
1 sibling, 1 reply; 14+ messages in thread
From: Nathanael Rensen @ 2011-01-07 9:56 UTC (permalink / raw)
To: Pasi Kärkkäinen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com, Ian Campbell
[-- Attachment #1: Type: text/plain, Size: 753 bytes --]
On 7 January 2011 02:55, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> Something I noticed about the pvusb backend..
> Should the name of the driver be xen-usbbk instead of just usbbk ?
>
> The other backend driver modules seem to have xen- prefix.
>
> -- Pasi
Good point - although I think xen-usbback would better match
xen-blkback, xen-netback, xen-pciback.
For consistency the config options should probably be
CONFIG_XEN_USBDEV_BACKEND and CONFIG_XEN_USBDEV_FRONTEND (the previous
patch had CONFIG_XEN_USB_...).
I also noticed the frontend driver was producing a warning when
loading which is resolved by setting the module owner.
I've placed an updated diff here:
http://members.iinet.net.au/~nathanael/pvusb.diff
Nathanael
[-- Attachment #2: pvusb2.diff --]
[-- Type: application/octet-stream, Size: 113806 bytes --]
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 9b43b22..b77ddfb 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -381,3 +381,16 @@ config USB_HWA_HCD
To compile this driver a module, choose M here: the module
will be called "hwa-hc".
+
+config XEN_USBDEV_FRONTEND
+ tristate "Xen pvusb device frontend driver"
+ depends on XEN && USB
+ select XEN_XENBUS_FRONTEND
+ default m
+ 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.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index f58b249..be2f0ff 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
+obj-$(CONFIG_XEN_USBDEV_FRONTEND) += xen-usbfront.o
diff --git a/drivers/usb/host/xen-usbfront.c b/drivers/usb/host/xen-usbfront.c
new file mode 100644
index 0000000..14e9a02
--- /dev/null
+++ b/drivers/usb/host/xen-usbfront.c
@@ -0,0 +1,1741 @@
+/*
+ * xen-usbfront.c
+ *
+ * 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.
+ */
+
+#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);
+
+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);
+}
+
+/*
+ * 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;
+}
+
+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;
+}
+
+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
+};
+
+#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",
+ .owner = THIS_MODULE,
+ .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 --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index fa9982e..98a97b03 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -51,7 +51,14 @@ config XEN_BLKDEV_BACKEND
block devices to other guests via a high-performance shared-memory
interface.
-
+config XEN_USBDEV_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 --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index ef1ea63..ebce2b3 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/
obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/
obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/
obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/
+obj-$(CONFIG_XEN_USBDEV_BACKEND) += usbback/
obj-$(CONFIG_XENFS) += xenfs/
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
obj-$(CONFIG_XEN_MCE) += mce.o
diff --git a/drivers/xen/usbback/Makefile b/drivers/xen/usbback/Makefile
new file mode 100644
index 0000000..23a3a40
--- /dev/null
+++ b/drivers/xen/usbback/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_XEN_USBDEV_BACKEND) := xen-usbback.o
+
+xen-usbback-y := usbstub.o xenbus.o interface.o usbback.o
diff --git a/drivers/xen/usbback/interface.c b/drivers/xen/usbback/interface.c
new file mode 100644
index 0000000..99f7483
--- /dev/null
+++ b/drivers/xen/usbback/interface.c
@@ -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 --git a/drivers/xen/usbback/usbback.c b/drivers/xen/usbback/usbback.c
new file mode 100644
index 0000000..b8c671d
--- /dev/null
+++ b/drivers/xen/usbback/usbback.c
@@ -0,0 +1,1194 @@
+/*
+ * 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++) {
+ 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 --git a/drivers/xen/usbback/usbback.h b/drivers/xen/usbback/usbback.h
new file mode 100644
index 0000000..701117f
--- /dev/null
+++ b/drivers/xen/usbback/usbback.h
@@ -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 --git a/drivers/xen/usbback/usbstub.c b/drivers/xen/usbback/usbstub.c
new file mode 100644
index 0000000..3a1a440
--- /dev/null
+++ b/drivers/xen/usbback/usbstub.c
@@ -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 --git a/drivers/xen/usbback/xenbus.c b/drivers/xen/usbback/xenbus.c
new file mode 100644
index 0000000..a8dca3c
--- /dev/null
+++ b/drivers/xen/usbback/xenbus.c
@@ -0,0 +1,338 @@
+/*
+ * 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",
+ .owner = THIS_MODULE,
+ .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 --git a/include/xen/interface/io/usbif.h b/include/xen/interface/io/usbif.h
new file mode 100644
index 0000000..6099c29
--- /dev/null
+++ b/include/xen/interface/io/usbif.h
@@ -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__ */
[-- Attachment #3: Type: text/plain, Size: 138 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [SPAM] Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-06 18:55 ` Pasi Kärkkäinen
2011-01-07 9:56 ` Nathanael Rensen
@ 2011-01-07 10:04 ` Ian Campbell
1 sibling, 0 replies; 14+ messages in thread
From: Ian Campbell @ 2011-01-07 10:04 UTC (permalink / raw)
To: Pasi Kärkkäinen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com,
Nathanael Rensen
On Thu, 2011-01-06 at 18:55 +0000, Pasi Kärkkäinen wrote:
> On Wed, Jan 05, 2011 at 09:07:49PM +0800, Nathanael Rensen wrote:
> > On Tue, Jan 04, 2011 at 09:33:39AM +0000, Ian Campbell wrote:
> > >
> > > I think the correct path for this functionality is to first get it
> > > accepted into the upstream kernel by working with the USB subsystem
> > > maintainer+list, fixing the issue arising from their review etc.
> >
> > I'm happy to have a shot at that. I don't imagine it will be a quick
> > process so in the meantime I think there is value in supporting pvusb
> > in stable/2.6.32.x to encourage people to test and identify issues,
> > and also to make it easier to track the maintenance.
> >
>
> Yeah, at least earlier Jeremy said he's happy to take most patches for xen/stable-2.6.32.x :)
I have fewer objections if Nathanael is stepping up to maintain the
functionality and to take on the upstreaming work etc (thanks
Nathanael!). The existing maintainers mostly have their plates full.
(I'm assuming the original author/maintainer of this code is no longer
active in the area, otherwise we really ought to be hearing from them
about this).
However what we need to avoid is having this conversation again WRT
xen/stable-2.6.{38,39,40,41,...}.x and so on for every stable (or long
term stable) branch.
I didn't review the code in details but checkpatch.pl says:
total: 7 errors, 104 warnings, 4226 lines checked
The majority look to be trivial to resolve whitespace or new typedef
issues. There's a bunch of 80-character line warnings but personally I'd
encourage using your own judgement on a case by case basis for those
rather than blindly following checkpatch.pl (applies in general too but
specifically wrt line length).
Also the patch needs a suitable commit message and a signed-off-by per
the D.C.O in Documentation/SubmittingPatches.
> > > The stuff necessary to get the frontend upstream has been upstream for
> > > ages. For the backend basic dom0 boot support is in 2.6.37 and the
> > > generic scaffolding for backends is currently in linux-next (via
> > > Konrad's tree) and is intended to be in the next merge window.
> > >
> > > I think the backend just looks like a regular USB driver to the host
> > > system so it probably belongs in drivers/usb/<something>/xen-usbback/
> > > and not drivers/xen. (I'm not sure what the <something> should be,
> > > perhaps "misc").
> >
> > I agree that the frontend driver looks like a regular USB host
> > controller driver and belongs in drivers/usb/host. From the dom0
> > perspective the backend driver is a consumer of USB services rather
> > than a provider so my inclination is that the proper place for the
> > backend is drivers/xen along with blkback, netback and pciback. Much
> > the same way as I would expect to find a USB TV tuner driver in
> > drivers/media rather than drivers/usb. I would prefer to remain
> > consistent with the model established by the block, net and pci
> > drivers,
A fair point.
> but I am happy to adopt whatever convention is acceptable to
> > upstream.
That's probably the best approach to take.
> Something I noticed about the pvusb backend..
> Should the name of the driver be xen-usbbk instead of just usbbk ?
>
> The other backend driver modules seem to have xen- prefix.
xen-usbback please but, yes, this is a good change to make which
happened to the other drivers in the pvops tree sometime ago e.g.:
a84aa84cd0db4dd6a1a911f2263846c9b6f30a49 xen: rename netbk module xen-netback.
e762bd5145e59c043e47228fe1a6c1eebef07c0c xen: rename blkbk module xen-blkback.
Skimming "git log --pretty=oneline --no-merges xen/xen/stable-2.6.32.x
-- drivers/xen/{blk,net}back drivers/net/xen-netfront.c
drivers/block/xen-blkfront.c" for other generic changes which would be
worth propagating gives:
71133087313f15db44ffb6ea802e5bdb2479a600 xen: use less generic names in blkfront driver.
9c9c87f53f87d0368ef04207cce4c92884f4ae3d xen: use less generic names in netfront driver.
0c989045948320d583a190b75a12ba0ec556b804 Fix compile warnings: ignoring return value of 'xenbus_register_backend' ..
d2748d40e3e8a14706d0f3a378160def877aa222 xen/netback: don't include xen/evtchn.h
d3d3c63ce04b34ad6b45ed6da12d2be012861622 xen/blkback: don't include xen/evtchn.h
Ian.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-07 9:56 ` Nathanael Rensen
@ 2011-01-07 10:28 ` Pasi Kärkkäinen
2011-03-23 14:42 ` Pasi Kärkkäinen
0 siblings, 1 reply; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-01-07 10:28 UTC (permalink / raw)
To: Nathanael Rensen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com, Ian Campbell
On Fri, Jan 07, 2011 at 05:56:28PM +0800, Nathanael Rensen wrote:
> On 7 January 2011 02:55, Pasi Kärkkäinen <pasik@iki.fi> wrote:
>
> > Something I noticed about the pvusb backend..
> > Should the name of the driver be xen-usbbk instead of just usbbk ?
> >
> > The other backend driver modules seem to have xen- prefix.
> >
> > -- Pasi
>
> Good point - although I think xen-usbback would better match
> xen-blkback, xen-netback, xen-pciback.
>
Yeah, yesterday I tried porting PVSCSI drivers to pvops,
and I used xen-scsiback and xen-scsifront there aswell.
> For consistency the config options should probably be
> CONFIG_XEN_USBDEV_BACKEND and CONFIG_XEN_USBDEV_FRONTEND (the previous
> patch had CONFIG_XEN_USB_...).
>
I'll have to check these for PVSCSI aswell.
> I also noticed the frontend driver was producing a warning when
> loading which is resolved by setting the module owner.
>
> I've placed an updated diff here:
> http://members.iinet.net.au/~nathanael/pvusb.diff
>
Great, thanks.
-- Pasi
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-01-07 10:28 ` Pasi Kärkkäinen
@ 2011-03-23 14:42 ` Pasi Kärkkäinen
2011-06-15 21:07 ` Pasi Kärkkäinen
[not found] ` <CAHd5nyxU6OEqDeiwooxNt4q_U+AzATDA6b1Ax0xuQZJiuzbLuA@mail.gmail.com>
0 siblings, 2 replies; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-03-23 14:42 UTC (permalink / raw)
To: Nathanael Rensen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com, Ian Campbell
On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
>
> > I also noticed the frontend driver was producing a warning when
> > loading which is resolved by setting the module owner.
> >
> > I've placed an updated diff here:
> > http://members.iinet.net.au/~nathanael/pvusb.diff
> >
>
> Great, thanks.
>
Hello again,
Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
-- Pasi
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Re: pvusb drivers for pvops 2.6.32.x kernel
2011-03-23 14:42 ` Pasi Kärkkäinen
@ 2011-06-15 21:07 ` Pasi Kärkkäinen
[not found] ` <CAHd5nyxU6OEqDeiwooxNt4q_U+AzATDA6b1Ax0xuQZJiuzbLuA@mail.gmail.com>
1 sibling, 0 replies; 14+ messages in thread
From: Pasi Kärkkäinen @ 2011-06-15 21:07 UTC (permalink / raw)
To: Nathanael Rensen
Cc: Jeremy Fitzhardinge, xen-devel@lists.xensource.com, Ian Campbell
On Wed, Mar 23, 2011 at 04:42:57PM +0200, Pasi Kärkkäinen wrote:
> On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
> >
> > > I also noticed the frontend driver was producing a warning when
> > > loading which is resolved by setting the module owner.
> > >
> > > I've placed an updated diff here:
> > > http://members.iinet.net.au/~nathanael/pvusb.diff
> > >
> >
> > Great, thanks.
> >
>
> Hello again,
>
> Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
>
Hello,
Now there's actually LinuxCon Vancouver event coming up,
where Linux USB virtualization is going to be discussed:
http://marc.info/?l=linux-kernel&m=130766622608800&w=2
So people interested in Xen PVUSB might want to attend/contribute..
http://events.linuxfoundation.org/events/linuxcon
-- Pasi
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: pvusb drivers for pvops 2.6.32.x kernel
[not found] ` <CAHd5nyxU6OEqDeiwooxNt4q_U+AzATDA6b1Ax0xuQZJiuzbLuA@mail.gmail.com>
@ 2012-02-08 6:59 ` Pasi Kärkkäinen
2012-02-08 17:38 ` Konrad Rzeszutek Wilk
2012-02-08 17:40 ` Konrad Rzeszutek Wilk
0 siblings, 2 replies; 14+ messages in thread
From: Pasi Kärkkäinen @ 2012-02-08 6:59 UTC (permalink / raw)
To: Nathanael Rensen; +Cc: xen-devel, Konrad Rzeszutek Wilk
On Wed, Feb 08, 2012 at 08:56:22AM +0800, Nathanael Rensen wrote:
> Hi Pasi,
>
Hello,
(I added Konrad and xen-devel to CC list, hopefully that's ok..)
> It's been a very long time since we discussed PVUSB - I haven't given
> up, just been caught up with other things.
>
Great! Really nice to hear from you again..
Just last weekend I fetched the opensuse Linux 3.2 kernel-xen src.rpm,
extracted the xen patches, and started preparing to re-port the driver to pvops.
So now I don't have to finish that :)
> I've updated the PVUSB patch, done some refactoring and cleaned up the
> checkpatch warnings.
> The patch is targetted at linux-next, but it also applies cleanly to
> Konrad's linux-next branch:
>
> http://members.iinet.net.au/~nathanael/0001-pvusb-driver.linux-next.patch
>
> I haven't done a lot of testing, but it does work for my basic test
> (USB TV-tuner). My next step is to get in contact with the USB
> maintainer and see what further changes are required in order to get
> this work upstreamed. In the meantime, the more testing and exposure
> the patch gets the better.
>
I'm sure we can get people testing this patch. Many people have been asking
for USB passthru solution for Linux 3.x.
> Thanks,
>
Thanks a lot!
-- Pasi
> Nathanael
>
> On 23 March 2011 22:42, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> > On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
> >>
> >> > I also noticed the frontend driver was producing a warning when
> >> > loading which is resolved by setting the module owner.
> >> >
> >> > I've placed an updated diff here:
> >> > http://members.iinet.net.au/~nathanael/pvusb.diff
> >> >
> >>
> >> Great, thanks.
> >>
> >
> > Hello again,
> >
> > Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
> >
> > -- Pasi
> >
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: pvusb drivers for pvops 2.6.32.x kernel
2012-02-08 6:59 ` Pasi Kärkkäinen
@ 2012-02-08 17:38 ` Konrad Rzeszutek Wilk
2012-02-08 17:40 ` Konrad Rzeszutek Wilk
1 sibling, 0 replies; 14+ messages in thread
From: Konrad Rzeszutek Wilk @ 2012-02-08 17:38 UTC (permalink / raw)
To: Pasi Kärkkäinen; +Cc: xen-devel, Nathanael Rensen
On Wed, Feb 08, 2012 at 08:59:30AM +0200, Pasi Kärkkäinen wrote:
> On Wed, Feb 08, 2012 at 08:56:22AM +0800, Nathanael Rensen wrote:
> > Hi Pasi,
> >
>
> Hello,
>
> (I added Konrad and xen-devel to CC list, hopefully that's ok..)
>
> > It's been a very long time since we discussed PVUSB - I haven't given
> > up, just been caught up with other things.
> >
>
> Great! Really nice to hear from you again..
>
> Just last weekend I fetched the opensuse Linux 3.2 kernel-xen src.rpm,
> extracted the xen patches, and started preparing to re-port the driver to pvops.
> So now I don't have to finish that :)
>
> > I've updated the PVUSB patch, done some refactoring and cleaned up the
> > checkpatch warnings.
> > The patch is targetted at linux-next, but it also applies cleanly to
> > Konrad's linux-next branch:
> >
> > http://members.iinet.net.au/~nathanael/0001-pvusb-driver.linux-next.patch
Nice job (I took a brief look at it). I see you handled those m2p_override
nicely.
> >
> > I haven't done a lot of testing, but it does work for my basic test
> > (USB TV-tuner). My next step is to get in contact with the USB
> > maintainer and see what further changes are required in order to get
> > this work upstreamed. In the meantime, the more testing and exposure
> > the patch gets the better.
OK. Well let me stick on my #testing branch which is a kitchensink of things
and play with a bit.
> >
>
> I'm sure we can get people testing this patch. Many people have been asking
> for USB passthru solution for Linux 3.x.
>
>
> > Thanks,
> >
>
> Thanks a lot!
>
>
> -- Pasi
>
> > Nathanael
> >
> > On 23 March 2011 22:42, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> > > On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
> > >>
> > >> > I also noticed the frontend driver was producing a warning when
> > >> > loading which is resolved by setting the module owner.
> > >> >
> > >> > I've placed an updated diff here:
> > >> > http://members.iinet.net.au/~nathanael/pvusb.diff
> > >> >
> > >>
> > >> Great, thanks.
> > >>
> > >
> > > Hello again,
> > >
> > > Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
> > >
> > > -- Pasi
> > >
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: pvusb drivers for pvops 2.6.32.x kernel
2012-02-08 6:59 ` Pasi Kärkkäinen
2012-02-08 17:38 ` Konrad Rzeszutek Wilk
@ 2012-02-08 17:40 ` Konrad Rzeszutek Wilk
2012-02-08 18:01 ` Xen pvusb drivers for upstream Linux 3.x kernels Pasi Kärkkäinen
1 sibling, 1 reply; 14+ messages in thread
From: Konrad Rzeszutek Wilk @ 2012-02-08 17:40 UTC (permalink / raw)
To: Pasi Kärkkäinen, james.harper; +Cc: xen-devel, Nathanael Rensen
On Wed, Feb 08, 2012 at 08:59:30AM +0200, Pasi Kärkkäinen wrote:
> On Wed, Feb 08, 2012 at 08:56:22AM +0800, Nathanael Rensen wrote:
> > Hi Pasi,
> >
>
> Hello,
>
> (I added Konrad and xen-devel to CC list, hopefully that's ok..)
And let me add James Harper here since he was doing some testing with
the Windows GPL PV USB driver as well
>
> > It's been a very long time since we discussed PVUSB - I haven't given
> > up, just been caught up with other things.
> >
>
> Great! Really nice to hear from you again..
>
> Just last weekend I fetched the opensuse Linux 3.2 kernel-xen src.rpm,
> extracted the xen patches, and started preparing to re-port the driver to pvops.
> So now I don't have to finish that :)
>
> > I've updated the PVUSB patch, done some refactoring and cleaned up the
> > checkpatch warnings.
> > The patch is targetted at linux-next, but it also applies cleanly to
> > Konrad's linux-next branch:
> >
> > http://members.iinet.net.au/~nathanael/0001-pvusb-driver.linux-next.patch
> >
> > I haven't done a lot of testing, but it does work for my basic test
> > (USB TV-tuner). My next step is to get in contact with the USB
> > maintainer and see what further changes are required in order to get
> > this work upstreamed. In the meantime, the more testing and exposure
> > the patch gets the better.
> >
>
> I'm sure we can get people testing this patch. Many people have been asking
> for USB passthru solution for Linux 3.x.
>
>
> > Thanks,
> >
>
> Thanks a lot!
>
>
> -- Pasi
>
> > Nathanael
> >
> > On 23 March 2011 22:42, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> > > On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
> > >>
> > >> > I also noticed the frontend driver was producing a warning when
> > >> > loading which is resolved by setting the module owner.
> > >> >
> > >> > I've placed an updated diff here:
> > >> > http://members.iinet.net.au/~nathanael/pvusb.diff
> > >> >
> > >>
> > >> Great, thanks.
> > >>
> > >
> > > Hello again,
> > >
> > > Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
> > >
> > > -- Pasi
> > >
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xensource.com
> http://lists.xensource.com/xen-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Xen pvusb drivers for upstream Linux 3.x kernels
2012-02-08 17:40 ` Konrad Rzeszutek Wilk
@ 2012-02-08 18:01 ` Pasi Kärkkäinen
0 siblings, 0 replies; 14+ messages in thread
From: Pasi Kärkkäinen @ 2012-02-08 18:01 UTC (permalink / raw)
To: Konrad Rzeszutek Wilk; +Cc: james.harper, xen-devel, Nathanael Rensen
On Wed, Feb 08, 2012 at 12:40:59PM -0500, Konrad Rzeszutek Wilk wrote:
> On Wed, Feb 08, 2012 at 08:59:30AM +0200, Pasi Kärkkäinen wrote:
> > On Wed, Feb 08, 2012 at 08:56:22AM +0800, Nathanael Rensen wrote:
> > > Hi Pasi,
> > >
> >
> > Hello,
> >
> > (I added Konrad and xen-devel to CC list, hopefully that's ok..)
>
> And let me add James Harper here since he was doing some testing with
> the Windows GPL PV USB driver as well
>
And let's modify the subject aswell :)
-- Pasi
>
> >
> > > It's been a very long time since we discussed PVUSB - I haven't given
> > > up, just been caught up with other things.
> > >
> >
> > Great! Really nice to hear from you again..
> >
> > Just last weekend I fetched the opensuse Linux 3.2 kernel-xen src.rpm,
> > extracted the xen patches, and started preparing to re-port the driver to pvops.
> > So now I don't have to finish that :)
> >
> > > I've updated the PVUSB patch, done some refactoring and cleaned up the
> > > checkpatch warnings.
> > > The patch is targetted at linux-next, but it also applies cleanly to
> > > Konrad's linux-next branch:
> > >
> > > http://members.iinet.net.au/~nathanael/0001-pvusb-driver.linux-next.patch
> > >
> > > I haven't done a lot of testing, but it does work for my basic test
> > > (USB TV-tuner). My next step is to get in contact with the USB
> > > maintainer and see what further changes are required in order to get
> > > this work upstreamed. In the meantime, the more testing and exposure
> > > the patch gets the better.
> > >
> >
> > I'm sure we can get people testing this patch. Many people have been asking
> > for USB passthru solution for Linux 3.x.
> >
> >
> > > Thanks,
> > >
> >
> > Thanks a lot!
> >
> >
> > -- Pasi
> >
> > > Nathanael
> > >
> > > On 23 March 2011 22:42, Pasi Kärkkäinen <pasik@iki.fi> wrote:
> > > > On Fri, Jan 07, 2011 at 12:28:19PM +0200, Pasi Kärkkäinen wrote:
> > > >>
> > > >> > I also noticed the frontend driver was producing a warning when
> > > >> > loading which is resolved by setting the module owner.
> > > >> >
> > > >> > I've placed an updated diff here:
> > > >> > http://members.iinet.net.au/~nathanael/pvusb.diff
> > > >> >
> > > >>
> > > >> Great, thanks.
> > > >>
> > > >
> > > > Hello again,
> > > >
> > > > Nathanael: Did you have time to take a look at upstreaming xen pvusb drivers ?
> > > >
> > > > -- Pasi
> > > >
> >
> > _______________________________________________
> > Xen-devel mailing list
> > Xen-devel@lists.xensource.com
> > http://lists.xensource.com/xen-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2012-02-08 18:01 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-01-03 11:41 pvusb drivers for pvops 2.6.32.x kernel Pasi Kärkkäinen
2011-01-04 9:33 ` Ian Campbell
2011-01-04 9:52 ` Pasi Kärkkäinen
2011-01-05 13:07 ` Nathanael Rensen
2011-01-06 18:55 ` Pasi Kärkkäinen
2011-01-07 9:56 ` Nathanael Rensen
2011-01-07 10:28 ` Pasi Kärkkäinen
2011-03-23 14:42 ` Pasi Kärkkäinen
2011-06-15 21:07 ` Pasi Kärkkäinen
[not found] ` <CAHd5nyxU6OEqDeiwooxNt4q_U+AzATDA6b1Ax0xuQZJiuzbLuA@mail.gmail.com>
2012-02-08 6:59 ` Pasi Kärkkäinen
2012-02-08 17:38 ` Konrad Rzeszutek Wilk
2012-02-08 17:40 ` Konrad Rzeszutek Wilk
2012-02-08 18:01 ` Xen pvusb drivers for upstream Linux 3.x kernels Pasi Kärkkäinen
2011-01-07 10:04 ` [SPAM] Re: Re: pvusb drivers for pvops 2.6.32.x kernel Ian Campbell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).