WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [linux-2.6.18-xen] PVUSB: frontend driver

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [linux-2.6.18-xen] PVUSB: frontend driver
From: "Xen patchbot-linux-2.6.18-xen" <patchbot-linux-2.6.18-xen@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2009 07:25:30 -0700
Delivery-date: Wed, 18 Mar 2009 07:27:31 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1237376571 0
# Node ID 8f996719f2ffb35354b82e1540eda1b3928136da
# Parent  71aedaa9e21c5750e8c8da01865f9052dee38267
PVUSB: frontend driver

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
---
 drivers/xen/usbfront/Makefile       |    7 
 drivers/xen/usbfront/usbfront-dbg.c |   99 +++++++
 drivers/xen/usbfront/usbfront-hcd.c |  279 ++++++++++++++++++++
 drivers/xen/usbfront/usbfront-hub.c |  479 ++++++++++++++++++++++++++++++++++++
 drivers/xen/usbfront/usbfront-q.c   |  418 +++++++++++++++++++++++++++++++
 drivers/xen/usbfront/usbfront.h     |  216 ++++++++++++++++
 drivers/xen/usbfront/xenbus.c       |  365 +++++++++++++++++++++++++++
 7 files changed, 1863 insertions(+)

diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/Makefile     Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,7 @@
+obj-$(CONFIG_XEN_USB_FRONTEND) := xen-hcd.o
+
+xen-hcd-y   := usbfront-hcd.o xenbus.o
+
+ifeq ($(CONFIG_XEN_USB_FRONTEND_HCD_STATS),y)
+EXTRA_CFLAGS += -DXENHCD_STATS
+endif
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/usbfront-dbg.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/usbfront-dbg.c       Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,99 @@
+/*
+ * usbfront-dbg.c
+ *
+ * Xen USB Virtual Host Controller - debugging
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+static ssize_t show_statistics(struct class_device *class_dev, char *buf)
+{
+       struct usb_bus *bus;
+       struct usb_hcd *hcd;
+       struct usbfront_info *info;
+       unsigned long flags;
+       unsigned temp, size;
+       char *next;
+
+       bus = class_get_devdata(class_dev);
+       hcd = bus->hcpriv;
+       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,
+                       hcd->self.controller->bus_id,
+                       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 CLASS_DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
+
+static inline void create_debug_file(struct usbfront_info *info)
+{
+       struct class_device *cldev = info_to_hcd(info)->self.class_dev;
+       class_device_create_file(cldev, &class_device_attr_statistics);
+}
+
+static inline void remove_debug_file(struct usbfront_info *info)
+{
+       struct class_device *cldev = info_to_hcd(info)->self.class_dev;
+       class_device_remove_file(cldev, &class_device_attr_statistics);
+}
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/usbfront-hcd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/usbfront-hcd.c       Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,279 @@
+/*
+ * usbfront-hcd.c
+ *
+ * Xen USB Virtual Host Controller driver
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbfront.h"
+#include "usbfront-dbg.c"
+#include "usbfront-hub.c"
+#include "usbfront-q.c"
+
+static void xenhcd_watchdog(unsigned long param)
+{
+       struct usbfront_info *info = (struct usbfront_info *) param;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (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_urbs);
+       INIT_LIST_HEAD(&info->inprogress_urbs);
+       INIT_LIST_HEAD(&info->unlinked_urbs);
+       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);
+       /*
+        * TODO: port power off, cancel all urbs.
+        */
+
+       if (HC_IS_RUNNING(hcd->state))
+               hcd->state = HC_STATE_HALT;
+       spin_unlock_irq(&info->lock);
+}
+
+/*
+ * TODO: incomplete suspend/resume functions!
+ */
+#if 0
+#ifdef CONFIG_PM
+/*
+ * suspend running HC
+ */
+static int xenhcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+       struct usbfront_info *info = hcd_to_info(hcd);
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (hcd->state != HC_STATE_SUSPENDED) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       /*
+        * TODO:
+        *      canceling all transfer, clear all hc queue,
+        *      stop kthread,
+        */
+
+       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+done:
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       return ret;
+}
+
+/*
+ * resume HC
+ */
+static int xenhcd_resume(struct usb_hcd *hcd)
+{
+       struct usbfront_info *info = hcd_to_info(hcd);
+       int ret = -EINVAL;
+
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       /*
+        * TODO:
+        *      re-init HC.
+        *      resume all roothub ports.
+        */
+
+       return ret;
+}
+#endif
+#endif
+
+/*
+ * called as .urb_enqueue()
+ * non-error returns are promise to giveback the urb later
+ */
+static int xenhcd_urb_enqueue(struct usb_hcd *hcd,
+                                   struct usb_host_endpoint *ep,
+                                   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;
+       }
+
+       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()
+ *
+ * just mark the urb as unlinked
+ * if the urb is in pending_urbs, move to unlinked_urbs
+ * TODO:
+ *     canceling the urb transfer in backend
+ */
+static int xenhcd_urb_dequeue(struct usb_hcd *hcd,
+                                   struct urb *urb)
+{
+       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;
+
+       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;
+}
+
+/*
+ * TODO:
+ * suspend/resume whole hcd and roothub
+ */
+static const char hcd_name[] = "xen_hcd";
+
+struct hc_driver usbfront_hc_driver = {
+       .description = hcd_name,
+       .product_desc = DRIVER_DESC,
+       .hcd_priv_size = sizeof(struct usbfront_info),
+       .flags = HCD_USB2,
+
+       /*
+        * basic HC lifecycle operations
+        */
+       .reset = xenhcd_setup,
+       .start = xenhcd_run,
+       .stop = xenhcd_stop,
+#if 0
+#ifdef CONFIG_PM
+       .suspend = xenhcd_suspend,
+       .resume = xenhcd_resume,
+#endif
+#endif
+       /*
+        * 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,
+#if 0
+#ifdef CONFIG_PM
+       .bus_suspend = xenhcd_bus_suspend,
+       .bus_resume = xenhcd_bus_resume,
+#endif
+#endif
+};
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/usbfront-hub.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/usbfront-hub.c       Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,479 @@
+/*
+ * usbfront-hub.c
+ *
+ * Xen USB Virtual Host Controller - Root Hub Emulations
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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/>.
+ */
+
+/*
+ * 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;
+
+       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);
+       }
+}
+
+void rhport_disconnect(struct usbfront_info *info, int portnum)
+{
+       rhport_connect(info, portnum, USB_SPEED_UNKNOWN);
+}
+
+void xenhcd_rhport_state_change(struct usbfront_info *info,
+                               int portnum, enum usb_device_speed speed)
+{
+       int changed = 0;
+       unsigned long flags;
+
+       if (portnum < 1 || portnum > info->rh_numports)
+               return; /* invalid port number */
+
+       spin_lock_irqsave(&info->lock, flags);
+       rhport_connect(info, portnum, speed);
+       if (info->ports[portnum-1].c_connection)
+               changed = 1;
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       if (changed)
+               usb_hcd_poll_rh_status(info_to_hcd(info));
+}
+
+/*
+ * 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);
+}
+
+#if 0
+#ifdef CONFIG_PM
+static int xenhcd_bus_suspend(struct usb_hcd *hcd)
+{
+       struct usbfront_info *info = hcd_to_info(hcd);
+       int i, ports;
+
+       ports = info->rh_numports;
+
+       spin_lock_irq(&info->lock);
+
+       if (HC_IS_RUNNING(hcd->state)) {
+               /*
+                * TODO:
+                * clean queue,
+                * stop all transfers,
+                * ...
+                */
+               hcd->state = HC_STATE_QUIESCING;
+       }
+
+       /* suspend any active ports*/
+       for (i = 1; i <= ports; i++) {
+               rhport_suspend(info, i);
+       }
+
+       del_timer_sync(&info->watchdog);
+
+       spin_unlock_irq(&info->lock);
+
+       return 0;
+}
+
+static int xenhcd_bus_resume(struct usb_hcd *hcd)
+{
+       struct usbfront_info *info = hcd_to_info(hcd);
+       int i, ports;
+
+       ports = info->rh_numports;
+
+       spin_lock_irq(&info->lock);
+       /* resume any suspended ports*/
+       for (i = 1; i <= ports; i++) {
+               rhport_resume(info, i);
+       }
+       hcd->state = HC_STATE_RUNNING;
+       spin_unlock_irq(&info->lock);
+       return 0;
+}
+#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;
+
+#ifdef USBFRONT_DEBUG
+       WPRINTK("xenusb_hub_control(typeReq %x wValue %x wIndex %x)\n",
+              typeReq, wValue, wIndex);
+#endif
+
+       spin_lock_irqsave(&info->lock, flags);
+       switch (typeReq) {
+       case ClearHubFeature:
+               /* ignore this request */
+               break;
+       case ClearPortFeature:
+               if (!wIndex || wIndex > ports)
+                       goto error;
+
+               switch(wValue) {
+               case USB_PORT_FEAT_SUSPEND:
+                       rhport_resume(info, wIndex);
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       rhport_power_off(info, wIndex);
+                       break;
+               case USB_PORT_FEAT_ENABLE:
+                       rhport_disable(info, wIndex);
+                       break;
+               case USB_PORT_FEAT_C_CONNECTION:
+                       info->ports[wIndex-1].c_connection = 0;
+                       /* falling through */
+               default:
+                       info->ports[wIndex-1].status &= ~(1 << wValue);
+                       break;
+               }
+               break;
+       case GetHubDescriptor:
+               xenhcd_hub_descriptor(info,
+                                     (struct usb_hub_descriptor*) buf);
+               break;
+       case GetHubStatus:
+               /* always local power supply good and no over-current exists. */
+               *(__le32 *)buf = cpu_to_le32(0);
+               break;
+       case GetPortStatus:
+               if (!wIndex || wIndex > ports)
+                       goto error;
+
+               wIndex--;
+
+               /* resume completion */
+               if (info->ports[wIndex].resuming &&
+                       time_after_eq(jiffies, info->ports[wIndex].timeout)) {
+                       info->ports[wIndex].status |= (USB_PORT_STAT_C_SUSPEND 
<< 16);
+                       info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
+               }
+
+               /* reset completion */
+               if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
+                       time_after_eq(jiffies, info->ports[wIndex].timeout)) {
+                       info->ports[wIndex].status |= (USB_PORT_STAT_C_RESET << 
16);
+                       info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
+
+                       if (info->devices[wIndex].status != 
USB_STATE_NOTATTACHED) {
+                               info->ports[wIndex].status |= 
USB_PORT_STAT_ENABLE;
+                               info->devices[wIndex].status = 
USB_STATE_DEFAULT;
+                       }
+
+                       switch(info->devices[wIndex].speed) {
+                       case USB_SPEED_LOW:
+                               info->ports[wIndex].status |= 
USB_PORT_STAT_LOW_SPEED;
+                               break;
+                       case USB_SPEED_HIGH:
+                               info->ports[wIndex].status |= 
USB_PORT_STAT_HIGH_SPEED;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               ((u16 *) buf)[0] = cpu_to_le16 (info->ports[wIndex].status);
+               ((u16 *) buf)[1] = cpu_to_le16 (info->ports[wIndex].status >> 
16);
+               break;
+       case SetHubFeature:
+               /* not supported */
+               goto error;
+       case SetPortFeature:
+               if (!wIndex || wIndex > ports)
+                       goto error;
+
+               switch(wValue) {
+               case USB_PORT_FEAT_POWER:
+                       rhport_power_on(info, wIndex);
+                       break;
+               case USB_PORT_FEAT_RESET:
+                       rhport_reset(info, wIndex);
+                       break;
+               case USB_PORT_FEAT_SUSPEND:
+                       rhport_suspend(info, wIndex);
+                       break;
+               default:
+                       if ((info->ports[wIndex-1].status & 
USB_PORT_STAT_POWER) != 0) {
+                               info->ports[wIndex-1].status |= (1 << wValue);
+                       }
+               }
+               break;
+
+       default:
+error:
+               ret = -EPIPE;
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       /* check status for each port */
+       for (i = 0; i < ports; i++) {
+               if (info->ports[i].status & PORT_C_MASK) {
+                       changed = 1;
+               }
+       }
+       if (changed)
+               usb_hcd_poll_rh_status(hcd);
+
+       return ret;
+}
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/usbfront-q.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/usbfront-q.c Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,418 @@
+/*
+ * usbfront-q.c
+ *
+ * Xen USB Virtual Host Controller - RING operations.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+struct kmem_cache *xenhcd_urbp_cachep;
+
+static struct urb_priv *alloc_urb_priv(struct urb *urb)
+{
+       struct urb_priv *urbp;
+
+       urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, GFP_ATOMIC);
+       if (!urbp) {
+               return NULL;
+       }
+
+       urbp->urb = urb;
+       urb->hcpriv = urbp;
+       urbp->req_id = ~0;
+       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_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_pfn;
+       unsigned int offset;
+       unsigned int len;
+       unsigned int bytes;
+       int i;
+
+       page = virt_to_page(addr);
+       buffer_pfn = page_to_phys(page) >> PAGE_SHIFT;
+       offset = offset_in_page(addr);
+       len = length;
+
+       for(i = 0;i < nr_pages;i++){
+               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_pfn, flags);
+               seg[i].gref = ref;
+               seg[i].offset = (uint16_t)offset;
+               seg[i].length = (uint16_t)bytes;
+
+               buffer_pfn++;
+               len -= bytes;
+               offset = 0;
+       }
+}
+
+static int map_urb_for_request(struct usbfront_info *info, struct urb *urb,
+               usbif_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, 0UL);
+}
+
+static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb)
+__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;
+       default:
+               COUNT(info->stats.complete);
+       }
+       spin_unlock(&info->lock);
+       usb_hcd_giveback_urb(info_to_hcd(info), urb, NULL);
+       spin_lock(&info->lock);
+}
+
+static inline int xenhcd_do_request(struct usbfront_info *info, struct 
urb_priv *urbp)
+{
+       usbif_request_t *ring_req;
+       struct urb *urb = urbp->urb;
+       uint16_t id;
+       int notify;
+       int ret = 0;
+
+       ring_req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
+       id = get_id_from_freelist(info);
+       ring_req->id = id;
+
+       ret = map_urb_for_request(info, urb, ring_req);
+       if (ret < 0) {
+               add_id_to_freelist(info, id);
+               return ret;
+       }
+
+       info->ring.req_prod_pvt++;
+       info->shadow[id].urb = urb;
+       info->shadow[id].req = *ring_req;
+       urbp->req_id = id;
+
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->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_urbs)) {
+               if (RING_FULL(&info->ring)) {
+                       COUNT(info->stats.ring_full);
+                       timer_action(info, TIMER_RING_WATCHDOG);
+                       goto done;
+               }
+
+               urbp = list_entry(info->pending_urbs.next, struct urb_priv, 
list);
+               ret = xenhcd_do_request(info, urbp);
+               if (ret == 0)
+                       list_move_tail(&urbp->list, &info->inprogress_urbs);
+               else
+                       xenhcd_giveback_urb(info, urbp->urb);
+       }
+       timer_action_done(info, TIMER_SCAN_PENDING_URBS);
+
+done:
+       return;
+}
+
+static void xenhcd_giveback_unlinked_urbs(struct usbfront_info *info)
+{
+       struct urb_priv *urbp, *tmp;
+
+       list_for_each_entry_safe(urbp, tmp, &info->unlinked_urbs, list) {
+               xenhcd_giveback_urb(info, urbp->urb);
+       }
+}
+
+static int xenhcd_submit_urb(struct usbfront_info *info, struct urb_priv *urbp)
+{
+       int ret = 0;
+
+       if (RING_FULL(&info->ring)) {
+               list_add_tail(&urbp->list, &info->pending_urbs);
+               COUNT(info->stats.ring_full);
+               timer_action(info, TIMER_RING_WATCHDOG);
+               goto done;
+       }
+
+       if (!list_empty(&info->pending_urbs)) {
+               list_add_tail(&urbp->list, &info->pending_urbs);
+               timer_action(info, TIMER_SCAN_PENDING_URBS);
+               goto done;
+       }
+
+       ret = xenhcd_do_request(info, urbp);
+       if (ret == 0)
+               list_add_tail(&urbp->list, &info->inprogress_urbs);
+
+done:
+       return ret;
+}
+
+static int xenhcd_unlink_urb(struct usbfront_info *info, struct urb_priv *urbp)
+{
+       if (urbp->unlinked)
+               return -EBUSY;
+       urbp->unlinked = 1;
+
+       /* if the urb is in pending_urbs */
+       if (urbp->req_id == ~0) {
+               list_move_tail(&urbp->list, &info->unlinked_urbs);
+               timer_action(info, TIMER_SCAN_PENDING_URBS);
+       }
+
+       /* TODO: send cancel request to backend */
+
+       return 0;
+}
+
+static int xenhcd_end_submit_urb(struct usbfront_info *info)
+{
+       usbif_response_t *ring_res;
+       struct urb *urb;
+       struct urb_priv *urbp;
+
+       RING_IDX i, rp;
+       uint16_t id;
+       int more_to_do = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock, flags);
+       rp = info->ring.sring->rsp_prod;
+       rmb(); /* ensure we see queued responses up to "rp" */
+
+       for (i = info->ring.rsp_cons; i != rp; i++) {
+               ring_res = RING_GET_RESPONSE(&info->ring, i);
+               id = ring_res->id;
+               xenhcd_gnttab_done(&info->shadow[id]);
+               urb = info->shadow[id].urb;
+               barrier();
+               add_id_to_freelist(info, id);
+
+               urbp = (struct urb_priv *)urb->hcpriv;
+               if (likely(!urbp->unlinked)) {
+                       urb->status = ring_res->status;
+                       urb->actual_length = ring_res->actual_length;
+                       urb->error_count = ring_res->error_count;
+                       urb->start_frame = ring_res->start_frame;
+               }
+               barrier();
+               xenhcd_giveback_urb(info, urb);
+       }
+       info->ring.rsp_cons = i;
+
+       if (i != info->ring.req_prod_pvt)
+               RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
+       else
+               info->ring.sring->rsp_event = i + 1;
+
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       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_end_submit_urb(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, struct pt_regs *ptregs)
+{
+       xenhcd_notify_work((struct usbfront_info *) dev_id);
+       return IRQ_HANDLED;
+}
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/usbfront.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/usbfront.h   Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,216 @@
+/*
+ * usbfront.h
+ *
+ * This file is part of Xen USB Virtual Host Controller driver.
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __XEN_USBFRONT_H__
+#define __XEN_USBFRONT_H__
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include <xen/xenbus.h>
+#include <xen/evtchn.h>
+#include <xen/gnttab.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"
+
+#define DRIVER_DESC "Xen USB2.0 Virtual Host Controller driver (usbfront)"
+
+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 */
+       unsigned unlinked:1; /* dequeued urb just marked */
+};
+
+/* 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_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 3 queues.
+        *
+        * pending_urbs:
+        *      If xenhcd_urb_enqueue() called in RING_FULL state,
+        *      the enqueued urbs are added to this queue, and waits
+        *      to be sent to the backend.
+        *
+        * inprogress_urbs:
+        *      After xenhcd_urb_enqueue() called and RING_REQUEST sent,
+        *      the urbs are added to this queue and waits for RING_RESPONSE.
+        *
+        * unlinked_urbs:
+        *      When xenhcd_urb_dequeue() called, if the dequeued urb is
+        *      listed in pending_urbs, that urb is moved to this queue
+        *      and waits to be given back to the USB core.
+        */
+       struct list_head pending_urbs;
+       struct list_head inprogress_urbs;
+       struct list_head unlinked_urbs;
+       spinlock_t lock;
+
+       /*
+        * timer function that kick pending_urbs and unlink_urbs.
+        */
+       unsigned long actions;
+       struct timer_list watchdog;
+
+       /*
+        * Virtual roothub:
+        * Emulates the hub ports and the attached devices status.
+        * USB_MAXCHILDREN is defined (16) in include/linux/usb.h
+        */
+       int rh_numports;
+       struct rhport_status ports[USB_MAXCHILDREN];
+       struct vdevice_status devices[USB_MAXCHILDREN];
+
+#ifdef XENHCD_STATS
+       struct xenhcd_stats stats;
+#define COUNT(x) do { (x)++; } while (0)
+#else
+#define COUNT(x) do {} while (0)
+#endif
+
+       /* Xen related staff */
+       struct xenbus_device *xbdev;
+       int ring_ref;
+       usbif_front_ring_t ring;
+       unsigned int irq;
+       struct usb_shadow shadow[USB_RING_SIZE];
+       unsigned long shadow_free;
+
+       /* RING_RESPONSE thread */
+       struct task_struct *kthread;
+       wait_queue_head_t wq;
+       unsigned int waiting_resp;
+};
+
+#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);
+       }
+}
+
+irqreturn_t xenhcd_int(int irq, void *dev_id, struct pt_regs *ptregs);
+
+#endif /* __XEN_USBFRONT_H__ */
diff -r 71aedaa9e21c -r 8f996719f2ff drivers/xen/usbfront/xenbus.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/usbfront/xenbus.c     Wed Mar 18 11:42:51 2009 +0000
@@ -0,0 +1,365 @@
+/*
+ * xenbus.c
+ *
+ * Xenbus interface for Xen USB Virtual Host Controller
+ *
+ * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
+ * Author: Noboru Iwamatsu <n_iwamatsu@xxxxxxxxxxxxxx>
+ *
+ * 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,
+ *
+ * When distributed separately from the Linux kernel or incorporated into
+ * other software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "usbfront.h"
+
+extern struct hc_driver usbfront_hc_driver;
+extern struct kmem_cache *xenhcd_urbp_cachep;
+extern void xenhcd_rhport_state_change(struct usbfront_info *info,
+                                       int port, enum usb_device_speed speed);
+extern int xenhcd_schedule(void *arg);
+
+#define GRANT_INVALID_REF 0
+
+static void usbif_free(struct usbfront_info *info)
+{
+       if (info->ring_ref != GRANT_INVALID_REF) {
+               gnttab_end_foreign_access(info->ring_ref,
+                                         (unsigned long)info->ring.sring);
+               info->ring_ref = GRANT_INVALID_REF;
+               info->ring.sring = NULL;
+       }
+       if (info->irq)
+               unbind_from_irqhandler(info->irq, info);
+       info->irq = 0;
+}
+
+static int setup_usbring(struct xenbus_device *dev,
+                          struct usbfront_info *info)
+{
+       usbif_sring_t *sring;
+       int err;
+
+       info->ring_ref= GRANT_INVALID_REF;
+
+       sring = (usbif_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH);
+       if (!sring) {
+               xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
+               return -ENOMEM;
+       }
+       SHARED_RING_INIT(sring);
+       FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
+
+       err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
+       if (err < 0) {
+               free_page((unsigned long)sring);
+               info->ring.sring = NULL;
+               goto fail;
+       }
+       info->ring_ref = err;
+
+       err = bind_listening_port_to_irqhandler(
+               dev->otherend_id, xenhcd_int, SA_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:
+       usbif_free(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_usbring(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, "ring-ref", "%u",
+                           info->ring_ref);
+       if (err) {
+               message = "writing ring-ref";
+               goto abort_transaction;
+       }
+
+       err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+                           irq_to_evtchn_port(info->irq));
+       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;
+       }
+
+       xenbus_switch_state(dev, XenbusStateInitialised);
+
+       return 0;
+
+abort_transaction:
+       xenbus_transaction_end(xbt, 1);
+       xenbus_dev_fatal(dev, err, "%s", message);
+
+destroy_ring:
+       usbif_free(info);
+
+out:
+       return err;
+}
+
+static struct usb_hcd *create_hcd(struct xenbus_device *dev)
+{
+       int i;
+       int err = 0;
+       int num_ports;
+       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);
+       }
+
+       hcd = usb_create_hcd(&usbfront_hc_driver, &dev->dev, dev->dev.bus_id);
+       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_RING_SIZE; i++) {
+               info->shadow[i].req.id = i+1;
+               info->shadow[i].urb = NULL;
+       }
+       info->shadow[USB_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;
+       char name[TASK_COMM_LEN];
+
+       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->dev.driver_data = 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);
+       snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum);
+       info->kthread = kthread_run(xenhcd_schedule, info, name);
+        if (IS_ERR(info->kthread)) {
+                err = PTR_ERR(info->kthread);
+                info->kthread = NULL;
+                goto fail;
+        }
+
+       err = talk_to_backend(dev, info);
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       usb_put_hcd(hcd);
+       dev->dev.driver_data = NULL;
+       return err;
+}
+
+/*
+ * 0=disconnected, 1=low_speed, 2=full_speed, 3=high_speed
+ */
+static void usbfront_do_hotplug(struct usbfront_info *info)
+{
+       char port_str[8];
+       int i;
+       int err;
+       int state;
+
+       for (i = 1; i <= info->rh_numports; i++) {
+               sprintf(port_str, "port-%d", i);
+               err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                                       port_str, "%d", &state);
+               if (err == 1)
+                       xenhcd_rhport_state_change(info, i, state);
+       }
+}
+
+static void backend_changed(struct xenbus_device *dev,
+                                    enum xenbus_state backend_state)
+{
+       struct usbfront_info *info = dev->dev.driver_data;
+
+       switch (backend_state) {
+       case XenbusStateInitialising:
+       case XenbusStateInitWait:
+       case XenbusStateInitialised:
+       case XenbusStateUnknown:
+       case XenbusStateClosed:
+               break;
+
+       case XenbusStateConnected:
+               if (dev->state == XenbusStateConnected)
+                       break;
+               if (dev->state == XenbusStateInitialised)
+                       usbfront_do_hotplug(info);
+               xenbus_switch_state(dev, XenbusStateConnected);
+               break;
+
+       case XenbusStateClosing:
+               xenbus_frontend_closed(dev);
+               break;
+
+       case XenbusStateReconfiguring:
+               if (dev->state == XenbusStateConnected)
+                       xenbus_switch_state(dev, XenbusStateReconfiguring);
+               break;
+
+       case XenbusStateReconfigured:
+               usbfront_do_hotplug(info);
+               xenbus_switch_state(dev, XenbusStateConnected);
+               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->dev.driver_data;
+       struct usb_hcd *hcd = info_to_hcd(info);
+
+       usb_remove_hcd(hcd);
+       if (info->kthread) {
+               kthread_stop(info->kthread);
+               info->kthread = NULL;
+       }
+       usbif_free(info);
+       usb_put_hcd(hcd);
+
+       return 0;
+}
+
+static const struct xenbus_device_id usbfront_ids[] = {
+       { "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 (!is_running_on_xen())
+               return -ENODEV;
+
+       xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv",
+                       sizeof(struct urb_priv), 0, 0, NULL, 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(DRIVER_DESC);
+MODULE_LICENSE("Dual BSD/GPL");

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [linux-2.6.18-xen] PVUSB: frontend driver, Xen patchbot-linux-2.6.18-xen <=