All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] USB virtualisation (experimental)
@ 2005-01-10 17:03 Mark Williamson
  2005-01-11  0:24 ` Nivedita Singhvi
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Mark Williamson @ 2005-01-10 17:03 UTC (permalink / raw)
  To: xen-devel

Hi all,

A number of people have expressed interest in the USB virtualisation code.  
I'm making a patch against 2.0-testing available for those who wish to have a 
play with it.

Description:
USB virtualisation allows otherwise unprivileged domains to be given control 
of a USB port on the host.  The unprivileged domain sees a virtual USB host 
controller with only the ports it has been granted access to.  For instance, 
you might dedicate a USB webcam to one domain, a USB pendrive to another 
domain and a USB CD-burner to another.

This patch includes the kernel modifications (backend and frontend drivers) to 
2.4.28 and modifications to the userspace toolset.  This is sufficient to set 
up USB virtualisation for 2.4.28 on xen-2.0-testing.

There are still a few sticky bugs that are really annoying me at the moment 
but it is possible to mount USB flash drives, capture realtime video from a 
webcam, etc. in a domU by virtualising through dom0.

Disclaimers:
* Don't use it on production systems
* Don't trust it with data you care about
* Some USB device drivers may need tweaking - if you have problems with a 
device, let me know
* YMMV but it works for me ;-)

Installation:
1) download the patch from
http://www.cambridge.intel-research.net/~mwilli2/xen-2.0-testing-usb.patch
2) apply to a fresh xen-2.0-testing.bk using
patch -p1 < xen-2.0-testing-usb.patch
(say yes to all the SCCS checkouts)
3) set up some appropriate kernel configs in 
install/boot/config-2.4.28-xen{0,U}.  You should enable 
CONFIG_XEN_USB_FRONTEND, CONFIG_USB in the configuration for any devices you 
want to run.
Don't (!) build drivers for devices you want to virtualise into dom0's kernel, 
or dom0 will grab the device before the backend gets a chance to export it.
4) build & install the updated kernel & tools on your test machine.  Note 
again you _have_ to use the 2.4 kernels, 2.6 isn't supported (yet).
5) for frontend domains, add a
usb = [ 'port_number', 'port_number', ... ]
to the config file to give a domain control of those physical USB ports.  You 
can specify ports on a hub by giving the path through the device tree e.g. 
'1/2' = second port on the hub plugged into the first port on the host.

Any questions, comments, problems, success reports - let us know!

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] USB virtualisation (experimental)
  2005-01-10 17:03 [PATCH] USB virtualisation (experimental) Mark Williamson
@ 2005-01-11  0:24 ` Nivedita Singhvi
  2005-01-11  3:21   ` Mark Williamson
  2005-01-14  1:38 ` [updated] " Mark A. Williamson
  2005-01-14  2:49 ` Mark Williamson
  2 siblings, 1 reply; 15+ messages in thread
From: Nivedita Singhvi @ 2005-01-11  0:24 UTC (permalink / raw)
  To: Mark Williamson; +Cc: xen-devel

Mark Williamson wrote:

> This patch includes the kernel modifications (backend and frontend drivers) to 
> 2.4.28 and modifications to the userspace toolset.  This is sufficient to set 
> up USB virtualisation for 2.4.28 on xen-2.0-testing.
> 
> There are still a few sticky bugs that are really annoying me at the moment 
> but it is possible to mount USB flash drives, capture realtime video from a 
> webcam, etc. in a domU by virtualising through dom0.

> Any questions, comments, problems, success reports - let us know!

Nice work, Mark! Will be checking this out, but need
a 2.6 version. Will you be working on the 2.6
port in the near future? If not, perhaps we could
help..

thanks,
Nivedita



-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] USB virtualisation (experimental)
  2005-01-11  0:24 ` Nivedita Singhvi
@ 2005-01-11  3:21   ` Mark Williamson
  2005-01-12 14:58     ` aq
  0 siblings, 1 reply; 15+ messages in thread
From: Mark Williamson @ 2005-01-11  3:21 UTC (permalink / raw)
  To: Nivedita Singhvi; +Cc: Mark Williamson, xen-devel

> Nice work, Mark!

Cheers!  Don't get too excited until it works for you too though ;-)

> Will be checking this out, but need 
> a 2.6 version. Will you be working on the 2.6
> port in the near future?

It'll probably be a while - once I've chased down a few more issues and merged 
the patch into BitKeeper, I'm keen to go work on something else for a bit ;-)

I suspect porting the backend will be reasonably straightforward as I don't 
think the USB client API has changed much (at all?).

The frontend will probably require a little of work - the host controller 
interface has changed a bit.  A quick scan of the code doesn't suggest 
anything dramatic, however.

> If not, perhaps we could 
> help..

That would be very welcome :-)  If you have the time to try it out I'd love to 
see a patch that I could merge.

Another thing I'd like to see someday is a BSD frontend.  My frontend is GPL 
so it's probably easiest to rewrite a BSD frontend from scratch.  If anyone 
in the community is interested I could provide assistance.

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] USB virtualisation (experimental)
  2005-01-11  3:21   ` Mark Williamson
@ 2005-01-12 14:58     ` aq
  2005-01-12 15:12       ` Mark Williamson
  0 siblings, 1 reply; 15+ messages in thread
From: aq @ 2005-01-12 14:58 UTC (permalink / raw)
  To: Mark Williamson; +Cc: Nivedita Singhvi, xen-devel

Mark, how did you manage to include in the patch also main.bak, main.c2? ;-)

I am longing to see 2.6 USB virtualization soon. 

Thank you,
AQ


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] USB virtualisation (experimental)
  2005-01-12 14:58     ` aq
@ 2005-01-12 15:12       ` Mark Williamson
  0 siblings, 0 replies; 15+ messages in thread
From: Mark Williamson @ 2005-01-12 15:12 UTC (permalink / raw)
  To: aq; +Cc: Mark Williamson, Nivedita Singhvi, xen-devel

> Mark, how did you manage to include in the patch also main.bak, main.c2?
> ;-)

Oops, sorry :-)  They were just hanging around in the src tree and got plopped 
out when I did the diff.  Ignore them.

Let me know how you get on if you try this out.

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-10 17:03 [PATCH] USB virtualisation (experimental) Mark Williamson
  2005-01-11  0:24 ` Nivedita Singhvi
@ 2005-01-14  1:38 ` Mark A. Williamson
  2005-01-14  2:49 ` Mark Williamson
  2 siblings, 0 replies; 15+ messages in thread
From: Mark A. Williamson @ 2005-01-14  1:38 UTC (permalink / raw)
  To: xen-devel

[-- Attachment #1: Type: text/plain, Size: 3248 bytes --]

All,

Here is an updated USB virtualisation patch.  Things are rather more well 
behaved now.  I just dumped 8 meg of video out of a USB webcam and onto a USB 
pendrive (with both of these peripherals running through the USB virt 
system), no problems at all.

I'm intending to merge this into -unstable fairly soon but in the meantime, 
the patches are against 2.0-testing.

Enjoy,
Mark

On Monday 10 January 2005 17:03, Mark Williamson wrote:
> Hi all,
>
> A number of people have expressed interest in the USB virtualisation code.
> I'm making a patch against 2.0-testing available for those who wish to have
> a play with it.
>
> Description:
> USB virtualisation allows otherwise unprivileged domains to be given
> control of a USB port on the host.  The unprivileged domain sees a virtual
> USB host controller with only the ports it has been granted access to.  For
> instance, you might dedicate a USB webcam to one domain, a USB pendrive to
> another domain and a USB CD-burner to another.
>
> This patch includes the kernel modifications (backend and frontend drivers)
> to 2.4.28 and modifications to the userspace toolset.  This is sufficient
> to set up USB virtualisation for 2.4.28 on xen-2.0-testing.
>
> There are still a few sticky bugs that are really annoying me at the moment
> but it is possible to mount USB flash drives, capture realtime video from a
> webcam, etc. in a domU by virtualising through dom0.
>
> Disclaimers:
> * Don't use it on production systems
> * Don't trust it with data you care about
> * Some USB device drivers may need tweaking - if you have problems with a
> device, let me know
> * YMMV but it works for me ;-)
>
> Installation:
> 1) download the patch from
> http://www.cambridge.intel-research.net/~mwilli2/xen-2.0-testing-usb.patch
> 2) apply to a fresh xen-2.0-testing.bk using
> patch -p1 < xen-2.0-testing-usb.patch
> (say yes to all the SCCS checkouts)
> 3) set up some appropriate kernel configs in
> install/boot/config-2.4.28-xen{0,U}.  You should enable
> CONFIG_XEN_USB_FRONTEND, CONFIG_USB in the configuration for any devices
> you want to run.
> Don't (!) build drivers for devices you want to virtualise into dom0's
> kernel, or dom0 will grab the device before the backend gets a chance to
> export it. 4) build & install the updated kernel & tools on your test
> machine.  Note again you _have_ to use the 2.4 kernels, 2.6 isn't supported
> (yet). 5) for frontend domains, add a
> usb = [ 'port_number', 'port_number', ... ]
> to the config file to give a domain control of those physical USB ports. 
> You can specify ports on a hub by giving the path through the device tree
> e.g. '1/2' = second port on the hub plugged into the first port on the
> host.
>
> Any questions, comments, problems, success reports - let us know!
>
> Cheers,
> Mark
>
>
> -------------------------------------------------------
> The SF.Net email is sponsored by: Beat the post-holiday blues
> Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
> It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/xen-devel

[-- Attachment #2: xen-2.0-testing-usb.patch --]
[-- Type: text/x-diff, Size: 223149 bytes --]

diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/Makefile xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/Makefile
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/Makefile	2005-01-10 03:18:01 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/Makefile	2005-01-04 02:17:23 +00:00
@@ -61,6 +61,7 @@
 SUBDIRS += arch/xen/drivers/evtchn
 SUBDIRS += arch/xen/drivers/blkif
 SUBDIRS += arch/xen/drivers/netif
+SUBDIRS += arch/xen/drivers/usbif
 SUBDIRS += arch/xen/drivers/balloon
 ifdef CONFIG_XEN_PRIVILEGED_GUEST
 SUBDIRS += arch/xen/drivers/dom0 
@@ -71,6 +72,7 @@
 CORE_FILES += arch/xen/drivers/console/drv.o
 DRIVERS += arch/xen/drivers/blkif/drv.o
 DRIVERS += arch/xen/drivers/netif/drv.o
+DRIVERS += arch/xen/drivers/usbif/drv.o
 ifdef CONFIG_XEN_PRIVILEGED_GUEST
 CORE_FILES += arch/xen/drivers/dom0/drv.o
 endif
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/config.in xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/config.in
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/config.in	2005-01-10 03:18:01 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/config.in	2005-01-10 03:13:15 +00:00
@@ -19,11 +19,12 @@
 bool 'Scrub memory before freeing it to Xen' CONFIG_XEN_SCRUB_PAGES
 bool 'Network-device frontend driver' CONFIG_XEN_NETDEV_FRONTEND
 bool 'Block-device frontend driver' CONFIG_XEN_BLKDEV_FRONTEND
+bool 'USB-device frontend driver' CONFIG_XEN_USB_FRONTEND
 endmenu
 # The IBM S/390 patch needs this.
 define_bool CONFIG_NO_IDLE_HZ y
 
-if [ "$CONFIG_XEN_PHYSDEV_ACCESS" == "y" ]; then
+if [ "$CONFIG_XEN_PHYSDEV_ACCESS" = "y" ]; then
    define_bool CONFIG_FOREIGN_PAGES y
 else
    define_bool CONFIG_FOREIGN_PAGES n
@@ -262,7 +263,7 @@
 
 source drivers/char/Config.in
 
-if [ "$CONFIG_XEN_PHYSDEV_ACCESS" = "y" ]; then
+if [ "$CONFIG_XEN_PHYSDEV_ACCESS" = "y" -o "$CONFIG_XEN_USB_FRONTEND" = "y" ]; then
    source drivers/media/Config.in
 fi
 
@@ -295,9 +296,13 @@
       source drivers/sound/Config.in
    fi
    endmenu
+fi
 
+if [ "$CONFIG_XEN_PHYSDEV_ACCESS" = "y" -o "$CONFIG_XEN_USB_FRONTEND" = "y" ]; then
    source drivers/usb/Config.in
+fi
 
+if [ "$CONFIG_XEN_PHYSDEV_ACCESS" = "y" ]; then
    source net/bluetooth/Config.in
 fi
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/Makefile xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/Makefile
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/Makefile	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/Makefile	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,10 @@
+
+O_TARGET := drv.o
+
+subdir-$(CONFIG_XEN_USB_FRONTEND) += frontend
+obj-$(CONFIG_XEN_USB_FRONTEND) += frontend/drv.o
+
+subdir-$(CONFIG_XEN_PHYSDEV_ACCESS) += backend
+obj-$(CONFIG_XEN_PHYSDEV_ACCESS)    += backend/drv.o
+
+include $(TOPDIR)/Rules.make
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/Makefile xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/Makefile
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/Makefile	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/Makefile	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,3 @@
+O_TARGET := drv.o
+obj-y := main.o interface.o control.o # vrh.o don't think I need this!
+include $(TOPDIR)/Rules.make
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/common.h xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/common.h
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/common.h	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/common.h	2005-01-11 14:33:30 +00:00
@@ -0,0 +1,87 @@
+
+#ifndef __USBIF__BACKEND__COMMON_H__
+#define __USBIF__BACKEND__COMMON_H__
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/pgalloc.h>
+#include <asm-xen/ctrl_if.h>
+#include <asm-xen/hypervisor.h>
+
+#include "../usbif.h"
+
+#if 0
+#define ASSERT(_p) \
+    if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
+    __LINE__, __FILE__); *(int*)0=0; }
+#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
+                           __FILE__ , __LINE__ , ## _a )
+#else
+#define ASSERT(_p) ((void)0)
+#define DPRINTK(_f, _a...) ((void)0)
+#endif
+
+typedef struct usbif_priv_st usbif_priv_t;
+
+struct usbif_priv_st {
+    /* Unique identifier for this interface. */
+    domid_t          domid;
+    unsigned int     handle;
+    /* Physical parameters of the comms window. */
+    unsigned long    shmem_frame;
+    unsigned int     evtchn;
+    int              irq;
+    /* Comms information. */
+    usbif_t      *usb_ring_base; /* ioremap()'ed ptr to shmem_frame. */
+    USBIF_RING_IDX     usb_req_cons;  /* Request consumer. */
+    USBIF_RING_IDX     usb_resp_prod; /* Private version of resp. producer. */
+    /* Private fields. */
+    enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
+    /*
+     * DISCONNECT response is deferred until pending requests are ack'ed.
+     * We therefore need to store the id from the original request.
+     */
+    u8                   disconnect_rspid;
+    usbif_priv_t *hash_next;
+    struct list_head     usbif_list;
+    spinlock_t           usb_ring_lock;
+    atomic_t             refcnt;
+    atomic_t             work_scheduled;
+
+    struct work_struct work;
+};
+
+void usbif_create(usbif_be_create_t *create);
+void usbif_destroy(usbif_be_destroy_t *destroy);
+void usbif_connect(usbif_be_connect_t *connect);
+int  usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id);
+void usbif_disconnect_complete(usbif_priv_t *up);
+
+void usbif_release_port(usbif_be_release_port_t *msg);
+int usbif_claim_port(usbif_be_claim_port_t *msg);
+void usbif_release_ports(usbif_priv_t *up);
+
+usbif_priv_t *usbif_find(domid_t domid);
+#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
+#define usbif_put(_b)                             \
+    do {                                          \
+        if ( atomic_dec_and_test(&(_b)->refcnt) ) \
+            usbif_disconnect_complete(_b);        \
+    } while (0)
+
+
+void usbif_interface_init(void);
+void usbif_ctrlif_init(void);
+
+void usbif_deschedule(usbif_priv_t *usbif);
+
+irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs);
+
+#endif /* __USBIF__BACKEND__COMMON_H__ */
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/control.c xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/control.c
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/control.c	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/control.c	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/control.c
+ * 
+ * Routines for interfacing with the control plane.
+ * 
+ * Copyright (c) 2004, Keir Fraser
+ */
+
+#include "common.h"
+
+static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
+{
+    DPRINTK("Received usbif backend message, subtype=%d\n", msg->subtype);
+    
+    switch ( msg->subtype )
+    {
+    case CMSG_USBIF_BE_CREATE:
+        if ( msg->length != sizeof(usbif_be_create_t) )
+            goto parse_error;
+        usbif_create((usbif_be_create_t *)&msg->msg[0]);
+        break;        
+    case CMSG_USBIF_BE_DESTROY:
+        if ( msg->length != sizeof(usbif_be_destroy_t) )
+            goto parse_error;
+        usbif_destroy((usbif_be_destroy_t *)&msg->msg[0]);
+        break;        
+    case CMSG_USBIF_BE_CONNECT:
+        if ( msg->length != sizeof(usbif_be_connect_t) )
+            goto parse_error;
+        usbif_connect((usbif_be_connect_t *)&msg->msg[0]);
+        break;        
+    case CMSG_USBIF_BE_DISCONNECT:
+        if ( msg->length != sizeof(usbif_be_disconnect_t) )
+            goto parse_error;
+        if ( !usbif_disconnect((usbif_be_disconnect_t *)&msg->msg[0],msg->id) )
+            return; /* Sending the response is deferred until later. */
+        break;        
+    case CMSG_USBIF_BE_CLAIM_PORT:
+        if ( msg->length != sizeof(usbif_be_claim_port_t) )
+            goto parse_error;
+	usbif_claim_port((usbif_be_claim_port_t *)&msg->msg[0]);
+        break;
+    case CMSG_USBIF_BE_RELEASE_PORT:
+        if ( msg->length != sizeof(usbif_be_release_port_t) )
+            goto parse_error;
+        usbif_release_port((usbif_be_release_port_t *)&msg->msg[0]);
+        break;
+    default:
+        goto parse_error;
+    }
+
+    ctrl_if_send_response(msg);
+    return;
+
+ parse_error:
+    DPRINTK("Parse error while reading message subtype %d, len %d\n",
+            msg->subtype, msg->length);
+    msg->length = 0;
+    ctrl_if_send_response(msg);
+}
+
+void usbif_ctrlif_init(void)
+{
+    ctrl_msg_t                       cmsg;
+    usbif_be_driver_status_changed_t st;
+
+    (void)ctrl_if_register_receiver(CMSG_USBIF_BE, usbif_ctrlif_rx, 
+                                    CALLBACK_IN_BLOCKING_CONTEXT);
+
+    /* Send a driver-UP notification to the domain controller. */
+    cmsg.type      = CMSG_USBIF_BE;
+    cmsg.subtype   = CMSG_USBIF_BE_DRIVER_STATUS_CHANGED;
+    cmsg.length    = sizeof(usbif_be_driver_status_changed_t);
+    st.status      = USBIF_DRIVER_STATUS_UP;
+    memcpy(cmsg.msg, &st, sizeof(st));
+    ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+}
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/interface.c xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/interface.c
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/interface.c	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/interface.c	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,248 @@
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/interface.c
+ * 
+ * USB device interface management.
+ * 
+ * by Mark Williamson, Copyright (c) 2004
+ */
+
+
+/******************************************************************************
+ * arch/xen/drivers/blkif/backend/interface.c
+ * 
+ * Block-device interface management.
+ * 
+ * Copyright (c) 2004, Keir Fraser
+ */
+
+#include "common.h"
+
+#define USBIF_HASHSZ 1024
+#define USBIF_HASH(_d) (((int)(_d))&(USBIF_HASHSZ-1))
+
+static kmem_cache_t      *usbif_priv_cachep;
+static usbif_priv_t      *usbif_priv_hash[USBIF_HASHSZ];
+
+usbif_priv_t *usbif_find(domid_t domid)
+{
+    usbif_priv_t *up = usbif_priv_hash[USBIF_HASH(domid)];
+    while ( (up != NULL ) && ( up->domid != domid ) )
+        up = up->hash_next;
+    return up;
+}
+
+static void __usbif_disconnect_complete(void *arg)
+{
+    usbif_priv_t         *usbif = (usbif_priv_t *)arg;
+    ctrl_msg_t            cmsg;
+    usbif_be_disconnect_t disc;
+
+    /*
+     * These can't be done in usbif_disconnect() because at that point there
+     * may be outstanding requests at the device whose asynchronous responses
+     * must still be notified to the remote driver.
+     */
+    unbind_evtchn_from_irq(usbif->evtchn);
+    vfree(usbif->usb_ring_base);
+
+    /* Construct the deferred response message. */
+    cmsg.type         = CMSG_USBIF_BE;
+    cmsg.subtype      = CMSG_USBIF_BE_DISCONNECT;
+    cmsg.id           = usbif->disconnect_rspid;
+    cmsg.length       = sizeof(usbif_be_disconnect_t);
+    disc.domid        = usbif->domid;
+    disc.status       = USBIF_BE_STATUS_OKAY;
+    memcpy(cmsg.msg, &disc, sizeof(disc));
+
+    /*
+     * Make sure message is constructed /before/ status change, because
+     * after the status change the 'usbif' structure could be deallocated at
+     * any time. Also make sure we send the response /after/ status change,
+     * as otherwise a subsequent CONNECT request could spuriously fail if
+     * another CPU doesn't see the status change yet.
+     */
+    mb();
+    if ( usbif->status != DISCONNECTING )
+        BUG();
+    usbif->status = DISCONNECTED;
+    mb();
+
+    /* Send the successful response. */
+    ctrl_if_send_response(&cmsg);
+}
+
+void usbif_disconnect_complete(usbif_priv_t *up)
+{
+    INIT_WORK(&up->work, __usbif_disconnect_complete, (void *)up);
+    schedule_work(&up->work);
+}
+
+void usbif_create(usbif_be_create_t *create)
+{
+    domid_t       domid  = create->domid;
+    usbif_priv_t **pup, *up;
+
+    if ( (up = kmem_cache_alloc(usbif_priv_cachep, GFP_KERNEL)) == NULL )
+    {
+        DPRINTK("Could not create usbif: out of memory\n");
+        create->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+        return;
+    }
+
+    memset(up, 0, sizeof(*up));
+    up->domid  = domid;
+    up->status = DISCONNECTED;
+    spin_lock_init(&up->usb_ring_lock);
+    atomic_set(&up->refcnt, 0);
+
+    pup = &usbif_priv_hash[USBIF_HASH(domid)];
+    while ( *pup != NULL )
+    {
+        if ( (*pup)->domid == domid )
+        {
+            create->status = USBIF_BE_STATUS_INTERFACE_EXISTS;
+            kmem_cache_free(usbif_priv_cachep, up);
+            return;
+        }
+        pup = &(*pup)->hash_next;
+    }
+
+    up->hash_next = *pup;
+    *pup = up;
+
+    create->status = USBIF_BE_STATUS_OKAY;
+}
+
+void usbif_destroy(usbif_be_destroy_t *destroy)
+{
+    domid_t       domid  = destroy->domid;
+    usbif_priv_t  **pup, *up;
+
+    pup = &usbif_priv_hash[USBIF_HASH(domid)];
+    while ( (up = *pup) != NULL )
+    {
+        if ( up->domid == domid )
+        {
+            if ( up->status != DISCONNECTED )
+                goto still_connected;
+            goto destroy;
+        }
+        pup = &up->hash_next;
+    }
+
+    destroy->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+    return;
+
+ still_connected:
+    destroy->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
+    return;
+
+ destroy:
+    *pup = up->hash_next;
+    usbif_release_ports(up);
+    kmem_cache_free(usbif_priv_cachep, up);
+    destroy->status = USBIF_BE_STATUS_OKAY;
+}
+
+void usbif_connect(usbif_be_connect_t *connect)
+{
+    domid_t       domid  = connect->domid;
+    unsigned int  evtchn = connect->evtchn;
+    unsigned long shmem_frame = connect->shmem_frame;
+    struct vm_struct *vma;
+    pgprot_t      prot;
+    int           error;
+    usbif_priv_t *up;
+
+    up = usbif_find(domid);
+    if ( unlikely(up == NULL) )
+    {
+        DPRINTK("usbif_connect attempted for non-existent usbif (%u)\n", 
+                connect->domid); 
+        connect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+        return;
+    }
+
+    if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
+    {
+        connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+        return;
+    }
+
+    prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED);
+    error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
+                                    shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
+                                    prot, domid);
+    if ( error != 0 )
+    {
+        if ( error == -ENOMEM )
+            connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+        else if ( error == -EFAULT )
+            connect->status = USBIF_BE_STATUS_MAPPING_ERROR;
+        else
+            connect->status = USBIF_BE_STATUS_ERROR;
+        vfree(vma->addr);
+        return;
+    }
+
+    if ( up->status != DISCONNECTED )
+    {
+        connect->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
+        vfree(vma->addr);
+        return;
+    }
+
+    up->evtchn        = evtchn;
+    up->irq           = bind_evtchn_to_irq(evtchn);
+    up->shmem_frame   = shmem_frame;
+    up->usb_ring_base = (usbif_t *)vma->addr;
+    up->status        = CONNECTED;
+    usbif_get(up);
+
+    request_irq(up->irq, usbif_be_int, 0, "usbif-backend", up);
+
+    connect->status = USBIF_BE_STATUS_OKAY;
+}
+
+/* Remove URBs for this interface before destroying it. */
+void usbif_deschedule(usbif_priv_t *up)
+{
+    remove_from_usbif_list(up);
+}
+
+int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id)
+{
+    domid_t       domid  = disconnect->domid;
+    usbif_priv_t *up;
+
+    up = usbif_find(domid);
+    if ( unlikely(up == NULL) )
+    {
+        DPRINTK("usbif_disconnect attempted for non-existent usbif"
+                " (%u)\n", disconnect->domid); 
+        disconnect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+        return 1; /* Caller will send response error message. */
+    }
+
+    if ( up->status == CONNECTED )
+    {
+        up->status = DISCONNECTING;
+        up->disconnect_rspid = rsp_id;
+        wmb(); /* Let other CPUs see the status change. */
+        free_irq(up->irq, up);
+	usbif_deschedule(up);
+        usbif_put(up);
+        return 0; /* Caller should not send response message. */
+    }
+
+    disconnect->status = USBIF_BE_STATUS_OKAY;
+    return 1;
+}
+
+void __init usbif_interface_init(void)
+{
+    usbif_priv_cachep = kmem_cache_create("usbif_priv_cache",
+					  sizeof(usbif_priv_t), 
+					  0, 0, NULL, NULL);
+    memset(usbif_priv_hash, 0, sizeof(usbif_priv_hash));
+}
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/main.c xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/main.c
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/main.c	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/backend/main.c	2005-01-14 01:27:35 +00:00
@@ -0,0 +1,991 @@
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/main.c
+ * 
+ * Backend for the Xen virtual USB driver - provides an abstraction of a
+ * USB host controller to the corresponding frontend driver.
+ *
+ * by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge
+ *
+ * Based on arch/xen/drivers/blkif/backend/main.c
+ * Copyright (c) 2003-2004, Keir Fraser & Steve Hand
+ */
+
+#include "common.h"
+
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/tqueue.h>
+
+/*
+ * This is rather arbitrary.
+ */
+#define MAX_PENDING_REQS 4
+#define BATCH_PER_DOMAIN 1
+
+static unsigned long mmap_vstart;
+#define MMAP_PAGES_PER_REQUEST \
+    (200)
+#define MMAP_PAGES             \
+    (MAX_PENDING_REQS * MMAP_PAGES_PER_REQUEST)
+
+#define MMAP_VADDR(_req,_seg)                        \
+    (mmap_vstart +                                   \
+     ((_req) * MMAP_PAGES_PER_REQUEST * PAGE_SIZE) + \
+     ((_seg) * PAGE_SIZE))
+
+#define MIN(x,y) ( ( x < y ) ? x : y )
+
+static spinlock_t owned_ports_lock;
+LIST_HEAD(owned_ports);
+
+/* A list of these structures is used to track ownership of physical USB
+ * ports. */
+typedef struct 
+{
+    usbif_priv_t     *usbif_priv;
+    char             path[16];
+    int               guest_port;
+    int enabled;
+    struct list_head  list;
+    unsigned long guest_address; /* The USB device address that has been
+                                  * assigned by the guest. */
+    int               dev_present; /* Is there a device present? */
+    struct usb_device * dev;
+    unsigned long ifaces;  /* What interfaces are present on this device? */
+} owned_port_t;
+
+
+/*
+ * Each outstanding request that we've passed to the lower device layers has a
+ * 'pending_req' allocated to it.  The request is complete, the specified
+ * domain has a response queued for it, with the saved 'id' passed back.
+ */
+typedef struct {
+    usbif_priv_t       *usbif_priv;
+    usbif_iso_t        *iso_sched;
+    unsigned long      id;
+    int                nr_pages;
+    unsigned short     operation;
+    int                status;
+} pending_req_t;
+
+/*
+ * We can't allocate pending_req's in order, since they may complete out of 
+ * order. We therefore maintain an allocation ring. This ring also indicates 
+ * when enough work has been passed down -- at that point the allocation ring 
+ * will be empty.
+ */
+static pending_req_t pending_reqs[MAX_PENDING_REQS];
+static unsigned char pending_ring[MAX_PENDING_REQS];
+static spinlock_t pend_prod_lock = SPIN_LOCK_UNLOCKED;
+
+/* NB. We use a different index type to differentiate from shared blk rings. */
+typedef unsigned int PEND_RING_IDX;
+#define MASK_PEND_IDX(_i) ((_i)&(MAX_PENDING_REQS-1))
+static PEND_RING_IDX pending_prod, pending_cons;
+#define NR_PENDING_REQS (MAX_PENDING_REQS - pending_prod + pending_cons)
+
+static int do_usb_io_op(usbif_priv_t *usbif, int max_to_do);
+static void make_response(usbif_priv_t *usbif, unsigned long id, 
+                          unsigned short op, int st, int inband,
+			  unsigned long actual_length);
+static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long port);
+static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req);    
+static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid);
+static owned_port_t *usbif_find_port(char *);
+
+
+void dump_port(owned_port_t *p)
+{
+    printk("owned_port_t @ %p\n", p);
+    printk("  usbif_priv @ %p\n", p->usbif_priv);
+    printk("  path: %s\n", p->path);
+    printk("  guest_port: %d\n", p->guest_port);
+    printk("  guest_address: %ld\n", p->guest_address);
+    printk("  dev_present: %d\n", p->dev_present);
+    printk("  dev @ %p\n", p->dev);
+    printk("  ifaces: 0x%lx\n", p->ifaces);
+}
+
+
+
+static void fast_flush_area(int idx, int nr_pages)
+{
+    multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
+    int               i;
+
+    for ( i = 0; i < nr_pages; i++ )
+    {
+        mcl[i].op = __HYPERVISOR_update_va_mapping;
+        mcl[i].args[0] = MMAP_VADDR(idx, i) >> PAGE_SHIFT;
+        mcl[i].args[1] = 0;
+        mcl[i].args[2] = 0;
+    }
+
+    mcl[nr_pages-1].args[2] = UVMF_FLUSH_TLB;
+    if ( unlikely(HYPERVISOR_multicall(mcl, nr_pages) != 0) )
+        BUG();
+}
+
+
+/******************************************************************
+ * USB INTERFACE SCHEDULER LIST MAINTENANCE
+ */
+
+static struct list_head usbio_schedule_list;
+static spinlock_t usbio_schedule_list_lock;
+
+static int __on_usbif_list(usbif_priv_t *up)
+{
+    return up->usbif_list.next != NULL;
+}
+
+void remove_from_usbif_list(usbif_priv_t *up)
+{
+    unsigned long flags;
+    if ( !__on_usbif_list(up) ) return;
+    spin_lock_irqsave(&usbio_schedule_list_lock, flags);
+    if ( __on_usbif_list(up) )
+    {
+        list_del(&up->usbif_list);
+        up->usbif_list.next = NULL;
+        usbif_put(up);
+    }
+    spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
+}
+
+static void add_to_usbif_list_tail(usbif_priv_t *up)
+{
+    unsigned long flags;
+    if ( __on_usbif_list(up) ) return;
+    spin_lock_irqsave(&usbio_schedule_list_lock, flags);
+    if ( !__on_usbif_list(up) && (up->status == CONNECTED) )
+    {
+        list_add_tail(&up->usbif_list, &usbio_schedule_list);
+        usbif_get(up);
+    }
+    spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
+}
+
+
+/******************************************************************
+ * COMPLETION CALLBACK -- Called as urb->complete()
+ */
+
+static void maybe_trigger_usbio_schedule(void);
+
+static void __end_usb_io_op(struct urb *purb)
+{
+    unsigned long flags;
+    pending_req_t *pending_req;
+    int pending_idx;
+
+    pending_req = purb->context;
+
+    //    printk("Completed for id = %p\n", pending_req->id);
+
+    pending_idx = pending_req - pending_reqs;
+
+    /* An error fails the entire request. */
+    if ( purb->status )
+    {
+        printk("URB @ %p failed. Status %d\n", purb, purb->status);
+    }
+
+    if ( usb_pipetype(purb->pipe) == 0 )
+    {
+        int i;
+        usbif_iso_t *sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, pending_req->nr_pages - 1);
+
+	//	printk("writing back schedule at %p\n", sched);
+
+        /* If we're dealing with an iso pipe, we need to copy back the schedule. */
+        for ( i = 0; i < purb->number_of_packets; i++ )
+        {
+            sched[i].length = purb->iso_frame_desc[i].actual_length;
+            ASSERT(sched[i].buffer_offset ==
+                   purb->iso_frame_desc[i].offset);
+            sched[i].status = purb->iso_frame_desc[i].status;
+        }
+    }
+    
+    //    printk("Flushing %d pages\n", pending_req->nr_pages);
+    fast_flush_area(pending_req - pending_reqs, pending_req->nr_pages);
+
+    kfree(purb->setup_packet);
+
+    spin_lock_irqsave(&pending_req->usbif_priv->usb_ring_lock, flags);
+    make_response(pending_req->usbif_priv, pending_req->id,
+		  pending_req->operation, pending_req->status, 0, purb->actual_length);
+    spin_unlock_irqrestore(&pending_req->usbif_priv->usb_ring_lock, flags);
+    usbif_put(pending_req->usbif_priv);
+
+    usb_free_urb(purb);
+
+    /* Free the pending request. */
+    spin_lock_irqsave(&pend_prod_lock, flags);
+    pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
+    spin_unlock_irqrestore(&pend_prod_lock, flags);
+
+    rmb();
+
+    /* Check for anything still waiting in the rings, having freed a request... */
+    maybe_trigger_usbio_schedule();
+}
+
+/******************************************************************
+ * SCHEDULER FUNCTIONS
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(usbio_schedule_wait);
+
+static int usbio_schedule(void *arg)
+{
+    DECLARE_WAITQUEUE(wq, current);
+
+    usbif_priv_t          *up;
+    struct list_head *ent;
+
+    daemonize();
+
+    for ( ; ; )
+    {
+        /* Wait for work to do. */
+        add_wait_queue(&usbio_schedule_wait, &wq);
+        set_current_state(TASK_INTERRUPTIBLE);
+        if ( (NR_PENDING_REQS == MAX_PENDING_REQS) || 
+             list_empty(&usbio_schedule_list) )
+            schedule();
+        __set_current_state(TASK_RUNNING);
+        remove_wait_queue(&usbio_schedule_wait, &wq);
+
+        /* Queue up a batch of requests. */
+        while ( (NR_PENDING_REQS < MAX_PENDING_REQS) &&
+                !list_empty(&usbio_schedule_list) )
+        {
+            ent = usbio_schedule_list.next;
+            up = list_entry(ent, usbif_priv_t, usbif_list);
+            usbif_get(up);
+            remove_from_usbif_list(up);
+            if ( do_usb_io_op(up, BATCH_PER_DOMAIN) )
+                add_to_usbif_list_tail(up);
+            usbif_put(up);
+        }
+    }
+}
+
+static void maybe_trigger_usbio_schedule(void)
+{
+    /*
+     * Needed so that two processes, who together make the following predicate
+     * true, don't both read stale values and evaluate the predicate
+     * incorrectly. Incredibly unlikely to stall the scheduler on x86, but...
+     */
+    smp_mb();
+
+    if ( !list_empty(&usbio_schedule_list) )
+        wake_up(&usbio_schedule_wait);
+}
+
+
+/******************************************************************************
+ * NOTIFICATION FROM GUEST OS.
+ */
+
+irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+    usbif_priv_t *up = dev_id;
+
+    smp_mb();
+
+    add_to_usbif_list_tail(up); 
+
+    /* Will in fact /always/ trigger an io schedule in this case. */
+    maybe_trigger_usbio_schedule();
+
+    return IRQ_HANDLED;
+}
+
+
+
+/******************************************************************
+ * DOWNWARD CALLS -- These interface with the usb-device layer proper.
+ */
+
+static int do_usb_io_op(usbif_priv_t *up, int max_to_do)
+{
+    usbif_t *usb_ring = up->usb_ring_base;
+    usbif_request_t *req;
+    USBIF_RING_IDX i, rp;
+    int more_to_do = 0;
+    unsigned long flags;
+
+    spin_lock_irqsave(&up->usb_ring_lock, flags);
+
+    rp = usb_ring->req_prod;
+    rmb(); /* Ensure we see queued requests up to 'rp'. */
+    
+    /* Take items off the comms ring, taking care not to overflow. */
+    for ( i = up->usb_req_cons; 
+          (i != rp) && ((i-up->usb_resp_prod) != USBIF_RING_SIZE);
+          i++ )
+    {
+        if ( (max_to_do-- == 0) || (NR_PENDING_REQS == MAX_PENDING_REQS) )
+        {
+            more_to_do = 1;
+            break;
+        }
+
+        req = &usb_ring->ring[MASK_USBIF_IDX(i)].req;
+        
+        switch ( req->operation )
+        {
+        case USBIF_OP_PROBE:
+            dispatch_usb_probe(up, req->id, req->port);
+            break;
+
+        case USBIF_OP_IO:
+	  /* Assemble an appropriate URB. */
+	  dispatch_usb_io(up, req);
+          break;
+
+	case USBIF_OP_RESET:
+	  dispatch_usb_reset(up, req->port);
+          break;
+
+        default:
+            DPRINTK("error: unknown USB io operation [%d]\n",
+                    req->operation);
+            make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+            break;
+        }
+    }
+
+    up->usb_req_cons = i;
+
+    spin_unlock_irqrestore(&up->usb_ring_lock, flags);
+
+    return more_to_do;
+}
+
+static owned_port_t *find_guest_port(usbif_priv_t *up, int port)
+{
+    unsigned long flags;
+    struct list_head *l;
+
+    spin_lock_irqsave(&owned_ports_lock, flags);
+    list_for_each(l, &owned_ports)
+    {
+        owned_port_t *p = list_entry(l, owned_port_t, list);
+        if(p->usbif_priv == up && p->guest_port == port)
+        {
+            spin_unlock_irqrestore(&owned_ports_lock, flags);
+            return p;
+        }
+    }
+    spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+    return NULL;
+}
+
+static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid)
+{
+    owned_port_t *port = find_guest_port(up, portid);
+    int ret = 0;
+
+
+    /* Allowing the guest to actually reset the device causes more problems
+     * than it's worth.  We just fake it out in software but we will do a real
+     * reset when the interface is destroyed. */
+
+#if 0
+    printk("Reset port %d\n", portid);
+
+    dump_port(port);
+#endif
+
+    port->guest_address = 0;
+    /* If there's an attached device then the port is now enabled. */
+    if ( port->dev_present )
+        port->enabled = 1;
+    else
+        port->enabled = 0;
+
+    make_response(up, 0, USBIF_OP_RESET, ret, 0, 0);
+}
+
+static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long portid)
+{
+    owned_port_t *port = find_guest_port(up, portid);
+    int ret;
+ 
+    if ( port != NULL )
+        ret = port->dev_present;
+    else
+    {
+        ret = -EINVAL;
+        printk("dispatch_usb_probe(): invalid port probe request (port %ld)\n",
+	       portid);
+    }
+
+    /* Probe result is sent back in-band.  Probes don't have an associated id
+     * right now... */
+    make_response(up, id, USBIF_OP_PROBE, ret, portid, 0);
+}
+
+owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req);
+
+static void dump_request(usbif_request_t *req)
+{    
+    printk("id = 0x%lx\n", req->id);
+    
+	printk("devnum %d\n", req->devnum);
+	printk("endpoint 0x%x\n", req->endpoint);
+	printk("direction %d\n", req->direction);
+	printk("speed %d\n", req->speed);
+        printk("pipe_type 0x%x\n", req->pipe_type);
+        printk("transfer_buffer 0x%lx\n", req->transfer_buffer);
+        printk("length 0x%lx\n", req->length);
+        printk("transfer_flags 0x%lx\n", req->transfer_flags);
+        printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+               req->setup[0], req->setup[1], req->setup[2], req->setup[3],
+               req->setup[4], req->setup[5], req->setup[6], req->setup[7]);
+        printk("iso_schedule = 0x%lx\n", req->iso_schedule);
+        printk("num_iso %ld\n", req->num_iso);
+}
+
+void dump_urb(struct urb *urb)
+{
+    printk("dumping urb @ %p\n", urb);
+
+#define DUMP_URB_FIELD(name, format) printk("  " # name " " format "\n", urb-> name)
+    
+    DUMP_URB_FIELD(pipe, "0x%x");
+    DUMP_URB_FIELD(status, "%d");
+    DUMP_URB_FIELD(transfer_flags, "0x%x");    
+    DUMP_URB_FIELD(transfer_buffer, "%p");
+    DUMP_URB_FIELD(transfer_buffer_length, "%d");
+    DUMP_URB_FIELD(actual_length, "%d");
+}
+
+
+static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
+{
+    unsigned long buffer_mach;
+    int i = 0, offset = 0,
+        pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
+    pending_req_t *pending_req;
+    unsigned long  remap_prot;
+    multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
+    struct urb *purb = NULL;
+    owned_port_t *port;
+    char *setup;    
+
+/*     printk("request to up @ %p, dom = %d, refcnt = %d dev = %d\n", */
+/* 	   up, up->domid, up->refcnt, req->devnum); */
+
+//    dump_request(req);
+
+    if ( NR_PENDING_REQS == MAX_PENDING_REQS )
+    {
+        printk("usbback: Max requests already queued.  Now giving up!\n");
+        
+        return;
+    }
+
+    port = find_port_for_request(up, req);
+
+    if(port == NULL)
+    {
+	printk("No such device! (%d)\n", req->devnum);
+        dump_request(req);
+
+        make_response(up, req->id, req->operation, -ENODEV, 0, 0);
+	return;
+    }
+
+    setup = kmalloc(8, GFP_ATOMIC | GFP_NOIO);
+
+    if ( setup == NULL )
+        goto no_mem;
+   
+    /* Copy request out for safety. */
+    memcpy(setup, req->setup, 8);
+
+    if( setup[0] == 0x0 && setup[1] == 0x5)
+    {
+        /* To virtualise the USB address space, we need to intercept
+         * set_address messages and emulate.  From the USB specification:
+         * bmRequestType = 0x0;
+         * Brequest = SET_ADDRESS (i.e. 0x5)
+         * wValue = device address
+         * wIndex = 0
+         * wLength = 0
+         * data = None
+         */
+        /* Store into the guest transfer buffer using cpu_to_le16 */
+        port->guest_address = le16_to_cpu(*(u16 *)(setup + 2));
+        /* Make a successful response.  That was easy! */
+
+        make_response(up, req->id, req->operation, 0, 0, 0);
+
+	kfree(setup);
+        return;
+    }
+    else if ( setup[0] == 0x0 && setup[1] == 0x9 )
+    {
+        /* The host kernel needs to know what device configuration is in use
+         * because various error checks get confused otherwise.  We just do
+         * configuration settings here, under controlled conditions.
+         */
+        usb_set_configuration(port->dev, setup[2]);
+
+        make_response(up, req->id, req->operation, 0, 0, 0);
+
+        kfree(setup);
+        return;
+    }
+
+    else if ( setup[0] == 0x1 && setup[1] == 0xB )
+    {
+        /* The host kernel needs to know what device interface is in use
+         * because various error checks get confused otherwise.  We just do
+         * configuration settings here, under controlled conditions.
+         */
+        usb_set_interface(port->dev, (setup[4] | setup[5] << 8),
+                          (setup[2] | setup[3] << 8) );
+
+        make_response(up, req->id, req->operation, 0, 0, 0);
+
+        kfree(setup);
+        return;
+    }
+
+    if ( req->length > MMAP_PAGES_PER_REQUEST * PAGE_SIZE )
+    {
+        printk("usbback: request of %d bytes too large, failing it\n");
+        make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+        kfree(setup);
+        return;
+    }
+    
+    buffer_mach = req->transfer_buffer;
+
+    if( buffer_mach == 0 )
+	goto no_remap;
+
+    /* Always map writeable for now. */
+    remap_prot = _PAGE_PRESENT|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW;
+
+    for ( i = 0, offset = 0; offset < req->length;
+          i++, offset += PAGE_SIZE )
+    {
+      //        printk("length = %d, offset = %d, looping!\n", req->length, offset);
+        
+	mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
+	mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
+        mcl[i].args[1] = (buffer_mach & PAGE_MASK) + offset | remap_prot;
+        mcl[i].args[2] = 0;
+        mcl[i].args[3] = up->domid;
+        
+        phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
+            FOREIGN_FRAME(buffer_mach + offset >> PAGE_SHIFT);
+	//	printk("i = %d\n", i);
+    }
+
+    if ( req->pipe_type == 0 && req->num_iso > 0 ) /* Maybe schedule ISO... */
+    {
+      //      printk("for iso, i = %d\n", i);
+        /* Map in ISO schedule, if necessary. */
+        mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
+        mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
+        mcl[i].args[1] = (req->iso_schedule & PAGE_MASK) | remap_prot;
+        mcl[i].args[2] = 0;
+        mcl[i].args[3] = up->domid;
+
+    phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
+        FOREIGN_FRAME(req->iso_schedule >> PAGE_SHIFT);
+
+    //    printk("Mapped iso at %p\n", MMAP_VADDR(pending_idx, i));
+    i++;
+    }
+
+    //    printk("Well we got this far!\n");
+
+    if ( unlikely(HYPERVISOR_multicall(mcl, i) != 0) )
+        BUG();
+    
+    {
+        int j;
+        for ( j = 0; j < i; j++ )
+        {
+            if ( unlikely(mcl[j].args[5] != 0) )
+            {
+                printk("invalid buffer %d -- could not remap it\n", j);
+                fast_flush_area(pending_idx, i);
+		printk("sending invalid descriptor\n");
+                goto bad_descriptor;
+            }
+	}
+    }
+    
+
+ no_remap:
+
+    /* We have to do this because some things might complete out of order. */
+    pending_req = &pending_reqs[pending_idx];
+    pending_req->usbif_priv= up;
+    pending_req->id        = req->id;
+    pending_req->operation = req->operation;
+    pending_req->nr_pages  = i;
+
+    pending_cons++;
+
+    usbif_get(up);
+    
+    /* Fill out an actual request for the USB layer. */
+    purb = usb_alloc_urb(req->num_iso);
+
+    if ( purb == NULL )
+        goto no_mem;
+
+    purb->dev = port->dev;
+    purb->context = pending_req;
+    purb->transfer_buffer = (void *)MMAP_VADDR(pending_idx, 0) + (buffer_mach & ~PAGE_MASK);
+    if(buffer_mach == 0)
+      purb->transfer_buffer = NULL;
+    purb->complete = __end_usb_io_op;
+    purb->transfer_buffer_length = req->length;
+    purb->transfer_flags = req->transfer_flags;
+
+/*     if ( req->transfer_flags != 0 ) */
+/*       dump_request(req); */
+
+    purb->pipe = 0;
+    purb->pipe |= req->direction << 7;
+    purb->pipe |= port->dev->devnum << 8;
+    purb->pipe |= req->speed << 26;
+    purb->pipe |= req->pipe_type << 30;
+    purb->pipe |= req->endpoint << 15;
+
+    purb->number_of_packets = req->num_iso;
+
+    /* Make sure there's always some kind of timeout. */
+    purb->timeout = ( req->timeout > 0 ) ?  (req->timeout * HZ) / 1000
+                    :  1000;
+
+    purb->setup_packet = setup;
+
+    if ( req->pipe_type == 0 ) /* ISO */
+    {
+        int j;
+        usbif_iso_t *iso_sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, i - 1);
+
+	//	printk("Reading iso sched at %p\n", iso_sched);
+
+        /* If we're dealing with an iso pipe, we need to copy in a schedule. */
+        for ( j = 0; j < req->num_iso; j++ )
+        {
+            purb->iso_frame_desc[j].length = iso_sched[j].length;
+            purb->iso_frame_desc[j].offset = iso_sched[j].buffer_offset;
+            iso_sched[j].status = 0;
+        }
+        pending_req->iso_sched = iso_sched;
+    }
+
+    {
+      int ret;
+      ret = usb_submit_urb(purb);
+
+      //      dump_urb(purb);
+
+      if ( ret != 0 )
+          goto bad_descriptor; /* XXX free pending here! */
+    }
+    
+    return;
+
+ bad_descriptor:
+    kfree ( setup );
+    if ( purb != NULL )
+        usb_free_urb(purb);
+    make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+    return;
+    
+ no_mem:
+    if ( setup != NULL )
+        kfree(setup);
+    make_response(up, req->id, req->operation, -ENOMEM, 0, 0);
+    return;
+} 
+
+
+
+/******************************************************************
+ * MISCELLANEOUS SETUP / TEARDOWN / DEBUGGING
+ */
+
+
+static void make_response(usbif_priv_t *up, unsigned long id,
+                          unsigned short op, int st, int inband,
+			  unsigned long length)
+{
+    usbif_response_t *resp;
+    unsigned long     flags;
+
+#if 0
+    printk("usbback: Sending response:\n");
+    printk("         id = 0x%x\n", id);
+    printk("         op = %d\n", op);
+    printk("         status = %d\n", st);
+    printk("         data = %d\n", inband);
+    printk("         length = %d\n", length);
+#endif
+
+    /* Place on the response ring for the relevant domain. */ 
+    spin_lock_irqsave(&up->usb_ring_lock, flags);
+    resp = &up->usb_ring_base->
+        ring[MASK_USBIF_IDX(up->usb_resp_prod)].resp;
+    resp->id        = id;
+    resp->operation = op;
+    resp->status    = st;
+    resp->data      = inband;
+    resp->length = length;
+    wmb(); /* Ensure other side can see the response fields. */
+    up->usb_ring_base->resp_prod = ++up->usb_resp_prod;
+    spin_unlock_irqrestore(&up->usb_ring_lock, flags);
+
+    /* Kick the relevant domain. */
+    notify_via_evtchn(up->evtchn);
+}
+
+/**
+ * usbif_claim_port - claim devices on a port on behalf of guest
+ *
+ * Once completed, this will ensure that any device attached to that
+ * port is claimed by this driver for use by the guest.
+ */
+int usbif_claim_port(usbif_be_claim_port_t *msg)
+{
+    owned_port_t *o_p;
+    
+    /* Sanity... */
+    if ( usbif_find_port(msg->path) != NULL )
+    {
+        printk("usbback: Attempted to claim USB port "
+               "we already own!\n");
+        return -EINVAL;
+    }
+
+    spin_lock_irq(&owned_ports_lock);
+    
+    /* No need for a slab cache - this should be infrequent. */
+    o_p = kmalloc(sizeof(owned_port_t), GFP_KERNEL);
+
+    o_p->enabled = 0;
+    o_p->usbif_priv = usbif_find(msg->domid);
+    o_p->guest_port = msg->usbif_port;
+    o_p->dev_present = 0;
+    o_p->guest_address = 0; /* Default address. */
+
+    strcpy(o_p->path, msg->path);
+
+    list_add(&o_p->list, &owned_ports);
+
+    printk("usbback: Claimed USB port (%s) for %d.%d\n", o_p->path,
+	   msg->domid, msg->usbif_port);
+
+    spin_unlock_irq(&owned_ports_lock);
+
+    /* Force a reprobe for unclaimed devices. */
+    usb_scan_devices();
+
+    return 0;
+}
+
+owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req)
+{
+    unsigned long flags;
+    struct list_head *port;
+
+    /* I'm assuming this is not called from IRQ context - correct?  I think
+     * it's probably only called in response to control messages or plug events
+     * in the USB hub kernel thread, so should be OK. */
+    spin_lock_irqsave(&owned_ports_lock, flags);
+    list_for_each(port, &owned_ports)
+    {
+        owned_port_t *p = list_entry(port, owned_port_t, list);
+        if(p->usbif_priv == up && p->guest_address == req->devnum && p->enabled )
+	  {
+#if 0
+              printk("Found port for devnum %d\n", req->devnum);
+
+              dump_port(p);
+#endif
+              return p;
+	  }
+    }
+    spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+    return NULL;    
+}
+
+owned_port_t *usbif_find_port(char *path)
+{
+    struct list_head *port;
+    unsigned long flags;
+
+    spin_lock_irqsave(&owned_ports_lock, flags);
+    list_for_each(port, &owned_ports)
+    {
+        owned_port_t *p = list_entry(port, owned_port_t, list);
+        if(!strcmp(path, p->path))
+        {
+            spin_unlock_irqrestore(&owned_ports_lock, flags);
+            return p;
+        }
+    }
+    spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+    return NULL;
+}
+
+
+static void *probe(struct usb_device *dev, unsigned iface,
+	    const struct usb_device_id *id)
+{
+    owned_port_t *p;
+
+    /* We don't care what the device is - if we own the port, we want it.  We
+     * don't deal with device-specifics in this driver, so we don't care what
+     * the device actually is ;-) */
+    if ( ( p = usbif_find_port(dev->devpath) ) != NULL )
+    {
+        printk("usbback: claimed device attached to owned port\n");
+
+        p->dev_present = 1;
+        p->dev = dev;
+        set_bit(iface, &p->ifaces);
+        
+        return p->usbif_priv;
+    }
+    else
+        printk("usbback: hotplug for non-owned port (%s), ignoring\n", dev->devpath);
+   
+
+    return NULL;
+}
+
+static void disconnect(struct usb_device *dev, void *usbif)
+{
+    /* Note the device is removed so we can tell the guest when it probes. */
+    owned_port_t *port = usbif_find_port(dev->devpath);
+    port->dev_present = 0;
+    port->dev = NULL;
+    port->ifaces = 0;
+}
+
+
+struct usb_driver driver =
+{
+    .owner      = THIS_MODULE,
+    .name       = "Xen USB Backend",
+    .probe      = probe,
+    .disconnect = disconnect,
+    .id_table   = NULL,
+};
+
+/* __usbif_release_port - internal mechanics for releasing a port */
+void __usbif_release_port(owned_port_t *p)
+{
+    int i;
+
+    for ( i = 0; p->ifaces != 0; i++)
+        if ( p->ifaces & 1 << i )
+        {
+            usb_driver_release_interface(&driver, usb_ifnum_to_if(p->dev, i));
+            clear_bit(i, &p->ifaces);
+        }
+    list_del(&p->list);
+
+    /* Reset the real device.  We don't simulate disconnect / probe for other
+     * drivers in this kernel because we assume the device is completely under
+     * the control of ourselves (i.e. the guest!).  This should ensure that the
+     * device is in a sane state for the next customer ;-) */
+    if ( p->dev != NULL)
+        usb_reset_device(p->dev);
+
+    kfree(p);
+}
+
+
+/**
+ * usbif_release_port - stop claiming devices on a port on behalf of guest
+ */
+void usbif_release_port(usbif_be_release_port_t *msg)
+{
+    owned_port_t *p;
+
+    spin_lock_irq(&owned_ports_lock);
+    p = usbif_find_port(msg->path);
+    __usbif_release_port(p);
+    spin_unlock_irq(&owned_ports_lock);
+}
+
+void usbif_release_ports(usbif_priv_t *up)
+{
+    struct list_head *port, *tmp;
+    unsigned long flags;
+    
+    spin_lock_irqsave(&owned_ports_lock, flags);
+    list_for_each_safe(port, tmp, &owned_ports)
+    {
+        owned_port_t *p = list_entry(port, owned_port_t, list);
+        if ( p->usbif_priv == up )
+            __usbif_release_port(p);
+    }
+    spin_unlock_irqrestore(&owned_ports_lock, flags);
+}
+
+static int __init usbif_init(void)
+{
+    int i;
+
+    if ( !(xen_start_info.flags & SIF_INITDOMAIN) &&
+         !(xen_start_info.flags & SIF_USB_BE_DOMAIN) )
+        return 0;
+    
+    INIT_LIST_HEAD(&owned_ports);
+
+    usb_register(&driver);
+
+    usbif_interface_init();
+
+    if ( (mmap_vstart = allocate_empty_lowmem_region(MMAP_PAGES)) == 0 )
+        BUG();
+
+    pending_cons = 0;
+    pending_prod = MAX_PENDING_REQS;
+    memset(pending_reqs, 0, sizeof(pending_reqs));
+    for ( i = 0; i < MAX_PENDING_REQS; i++ )
+        pending_ring[i] = i;
+
+    spin_lock_init(&usbio_schedule_list_lock);
+    INIT_LIST_HEAD(&usbio_schedule_list);
+
+    if ( kernel_thread(usbio_schedule, 0, CLONE_FS | CLONE_FILES) < 0 )
+        BUG();
+    
+    usbif_ctrlif_init();
+
+    spin_lock_init(&owned_ports_lock);
+
+    printk("Xen USB Backend Initialised");
+
+    return 0;
+}
+
+__initcall(usbif_init);
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/Makefile xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/Makefile
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/Makefile	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/Makefile	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,3 @@
+O_TARGET := drv.o
+obj-y := main.o
+include $(TOPDIR)/Rules.make
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/main.c xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/main.c
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/main.c	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/main.c	2005-01-13 23:19:25 +00:00
@@ -0,0 +1,1714 @@
+/*
+ * Xen Virtual USB Frontend Driver 
+ *
+ * This file contains the first version of the Xen virtual USB hub
+ * that I've managed not to delete by mistake (3rd time lucky!).
+ *
+ * Based on Linux's uhci.c, original copyright notices are displayed
+ * below.  Portions also (c) 2004 Intel Research Cambridge
+ * and (c) 2004 Mark Williamson
+ *
+ * Contact <mark.williamson@cl.cam.ac.uk> or
+ * <xen-devel@lists.sourceforge.net> regarding this code.
+ *
+ * Still to be (maybe) implemented:
+ * - multiple port
+ * - multiple interfaces
+ * - migration / backend restart support?
+ * - unloading support
+ *
+ * Differences to a normal host controller:
+ * - the backend does most of the mucky stuff so we don't have to do various
+ *   things that are necessary for a normal host controller (e.g. FSBR).
+ * - we don't have any hardware, so status registers are simulated in software.
+ */
+
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ *  - the low-level protocol (fairly simple but lots of small details)
+ *  - working around the horridness of the rest
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "xhci.h"
+
+#include <linux/pm.h>
+
+#include "../../../../../drivers/usb/hcd.h"
+
+#include "../usbif.h"
+#include <asm/ctrl_if.h>
+#include <asm/xen-public/io/domain_controller.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, Mark Williamson"
+#define DRIVER_DESC "Xen Virtual USB Host Controller Interface driver"
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ */
+#ifdef DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN    (PAGE_SIZE * 8)
+
+static kmem_cache_t *xhci_up_cachep;	/* urb_priv */
+
+static int rh_submit_urb(struct urb *urb);
+static int rh_unlink_urb(struct urb *urb);
+//static int xhci_get_current_frame_number(struct usb_device *dev);
+static int xhci_unlink_urb(struct urb *urb);
+static void xhci_unlink_generic(struct urb *urb);
+static void xhci_call_completion(struct urb *urb);
+static void xhci_drain_ring(void);
+
+#define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
+
+struct xhci *xhci;
+
+enum { USBIF_STATE_CONNECTED = 2,
+       USBIF_STATE_DISCONNECTED = 1,
+       USBIF_STATE_CLOSED =0
+};
+
+static int awaiting_reset = 0;
+
+/**
+ * xhci_construct_isoc - add isochronous information to a request
+ */
+int xhci_construct_isoc(usbif_request_t *req, struct urb *urb)
+{
+        usbif_iso_t *schedule;
+        int i;
+        struct urb_priv *urb_priv = urb->hcpriv;
+        
+        req->num_iso = urb->number_of_packets;
+        schedule = (usbif_iso_t *)__get_free_page(GFP_KERNEL);
+
+        if ( schedule == NULL )
+            return -ENOMEM;
+
+        for ( i = 0; i < req->num_iso; i++ )
+        {
+                schedule[i].buffer_offset = urb->iso_frame_desc[i].offset;
+                schedule[i].length = urb->iso_frame_desc[i].length;
+        }
+
+        urb_priv->schedule = schedule;
+	req->iso_schedule = virt_to_machine(schedule);
+
+        return 0;
+}
+
+#define USBIF_RING_FULL ((xhci->usbif->req_prod - xhci->usb_resp_cons) == USBIF_RING_SIZE)
+
+static void dump_urb(struct urb *urb)
+{
+        printk("dumping urb @ %p\n", urb);
+        
+        printk("hcpriv = %p\n", urb->hcpriv);
+        printk("next = %p\n", urb->next);
+        printk("dev = %p\n", urb->dev);
+        printk("pipe = 0x%lx\n", urb->pipe);
+        printk("status = %d\n", urb->status);
+        printk("transfer_flags = 0x%lx\n", urb->transfer_flags);
+        printk("transfer_buffer = %p\n", urb->transfer_buffer);
+        printk("transfer_buffer_length = %d\n", urb->transfer_buffer_length);
+        printk("actual_length = %d\n", urb->actual_length);
+        printk("bandwidth = %d\n", urb->bandwidth);
+        printk("setup_packet = %p\n", urb->setup_packet);
+        printk("complete = %p\n", urb->complete);
+        printk("interval = %d\n", urb->interval);
+        
+}
+
+
+static int
+xhci_queue_req(struct urb *urb)
+{
+        usbif_request_t *req;
+        usbif_t *usbif = xhci->usbif;
+
+#if 0
+        printk("usbif = %p, req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
+               usbif, usbif->req_prod, virt_to_machine(&usbif->req_prod),
+               usbif->resp_prod, xhci->usb_resp_cons);
+#endif
+        
+
+        if ( USBIF_RING_FULL )
+        {
+                printk("xhci_queue_req(): USB ring full, not queuing request\n");
+                return -ENOBUFS;
+        }
+
+        /* Stick something in the shared communications ring. */
+        req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+        req->operation       = USBIF_OP_IO;
+        req->port            = 0; /* We don't care what the port is. */
+        req->id              = (unsigned long) urb->hcpriv;
+        req->transfer_buffer = virt_to_machine(urb->transfer_buffer);
+	req->devnum          = usb_pipedevice(urb->pipe);
+        req->direction       = usb_pipein(urb->pipe);
+	req->speed           = usb_pipeslow(urb->pipe);
+        req->pipe_type       = usb_pipetype(urb->pipe);
+        req->length          = urb->transfer_buffer_length;
+        req->transfer_flags  = urb->transfer_flags;
+	req->endpoint        = usb_pipeendpoint(urb->pipe);
+	req->speed           = usb_pipeslow(urb->pipe);
+	req->timeout         = urb->timeout * (1000 / HZ);
+
+        if ( usb_pipetype(urb->pipe) == 0 ) /* ISO */
+        {
+            int ret = xhci_construct_isoc(req, urb);
+            if ( ret != 0 )
+                return ret;
+        }
+
+	if(urb->setup_packet != NULL)
+                memcpy(req->setup, urb->setup_packet, 8);
+        else
+                memset(req->setup, 0, 8);
+        
+        wmb();
+
+        usbif->req_prod++;
+
+	notify_via_evtchn(xhci->evtchn);
+
+	//	dump_urb(urb);
+
+        return -EINPROGRESS;
+}
+
+static inline usbif_request_t *
+xhci_queue_probe(usbif_vdev_t port)
+{
+        usbif_request_t *req;
+        usbif_t *usbif = xhci->usbif;
+
+#if 0
+	printk("queuing probe: req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
+	       usbif->req_prod, virt_to_machine(&usbif->req_prod),
+	       usbif->resp_prod, xhci->usb_resp_cons);
+#endif
+        
+        if ( USBIF_RING_FULL )
+        {
+                printk("xhci_queue_probe(): USB ring full, not queuing request\n");
+                return NULL;
+        }
+
+        /* Stick something in the shared communications ring. */
+        req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+        req->operation       = USBIF_OP_PROBE;
+        req->port            = port;
+        req->id              = 0;
+        req->transfer_buffer = 0;
+	req->devnum          = 0;
+        req->direction       = 0;
+	req->speed           = 0;
+        req->pipe_type       = 0;
+        req->length          = 0;
+        req->transfer_flags  = 0;
+	req->endpoint        = 0;
+	req->speed           = 0;
+
+        wmb();
+
+        usbif->req_prod++;
+
+	notify_via_evtchn(xhci->evtchn);
+
+        return req;
+}
+
+static int
+xhci_port_reset(usbif_vdev_t port)
+{
+        usbif_request_t *req;
+        usbif_t *usbif = xhci->usbif;
+
+        /* We only reset one port at a time, so we only need one variable per
+         * hub. */
+        awaiting_reset = 1;
+        
+        /* Stick something in the shared communications ring. */
+        req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+        req->operation       = USBIF_OP_RESET;
+        req->port            = port;
+        
+        wmb();
+
+        usbif->req_prod++;
+
+	notify_via_evtchn(xhci->evtchn);
+
+        while ( awaiting_reset > 0 )
+        {
+                mdelay(1);
+                xhci_drain_ring();
+        }
+
+        return awaiting_reset;
+}
+
+static void xhci_show_resp(usbif_response_t *r)
+{
+        printk("id=0x%lx, op=0x%x, data=0x%x, status=0x%x, length=0x%lx\n",
+               r->id, r->operation, r->data, r->status, r->length);
+}
+
+
+/*
+ * Only the USB core should call xhci_alloc_dev and xhci_free_dev
+ */
+static int xhci_alloc_dev(struct usb_device *dev)
+{
+	return 0;
+}
+
+static int xhci_free_dev(struct usb_device *dev)
+{
+	return 0;
+}
+
+static inline void xhci_add_complete(struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&xhci->complete_list_lock, flags);
+	list_add_tail(&urbp->complete_list, &xhci->complete_list);
+	spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+}
+
+/* When this returns, the owner of the URB may free its
+ * storage.
+ *
+ * We spin and wait for the URB to complete before returning.
+ */
+static void xhci_delete_urb(struct urb *urb)
+{
+        struct urb_priv *urbp;
+
+	urbp = urb->hcpriv;
+
+        /* If there's no urb_priv structure for this URB then it can't have
+         * been submitted at all. */
+	if ( urbp == NULL )
+		return;
+
+	/* For now we just spin until the URB completes.  It shouldn't take too
+         * long and we don't expect to have to do this very often. */
+	while ( urb->status == -EINPROGRESS )
+        {
+            xhci_drain_ring();
+            mdelay(1);
+        }
+
+	/* Now we know that further transfers to the buffer won't
+	 * occur, so we can safely return. */
+}
+
+static struct urb_priv *xhci_alloc_urb_priv(struct urb *urb)
+{
+	struct urb_priv *urbp;
+
+	urbp = kmem_cache_alloc(xhci_up_cachep, SLAB_ATOMIC);
+	if (!urbp) {
+		err("xhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
+		return NULL;
+	}
+
+	memset((void *)urbp, 0, sizeof(*urbp));
+
+	urbp->inserttime = jiffies;
+	urbp->urb = urb;
+	urbp->dev = urb->dev;
+	
+	INIT_LIST_HEAD(&urbp->complete_list);
+
+	urb->hcpriv = urbp;
+
+	return urbp;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+/* When is this called?  Do we need to stop the transfer (as we
+ * currently do)? */
+static void xhci_destroy_urb_priv(struct urb *urb)
+{
+    struct urb_priv *urbp;
+    
+    urbp = (struct urb_priv *)urb->hcpriv;
+    if (!urbp)
+        return;
+
+    if (!list_empty(&urb->urb_list))
+        warn("xhci_destroy_urb_priv: urb %p still on xhci->urb_list or xhci->remove_list", urb);
+    
+    if (!list_empty(&urbp->complete_list))
+        warn("xhci_destroy_urb_priv: urb %p still on xhci->complete_list", urb);
+    
+    kmem_cache_free(xhci_up_cachep, urb->hcpriv);
+
+    urb->hcpriv = NULL;
+}
+
+/**
+ * Try to find URBs in progress on the same pipe to the same device.
+ *
+ * MUST be called with xhci->urb_list_lock acquired
+ */
+static struct urb *xhci_find_urb_ep(struct xhci *xhci, struct urb *urb)
+{
+	struct list_head *tmp, *head;
+
+	/* We don't match Isoc transfers since they are special */
+	if (usb_pipeisoc(urb->pipe))
+		return NULL;
+
+	head = &xhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		if (u->dev == urb->dev && u->pipe == urb->pipe &&
+		    u->status == -EINPROGRESS)
+			return u;
+	}
+
+	return NULL;
+}
+
+static int xhci_submit_urb(struct urb *urb)
+{
+	int ret = -EINVAL;
+	unsigned long flags;
+	struct urb *eurb;
+	int bustime;
+
+#if 0
+        printk("submitting urb @ %p for dev @ %p, devnum = %d path %s\n",
+               urb, urb->dev, urb->dev->devnum, urb->dev->devpath);
+#endif
+
+	if (!urb)
+		return -EINVAL;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
+		warn("xhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
+		return -ENODEV;
+	}
+
+        if ( urb->dev->devpath == NULL )
+        {
+                printk("BARF!\n");
+                BUG();
+        }
+        
+        
+
+	usb_inc_dev_use(urb->dev);
+
+	spin_lock_irqsave(&xhci->urb_list_lock, flags);
+	spin_lock(&urb->lock);
+
+	if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
+	    urb->status == -ECONNABORTED) {
+		dbg("xhci_submit_urb: urb not available to submit (status = %d)", urb->status);
+		/* Since we can have problems on the out path */
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+		usb_dec_dev_use(urb->dev);
+
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&urb->urb_list);
+	if (!xhci_alloc_urb_priv(urb)) {
+		ret = -ENOMEM;
+
+		goto out;
+	}
+
+        ( (struct urb_priv *)urb->hcpriv )->in_progress = 1;
+
+	eurb = xhci_find_urb_ep(xhci, urb);
+	if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
+		ret = -ENXIO;
+
+		goto out;
+	}
+
+	/* Short circuit the virtual root hub */
+	if (urb->dev == xhci->rh.dev) {
+		ret = rh_submit_urb(urb);
+
+		goto out;
+	}
+
+	if ( usb_pipedevice(urb->pipe) == 1 )
+	  printk("dev = %p, dev->path = %s, rh.dev = %p, rh.dev.devnum = %d rh.dev->path = %s!\n",
+		 urb->dev, urb->dev->devpath, xhci->rh.dev, xhci->rh.dev->devnum, xhci->rh.dev->devpath);
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+		ret = xhci_queue_req(urb);
+		break;
+	case PIPE_INTERRUPT:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0)
+				ret = bustime;
+			else {
+				ret = xhci_queue_req(urb);
+				if (ret == -EINPROGRESS)
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+		} else		/* bandwidth is already set */
+			ret = xhci_queue_req(urb);
+		break;
+	case PIPE_BULK:
+		ret = xhci_queue_req(urb);
+		break;
+	case PIPE_ISOCHRONOUS:
+		if (urb->bandwidth == 0) {	/* not yet checked/allocated */
+			if (urb->number_of_packets <= 0) {
+				ret = -EINVAL;
+				break;
+			}
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+				break;
+			}
+
+			ret = xhci_queue_req(urb);
+			if (ret == -EINPROGRESS)
+				usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		} else		/* bandwidth is already set */
+			ret = xhci_queue_req(urb);
+		break;
+	}
+
+out:
+	urb->status = ret;
+
+	if (ret == -EINPROGRESS) {
+		/* We use _tail to make find_urb_ep more efficient */
+		list_add_tail(&urb->urb_list, &xhci->urb_list);
+
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+		return 0;
+	}
+
+	xhci_unlink_generic(urb);
+
+	spin_unlock(&urb->lock);
+	spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+	/* Only call completion if it was successful */
+	if (!ret)
+		xhci_call_completion(urb);
+
+	return ret;
+}
+
+/*
+ * Return the result of a transfer
+ *
+ * MUST be called with urb_list_lock acquired
+ */
+static void xhci_transfer_result(struct xhci *xhci, struct urb *urb)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct urb_priv *urbp;
+
+	/* The root hub is special */
+	if (urb->dev == xhci->rh.dev)
+		return;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+
+        if ( ( (struct urb_priv *)urb->hcpriv )->in_progress )
+                ret = -EINPROGRESS;
+
+        if (urb->actual_length < urb->transfer_buffer_length) {
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        ret = -EREMOTEIO;
+                }
+        }
+
+	if (urb->status == -EPIPE)
+        {
+                ret = urb->status;
+		/* endpoint has stalled - mark it halted */
+		usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe),
+                                  usb_pipeout(urb->pipe));
+        }
+
+	if ((debug == 1 && ret != 0 && ret != -EPIPE) ||
+            (ret != 0 && debug > 1)) {
+		/* Some debugging code */
+		dbg("xhci_result_interrupt/bulk() failed with status %x",
+			status);
+	}
+
+	if (ret == -EINPROGRESS)
+		goto out;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_CONTROL:
+	case PIPE_BULK:
+	case PIPE_ISOCHRONOUS:
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 1);
+		xhci_unlink_generic(urb);
+		break;
+	case PIPE_INTERRUPT:
+		/* Interrupts are an exception */
+		if (urb->interval)
+			goto out_complete;
+
+		/* Release bandwidth for Interrupt or Isoc. transfers */
+		/* Spinlock needed ? */
+		if (urb->bandwidth)
+			usb_release_bandwidth(urb->dev, urb, 0);
+		xhci_unlink_generic(urb);
+		break;
+	default:
+		info("xhci_transfer_result: unknown pipe type %d for urb %p\n",
+			usb_pipetype(urb->pipe), urb);
+	}
+
+	/* Remove it from xhci->urb_list */
+	list_del_init(&urb->urb_list);
+
+out_complete:
+	xhci_add_complete(urb);
+
+out:
+	spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void xhci_unlink_generic(struct urb *urb)
+{
+	struct urb_priv *urbp = urb->hcpriv;
+
+	/* We can get called when urbp allocation fails, so check */
+	if (!urbp)
+		return;
+
+        /* ??? This function is now so minimal it doesn't do much.  Do we really
+         * need it? */
+
+	xhci_delete_urb(urb);
+}
+
+static int xhci_unlink_urb(struct urb *urb)
+{
+	unsigned long flags;
+	struct urb_priv *urbp = urb->hcpriv;
+
+	if (!urb)
+		return -EINVAL;
+
+	if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+		return -ENODEV;
+
+	spin_lock_irqsave(&xhci->urb_list_lock, flags);
+	spin_lock(&urb->lock);
+
+	/* Release bandwidth for Interrupt or Isoc. transfers */
+	/* Spinlock needed ? */
+	if (urb->bandwidth) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth(urb->dev, urb, 0);
+			break;
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth(urb->dev, urb, 1);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (urb->status != -EINPROGRESS) {
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+		return 0;
+	}
+
+	list_del_init(&urb->urb_list);
+
+	xhci_unlink_generic(urb);
+
+	/* Short circuit the virtual root hub */
+	if (urb->dev == xhci->rh.dev) {
+		rh_unlink_urb(urb);
+
+		spin_unlock(&urb->lock);
+		spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+		xhci_call_completion(urb);
+	} else {
+		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+			urbp->status = urb->status = -ECONNABORTED;
+
+			spin_lock(&xhci->urb_remove_list_lock);
+
+			list_add(&urb->urb_list, &xhci->urb_remove_list);
+
+			spin_unlock(&xhci->urb_remove_list_lock);
+
+			spin_unlock(&urb->lock);
+			spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+		} else {
+			urb->status = -ENOENT;
+
+			spin_unlock(&urb->lock);
+			spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+			if (in_interrupt()) {	/* wait at least 1 frame */
+				static int errorcount = 10;
+
+				if (errorcount--)
+					dbg("xhci_unlink_urb called from interrupt for urb %p", urb);
+				udelay(1000);
+			} else
+				schedule_timeout(1+1*HZ/1000); 
+
+			xhci_call_completion(urb);
+		}
+	}
+
+	return 0;
+}
+
+
+struct usb_operations xhci_device_operations = {
+	.allocate = xhci_alloc_dev,
+	.deallocate = xhci_free_dev,
+        /* It doesn't look like any drivers actually care what the frame number
+	 * is at the moment!  If necessary, we could approximate the current
+	 * frame nubmer by passing it from the backend in response messages. */
+	.get_frame_number = NULL,
+	.submit_urb = xhci_submit_urb,
+	.unlink_urb = xhci_unlink_urb
+};
+
+/* Virtual Root Hub */
+
+static __u8 root_hub_dev_des[] =
+{
+ 	0x12,			/*  __u8  bLength; */
+	0x01,			/*  __u8  bDescriptorType; Device */
+	0x00,			/*  __u16 bcdUSB; v1.0 */
+	0x01,
+	0x09,			/*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  bDeviceSubClass; */
+	0x00,			/*  __u8  bDeviceProtocol; */
+	0x08,			/*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,			/*  __u16 idVendor; */
+	0x00,
+	0x00,			/*  __u16 idProduct; */
+	0x00,
+	0x00,			/*  __u16 bcdDevice; */
+	0x00,
+	0x00,			/*  __u8  iManufacturer; */
+	0x02,			/*  __u8  iProduct; */
+	0x01,			/*  __u8  iSerialNumber; */
+	0x01			/*  __u8  bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x02,			/*  __u8  bDescriptorType; Configuration */
+	0x19,			/*  __u16 wTotalLength; */
+	0x00,
+	0x01,			/*  __u8  bNumInterfaces; */
+	0x01,			/*  __u8  bConfigurationValue; */
+	0x00,			/*  __u8  iConfiguration; */
+	0x40,			/*  __u8  bmAttributes;
+					Bit 7: Bus-powered, 6: Self-powered,
+					Bit 5 Remote-wakeup, 4..0: resvd */
+	0x00,			/*  __u8  MaxPower; */
+
+	/* interface */
+	0x09,			/*  __u8  if_bLength; */
+	0x04,			/*  __u8  if_bDescriptorType; Interface */
+	0x00,			/*  __u8  if_bInterfaceNumber; */
+	0x00,			/*  __u8  if_bAlternateSetting; */
+	0x01,			/*  __u8  if_bNumEndpoints; */
+	0x09,			/*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,			/*  __u8  if_bInterfaceSubClass; */
+	0x00,			/*  __u8  if_bInterfaceProtocol; */
+	0x00,			/*  __u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,			/*  __u8  ep_bLength; */
+	0x05,			/*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,			/*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,			/*  __u8  ep_bmAttributes; Interrupt */
+	0x08,			/*  __u16 ep_wMaxPacketSize; 8 Bytes */
+	0x00,
+	0xff			/*  __u8  ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x02,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x01,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0x00,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+static int rh_send_irq(struct urb *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+        xhci_port_t *ports = xhci->rh.ports;
+	unsigned long flags;
+	int i, len = 1;
+	__u16 data = 0;
+
+	spin_lock_irqsave(&urb->lock, flags);
+	for (i = 0; i < xhci->rh.numports; i++) {
+                /* MAW: No idea what the old code was doing here or why it worked.
+		 * This implementation sets a bit if anything at all has changed on the 
+		 * port, as per USB spec 11.12 */
+		data |= (ports[i].cs_chg || ports[i].pe_chg )
+                        ? (1 << (i + 1))
+                        : 0;
+
+		len = (i + 1) / 8 + 1;
+	}
+
+	*(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
+	urb->actual_length = len;
+	urbp->status = 0;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if ((data > 0) && (xhci->rh.send != 0)) {
+		dbg("root-hub INT complete: data: %x", data);
+		xhci_call_completion(urb);
+	}
+
+	return 0;
+}
+
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+static int rh_init_int_timer(struct urb *urb);
+
+static void rh_int_timer_do(unsigned long ptr)
+{
+	struct urb *urb = (struct urb *)ptr;
+	struct list_head list, *tmp, *head;
+	unsigned long flags;
+	int i;
+
+	for ( i = 0; i < xhci->rh.numports; i++)
+                xhci_queue_probe(i);
+
+	if (xhci->rh.send)
+		rh_send_irq(urb);
+
+	INIT_LIST_HEAD(&list);
+
+	spin_lock_irqsave(&xhci->urb_list_lock, flags);
+	head = &xhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+		struct urb_priv *up = (struct urb_priv *)u->hcpriv;
+
+		tmp = tmp->next;
+
+		spin_lock(&u->lock);
+
+		/* Check if the URB timed out */
+		if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
+			list_del(&u->urb_list);
+			list_add_tail(&u->urb_list, &list);
+		}
+
+		spin_unlock(&u->lock);
+	}
+	spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+	head = &list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+		xhci_unlink_urb(u);
+	}
+
+	rh_init_int_timer(urb);
+}
+
+/* Root Hub INTs are polled by this timer */
+static int rh_init_int_timer(struct urb *urb)
+{
+	xhci->rh.interval = urb->interval;
+	init_timer(&xhci->rh.rh_int_timer);
+	xhci->rh.rh_int_timer.function = rh_int_timer_do;
+	xhci->rh.rh_int_timer.data = (unsigned long)urb;
+	xhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+	add_timer(&xhci->rh.rh_int_timer);
+
+	return 0;
+}
+
+#define OK(x)			len = (x); break
+
+/* Root Hub Control Pipe */
+static int rh_submit_urb(struct urb *urb)
+{
+	unsigned int pipe = urb->pipe;
+	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int leni = urb->transfer_buffer_length;
+	int len = 0;
+	xhci_port_t *status;
+	int stat = 0;
+	int i;
+	int retstatus;
+        unsigned long flags;
+        
+	__u16 cstatus;
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
+		xhci->rh.urb = urb;
+		xhci->rh.send = 1;
+		xhci->rh.interval = urb->interval;
+		rh_init_int_timer(urb);
+
+		return -EINPROGRESS;
+	}
+
+	bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8;
+	wValue = le16_to_cpu(cmd->wValue);
+	wIndex = le16_to_cpu(cmd->wIndex);
+	wLength = le16_to_cpu(cmd->wLength);
+
+	for (i = 0; i < 8; i++)
+		xhci->rh.c_p_r[i] = 0;
+
+        status = &xhci->rh.ports[wIndex - 1];
+
+        spin_lock_irqsave(&xhci->rh.port_state_lock, flags);
+
+	switch (bmRType_bReq) {
+		/* Request Destination:
+		   without flags: Device,
+		   RH_INTERFACE: interface,
+		   RH_ENDPOINT: endpoint,
+		   RH_CLASS means HUB here,
+		   RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
+		*/
+
+	case RH_GET_STATUS:
+		*(__u16 *)data = cpu_to_le16(1);
+		OK(2);
+	case RH_GET_STATUS | RH_INTERFACE:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_ENDPOINT:
+		*(__u16 *)data = cpu_to_le16(0);
+		OK(2);
+	case RH_GET_STATUS | RH_CLASS:
+		*(__u32 *)data = cpu_to_le32(0);
+		OK(4);		/* hub power */
+	case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+		cstatus = (status->cs_chg) |
+			(status->pe_chg << 1) |
+			(xhci->rh.c_p_r[wIndex - 1] << 4);
+		retstatus = (status->ccs) |
+			(status->pe << 1) |
+			(status->susp << 2) |
+			(status->pr << 8) |
+			(1 << 8) |      /* power on */
+			(status->lsda << 9);
+		*(__u16 *)data = cpu_to_le16(retstatus);
+		*(__u16 *)(data + 2) = cpu_to_le16(cstatus);
+		OK(4);
+	case RH_CLEAR_FEATURE | RH_ENDPOINT:
+		switch (wValue) {
+		case RH_ENDPOINT_STALL:
+			OK(0);
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_CLASS:
+		switch (wValue) {
+		case RH_C_HUB_OVER_CURRENT:
+			OK(0);	/* hub power over current */
+		}
+		break;
+	case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_ENABLE:
+                        status->pe     = 0;
+			OK(0);
+		case RH_PORT_SUSPEND:
+                        status->susp   = 0;
+			OK(0);
+		case RH_PORT_POWER:
+			OK(0);	/* port power */
+		case RH_C_PORT_CONNECTION:
+                        status->cs_chg = 0;
+			OK(0);
+		case RH_C_PORT_ENABLE:
+                        status->pe_chg = 0;
+			OK(0);
+		case RH_C_PORT_SUSPEND:
+			/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+			OK(0);
+		case RH_C_PORT_OVER_CURRENT:
+			OK(0);	/* port power over current */
+		case RH_C_PORT_RESET:
+			xhci->rh.c_p_r[wIndex - 1] = 0;
+			OK(0);
+		}
+		break;
+	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+		switch (wValue) {
+		case RH_PORT_SUSPEND:
+                        status->susp = 1;	
+			OK(0);
+		case RH_PORT_RESET:
+                {
+                        int ret;
+                        xhci->rh.c_p_r[wIndex - 1] = 1;
+                        status->pr = 0;
+                        status->pe = 1;
+                        ret = xhci_port_reset(wIndex - 1);
+                        /* XXX MAW: should probably cancel queued transfers during reset... *\/ */
+                        if ( ret == 0 ) { OK(0); }
+                        else { return ret; }
+                }
+                break;
+		case RH_PORT_POWER:
+			OK(0); /* port power ** */
+		case RH_PORT_ENABLE:
+                        status->pe = 1;
+			OK(0);
+		}
+		break;
+	case RH_SET_ADDRESS:
+	  printk("setting root hub device to %d\n", wValue);
+		xhci->rh.devnum = wValue;
+		OK(0);
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case 0x01:	/* device descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof(root_hub_dev_des), wLength));
+			memcpy(data, root_hub_dev_des, len);
+			OK(len);
+		case 0x02:	/* configuration descriptor */
+			len = min_t(unsigned int, leni,
+				  min_t(unsigned int,
+				      sizeof(root_hub_config_des), wLength));
+			memcpy (data, root_hub_config_des, len);
+			OK(len);
+		case 0x03:	/* string descriptors */
+			len = usb_root_hub_string (wValue & 0xff,
+				0, "XHCI-alt",
+				data, wLength);
+			if (len > 0) {
+				OK(min_t(int, leni, len));
+			} else 
+				stat = -EPIPE;
+		}
+		break;
+	case RH_GET_DESCRIPTOR | RH_CLASS:
+		root_hub_hub_des[2] = xhci->rh.numports;
+		len = min_t(unsigned int, leni,
+			  min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
+		memcpy(data, root_hub_hub_des, len);
+		OK(len);
+	case RH_GET_CONFIGURATION:
+		*(__u8 *)data = 0x01;
+		OK(1);
+	case RH_SET_CONFIGURATION:
+		OK(0);
+	case RH_GET_INTERFACE | RH_INTERFACE:
+		*(__u8 *)data = 0x00;
+		OK(1);
+	case RH_SET_INTERFACE | RH_INTERFACE:
+		OK(0);
+	default:
+		stat = -EPIPE;
+	}
+
+        spin_unlock_irqrestore(&xhci->rh.port_state_lock, flags);
+
+	urb->actual_length = len;
+
+	return stat;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static int rh_unlink_urb(struct urb *urb)
+{
+	if (xhci->rh.urb == urb) {
+		urb->status = -ENOENT;
+		xhci->rh.send = 0;
+		xhci->rh.urb = NULL;
+		del_timer(&xhci->rh.rh_int_timer);
+	}
+	return 0;
+}
+
+static void xhci_call_completion(struct urb *urb)
+{
+	struct urb_priv *urbp;
+	struct usb_device *dev = urb->dev;
+	int is_ring = 0, killed, resubmit_interrupt, status;
+	struct urb *nurb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&urb->lock, flags);
+
+	urbp = (struct urb_priv *)urb->hcpriv;
+	if (!urbp || !urb->dev) {
+		spin_unlock_irqrestore(&urb->lock, flags);
+		return;
+	}
+
+	killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
+			urb->status == -ECONNRESET);
+	resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+			urb->interval);
+
+	nurb = urb->next;
+	if (nurb && !killed) {
+		int count = 0;
+
+		while (nurb && nurb != urb && count < MAX_URB_LOOP) {
+			if (nurb->status == -ENOENT ||
+			    nurb->status == -ECONNABORTED ||
+			    nurb->status == -ECONNRESET) {
+				killed = 1;
+				break;
+			}
+
+			nurb = nurb->next;
+			count++;
+		}
+
+		if (count == MAX_URB_LOOP)
+			err("xhci_call_completion: too many linked URB's, loop? (first loop)");
+
+		/* Check to see if chain is a ring */
+		is_ring = (nurb == urb);
+	}
+
+	status = urbp->status;
+	if (!resubmit_interrupt || killed)
+		/* We don't need urb_priv anymore */
+		xhci_destroy_urb_priv(urb);
+
+	if (!killed)
+		urb->status = status;
+
+	spin_unlock_irqrestore(&urb->lock, flags);
+
+	if (urb->complete)
+		urb->complete(urb);
+
+	if (resubmit_interrupt)
+		/* Recheck the status. The completion handler may have */
+		/*  unlinked the resubmitting interrupt URB */
+		killed = (urb->status == -ENOENT ||
+			  urb->status == -ECONNABORTED ||
+			  urb->status == -ECONNRESET);
+
+	if (resubmit_interrupt && !killed) {
+                if ( urb->dev != xhci->rh.dev )
+                        xhci_queue_req(urb); /* XXX What if this fails? */
+                /* Don't need to resubmit URBs for the virtual root dev. */
+	} else {
+		if (is_ring && !killed) {
+			urb->dev = dev;
+			xhci_submit_urb(urb);
+		} else {
+			/* We decrement the usage count after we're done */
+			/*  with everything */
+			usb_dec_dev_use(dev);
+		}
+	}
+}
+
+static void xhci_finish_completion(void)
+{
+	struct list_head *tmp, *head;
+	unsigned long flags;
+
+	spin_lock_irqsave(&xhci->complete_list_lock, flags);
+	head = &xhci->complete_list;
+	tmp = head->next;
+	while (tmp != head) {
+		struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
+		struct urb *urb = urbp->urb;
+
+		list_del_init(&urbp->complete_list);
+		spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+
+		xhci_call_completion(urb);
+
+		spin_lock_irqsave(&xhci->complete_list_lock, flags);
+		head = &xhci->complete_list;
+		tmp = head->next;
+	}
+	spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+}
+
+void receive_usb_reset(usbif_response_t *resp)
+{
+    awaiting_reset = resp->status;
+    rmb();
+    
+}
+
+void receive_usb_probe(usbif_response_t *resp)
+{
+    spin_lock(&xhci->rh.port_state_lock);
+
+    if ( resp->status > 0 )
+    {
+        if ( resp->status == 1 )
+        {
+/* 	  printk("hey hey, there's a device on port %d\n", resp->data); */
+
+            /* If theres a device there and there wasn't one before there must
+             * have been a connection status change. */
+            if( xhci->rh.ports[resp->data].cs == 0 )
+	    {
+                xhci->rh.ports[resp->data].cs = 1;
+                xhci->rh.ports[resp->data].ccs = 1;
+                xhci->rh.ports[resp->data].cs_chg = 1;
+/* 		printk("Look at device on port %d that wasn't there before\n", resp->data); */
+	    }
+        }
+        else
+            printk("receive_usb_probe(): unexpected status %d for port %d\n",
+                   resp->status, resp->data);
+    }
+    else if ( resp->status < 0)
+        printk("receive_usb_probe(): got error status %d\n", resp->status);
+
+    spin_unlock(&xhci->rh.port_state_lock);
+}
+
+void receive_usb_io(usbif_response_t *resp)
+{
+        struct urb_priv *urbp = (struct urb_priv *)resp->id;
+        struct urb *urb = urbp->urb;
+
+        urb->actual_length = resp->length;
+	urb->status = resp->status;
+	urbp->status = resp->status;
+        urbp->in_progress = 0;
+
+        if( usb_pipetype(urb->pipe) == 0 ) /* ISO */
+        {
+                int i;
+              
+                /* Copy ISO schedule results back in. */
+
+                for ( i = 0; i < urb->number_of_packets; i++ )
+                {
+                        urb->iso_frame_desc[i].status
+			  = urbp->schedule[i].status;
+                        urb->iso_frame_desc[i].actual_length
+                                = urbp->schedule[i].length;
+                }
+                free_page((unsigned long)urbp->schedule);
+        }
+}
+
+static void xhci_drain_ring(void)
+{
+	struct list_head *tmp, *head;
+	usbif_t *usb_ring = xhci->usbif;
+	usbif_response_t *resp;
+        USBIF_RING_IDX i, rp;
+
+        /* Walk the ring here to get responses, updating URBs to show what
+         * completed. */
+        
+        rp = usb_ring->resp_prod;
+        rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+        /* Take items off the comms ring, taking care not to overflow. */
+        for ( i = xhci->usb_resp_cons; 
+              (i != rp) && ((i-usb_ring->req_prod) != USBIF_RING_SIZE);
+              i++ )
+        {
+            resp = &usb_ring->ring[MASK_USBIF_IDX(i)].resp;
+            
+            /* May need to deal with batching and with putting a ceiling on
+               the number dispatched for performance and anti-dos reasons */
+
+#if 0
+            printk("usbfront: Processing response:\n");
+            printk("          id = 0x%x\n", resp->id);
+            printk("          op = %d\n", resp->operation);
+            printk("          status = %d\n", resp->status);
+            printk("          length = %d\n", resp->length);
+#endif            
+
+            switch ( resp->operation )
+            {
+            case USBIF_OP_PROBE:
+                receive_usb_probe(resp);
+                break;
+                
+            case USBIF_OP_IO:
+                receive_usb_io(resp);
+                break;
+
+            case USBIF_OP_RESET:
+                receive_usb_reset(resp);
+                break;
+
+            default:
+                printk("error: unknown USB io operation response [%d]\n",
+                       usb_ring->ring[i].req.operation);
+                break;
+            }
+        }
+
+        xhci->usb_resp_cons = i;
+
+	/* Walk the list of pending URB's to see which ones completed and do
+         * callbacks, etc. */
+	spin_lock(&xhci->urb_list_lock);
+	head = &xhci->urb_list;
+	tmp = head->next;
+	while (tmp != head) {
+                
+		struct urb *urb = list_entry(tmp, struct urb, urb_list);
+
+		tmp = tmp->next;
+
+		/* Checks the status and does all of the magic necessary */
+		xhci_transfer_result(xhci, urb);
+	}
+	spin_unlock(&xhci->urb_list_lock);
+
+	xhci_finish_completion();
+}
+
+
+static void xhci_interrupt(int irq, void *__xhci, struct pt_regs *regs)
+{
+        xhci_drain_ring();
+}
+
+static void free_xhci(struct xhci *xhci)
+{
+	kfree(xhci);
+}
+
+/* /\* */
+/*  * De-allocate all resources.. */
+/*  *\/ */
+/* static void release_xhci(struct xhci *xhci) */
+/* { */
+/* 	if (xhci->irq >= 0) { */
+/* 		free_irq(xhci->irq, xhci); */
+/* 		xhci->irq = -1; */
+/* 	} */
+
+/*         /\* Get the ring back from the backend domain.  Then free it.  Hmmmm. */
+/*          * Lets ignore this for now - not particularly useful. *\/ */
+
+/* 	free_xhci(xhci); */
+/* } */
+
+/**
+ * Initialise a new virtual root hub for a new USB device channel.
+ */
+static int alloc_xhci(void)
+{
+	int retval;
+	struct usb_bus *bus;
+
+	retval = -EBUSY;
+
+	xhci = kmalloc(sizeof(*xhci), GFP_KERNEL);
+	if (!xhci) {
+		err("couldn't allocate xhci structure");
+		retval = -ENOMEM;
+		goto err_alloc_xhci;
+	}
+
+	/* Reset here so we don't get any interrupts from an old setup */
+	/*  or broken setup */
+	//	reset_hc(xhci);
+
+
+	xhci->state = USBIF_STATE_CLOSED;
+	xhci->is_suspended = 0;
+
+	spin_lock_init(&xhci->urb_remove_list_lock);
+	INIT_LIST_HEAD(&xhci->urb_remove_list);
+
+	spin_lock_init(&xhci->urb_list_lock);
+	INIT_LIST_HEAD(&xhci->urb_list);
+
+	spin_lock_init(&xhci->complete_list_lock);
+	INIT_LIST_HEAD(&xhci->complete_list);
+
+	spin_lock_init(&xhci->frame_list_lock);
+
+	/* We need exactly one page (per XHCI specs), how convenient */
+	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
+#if PAGE_SIZE < (4 * 1024)
+#error PAGE_SIZE is not atleast 4k
+#endif
+	bus = usb_alloc_bus(&xhci_device_operations);
+	if (!bus) {
+		err("unable to allocate bus");
+		goto err_alloc_bus;
+	}
+
+	xhci->bus = bus;
+	bus->bus_name = "XHCI";
+	bus->hcpriv = xhci;
+
+	usb_register_bus(xhci->bus);
+
+	/* Initialize the root hub */
+
+	xhci->rh.numports = 0;
+
+	xhci->bus->root_hub = xhci->rh.dev = usb_alloc_dev(NULL, xhci->bus);
+	if (!xhci->rh.dev) {
+		err("unable to allocate root hub");
+		goto err_alloc_root_hub;
+	}
+
+	xhci->state = 0;
+
+	return 0;
+
+/*
+ * error exits:
+ */
+err_start_root_hub:
+	free_irq(xhci->irq, xhci);
+	xhci->irq = -1;
+
+err_alloc_root_hub:
+	usb_free_bus(xhci->bus);
+	xhci->bus = NULL;
+
+err_alloc_bus:
+	free_xhci(xhci);
+
+err_alloc_xhci:
+	return retval;
+}
+
+static void usbif_status_change(usbif_fe_interface_status_changed_t *status)
+{
+    ctrl_msg_t                   cmsg;
+    usbif_fe_interface_connect_t up;
+    long rc;
+    usbif_t *usbif;
+
+    switch ( status->status )
+    {
+    case USBIF_INTERFACE_STATUS_DESTROYED:
+        printk(KERN_WARNING "Unexpected usbif-DESTROYED message in state %d\n",
+               xhci->state);
+        break;
+
+    case USBIF_INTERFACE_STATUS_DISCONNECTED:
+        if ( xhci->state != USBIF_STATE_CLOSED )
+        {
+            printk(KERN_WARNING "Unexpected usbif-DISCONNECTED message"
+                   " in state %d\n", xhci->state);
+            break;
+            /* Not bothering to do recovery here for now.  Keep things
+             * simple. */
+        }
+
+        /* Move from CLOSED to DISCONNECTED state. */
+        xhci->usbif = usbif = (usbif_t *)__get_free_page(GFP_KERNEL);
+        usbif->req_prod = usbif->resp_prod = 0;
+        xhci->state  = USBIF_STATE_DISCONNECTED;
+
+        /* Construct an interface-CONNECT message for the domain controller. */
+        cmsg.type      = CMSG_USBIF_FE;
+        cmsg.subtype   = CMSG_USBIF_FE_INTERFACE_CONNECT;
+        cmsg.length    = sizeof(usbif_fe_interface_connect_t);
+        up.shmem_frame = virt_to_machine(usbif) >> PAGE_SHIFT;
+        memcpy(cmsg.msg, &up, sizeof(up));
+        
+        /* Tell the controller to bring up the interface. */
+        ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+        break;
+
+    case USBIF_INTERFACE_STATUS_CONNECTED:
+        if ( xhci->state == USBIF_STATE_CLOSED )
+        {
+            printk(KERN_WARNING "Unexpected usbif-CONNECTED message"
+                   " in state %d\n", xhci->state);
+            break;
+        }
+
+        xhci->evtchn = status->evtchn;
+        xhci->irq = bind_evtchn_to_irq(xhci->evtchn);
+	xhci->bandwidth = status->bandwidth;
+	xhci->rh.numports = status->num_ports;
+
+        xhci->rh.ports = kmalloc (sizeof(xhci_port_t) * xhci->rh.numports, GFP_KERNEL);
+        memset(xhci->rh.ports, 0, sizeof(xhci_port_t) * xhci->rh.numports);
+
+        printk("rh.dev @ %p\n", xhci->rh.dev);
+
+	usb_connect(xhci->rh.dev);
+
+	if (usb_new_device(xhci->rh.dev) != 0) {
+		err("unable to start root hub");
+	}
+
+	/* Allocate the appropriate USB bandwidth here...  Need to
+	* somehow know what the total available is thought to be so we
+	* can calculate the reservation correctly. */
+ 	usb_claim_bandwidth(xhci->rh.dev, xhci->rh.urb,
+ 			    1000 - xhci->bandwidth, 0);
+
+        if ( (rc = request_irq(xhci->irq, xhci_interrupt, 
+                               SA_SAMPLE_RANDOM, "usbif", xhci)) )
+                printk(KERN_ALERT"usbfront request_irq failed (%ld)\n",rc);
+
+	printk(KERN_INFO __FILE__ ": USB XHCI: SHM at %p (0x%lx), EVTCHN %d IRQ %d\n",
+               xhci->usbif, virt_to_machine(xhci->usbif), xhci->evtchn, xhci->irq);
+
+        xhci->state = USBIF_STATE_CONNECTED;
+        
+        break;
+
+    default:
+        printk(KERN_WARNING "Status change to unknown value %d\n", 
+               status->status);
+        break;
+    }
+}
+
+
+static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
+{
+    switch ( msg->subtype )
+    {
+    case CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED:
+        if ( msg->length != sizeof(usbif_fe_interface_status_changed_t) )
+            goto parse_error;
+        usbif_status_change((usbif_fe_interface_status_changed_t *)
+                            &msg->msg[0]);
+        break;        
+
+        /* New interface...? */
+    default:
+        goto parse_error;
+    }
+
+    ctrl_if_send_response(msg);
+    return;
+
+ parse_error:
+    msg->length = 0;
+    ctrl_if_send_response(msg);
+}
+
+
+static int __init xhci_hcd_init(void)
+{
+	int retval = -ENOMEM, i;
+        usbif_fe_interface_status_changed_t st;
+        control_msg_t cmsg;
+
+	if ( (xen_start_info.flags & SIF_INITDOMAIN)
+	     || (xen_start_info.flags & SIF_USB_BE_DOMAIN) )
+                return 0;
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	if (debug) {
+		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+		if (!errbuf)
+			goto errbuf_failed;
+	}
+
+	xhci_up_cachep = kmem_cache_create("xhci_urb_priv",
+		sizeof(struct urb_priv), 0, 0, NULL, NULL);
+	if (!xhci_up_cachep)
+		goto up_failed;
+
+        /* Lazily avoid unloading issues for now. ;-)*/
+	MOD_INC_USE_COUNT;
+
+        /* Let the domain controller know we're here.  For now we wait until
+         * connection, as for the block and net drivers.  This is only strictly
+         * necessary if we're going to boot off a USB device. */
+        printk(KERN_INFO "Initialising Xen virtual USB hub\n");
+    
+        (void)ctrl_if_register_receiver(CMSG_USBIF_FE, usbif_ctrlif_rx,
+                                        CALLBACK_IN_BLOCKING_CONTEXT);
+        
+	alloc_xhci();
+
+        /* Send a driver-UP notification to the domain controller. */
+        cmsg.type      = CMSG_USBIF_FE;
+        cmsg.subtype   = CMSG_USBIF_FE_DRIVER_STATUS_CHANGED;
+        cmsg.length    = sizeof(usbif_fe_driver_status_changed_t);
+        st.status      = USBIF_DRIVER_STATUS_UP;
+        memcpy(cmsg.msg, &st, sizeof(st));
+        ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+        
+        /*
+         * We should read 'nr_interfaces' from response message and wait
+         * for notifications before proceeding. For now we assume that we
+         * will be notified of exactly one interface.
+         */
+        for ( i=0; (xhci->state != USBIF_STATE_CONNECTED) && (i < 10*HZ); i++ )
+        {
+            set_current_state(TASK_INTERRUPTIBLE);
+            schedule_timeout(1);
+        }
+        
+        if (xhci->state != USBIF_STATE_CONNECTED)
+            printk(KERN_INFO "Timeout connecting USB frontend driver!\n");
+	
+	return 0;
+
+up_failed:
+
+	if (errbuf)
+		kfree(errbuf);
+
+errbuf_failed:
+
+	return retval;
+}
+
+static void __exit xhci_hcd_cleanup(void) 
+{
+	if (kmem_cache_destroy(xhci_up_cachep))
+		printk(KERN_INFO "xhci: not all urb_priv's were freed\n");
+
+//        release_xhci(); do some calls here
+
+
+	if (errbuf)
+		kfree(errbuf);
+}
+
+module_init(xhci_hcd_init);
+module_exit(xhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/xhci.h xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/xhci.h
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/xhci.h	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/frontend/xhci.h	2005-01-11 14:37:38 +00:00
@@ -0,0 +1,210 @@
+#ifndef __LINUX_XHCI_H
+#define __LINUX_XHCI_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include "../usbif.h"
+#include <linux/spinlock.h>
+
+#define XHCI_NUMFRAMES		1024	/* in the frame list [array] */
+#define XHCI_MAX_SOF_NUMBER	2047	/* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES	1000	/* how far future frames can be scheduled */
+
+/* In the absence of actual hardware state, we maintain the current known state
+ * of the virtual hub ports in this data structure.
+ */
+typedef struct
+{
+        unsigned int cs     :1;     /* Connection status.  do we really need this /and/ ccs? */
+        unsigned int cs_chg :1; /* Connection status change.  */
+        unsigned int pe     :1;     /* Port enable.               */
+        unsigned int pe_chg :1; /* Port enable change.        */
+        unsigned int ccs    :1;    /* Current connect status.    */
+        unsigned int susp   :1;   /* Suspended.                 */
+        unsigned int lsda   :1;   /* Low speed device attached. */
+        unsigned int pr     :1;     /* Port reset.                */
+        
+    /* Device info? */
+} xhci_port_t;
+
+struct xhci_frame_list {
+	__u32 frame[XHCI_NUMFRAMES];
+
+	void *frame_cpu[XHCI_NUMFRAMES];
+};
+
+struct urb_priv;
+
+#define xhci_status_bits(ctrl_sts)	(ctrl_sts & 0xFE0000)
+#define xhci_actual_length(ctrl_sts)	((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+#define xhci_maxlen(token)	((token) >> 21)
+#define xhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
+#define xhci_toggle(token)	(((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define xhci_endpoint(token)	(((token) >> 15) & 0xf)
+#define xhci_devaddr(token)	(((token) >> 8) & 0x7f)
+#define xhci_devep(token)	(((token) >> 8) & 0x7ff)
+#define xhci_packetid(token)	((token) & TD_TOKEN_PID_MASK)
+#define xhci_packetout(token)	(xhci_packetid(token) != USB_PID_IN)
+#define xhci_packetin(token)	(xhci_packetid(token) == USB_PID_IN)
+
+struct virt_root_hub {
+	struct usb_device *dev;
+	int devnum;		/* Address of Root Hub endpoint */
+	struct urb *urb;
+	void *int_addr;
+	int send;
+	int interval;
+	int numports;
+	int c_p_r[8];
+	struct timer_list rh_int_timer;
+        spinlock_t port_state_lock;
+        xhci_port_t *ports;       /*  */
+};
+
+/*
+ * This describes the full xhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct xhci {
+
+#ifdef CONFIG_PROC_FS
+	/* procfs */
+	int num;
+	struct proc_dir_entry *proc_entry;
+#endif
+
+        int evtchn;                        /* Interdom channel to backend */
+        int irq;                           /* Bound to evtchn */
+        int state;                         /* State of this USB interface */
+        unsigned long bandwidth;
+        int handle;
+
+	struct usb_bus *bus;
+
+	spinlock_t frame_list_lock;
+	struct xhci_frame_list *fl;		/* P: xhci->frame_list_lock */
+	int is_suspended;
+
+	/* Main list of URB's currently controlled by this HC */
+	spinlock_t urb_list_lock;
+	struct list_head urb_list;		/* P: xhci->urb_list_lock */
+
+	/* List of asynchronously unlinked URB's */
+	spinlock_t urb_remove_list_lock;
+	struct list_head urb_remove_list;	/* P: xhci->urb_remove_list_lock */
+
+	/* List of URB's awaiting completion callback */
+	spinlock_t complete_list_lock;
+	struct list_head complete_list;		/* P: xhci->complete_list_lock */
+
+	struct virt_root_hub rh;	/* private data of the virtual root hub */
+
+        spinlock_t response_lock;
+
+        usbif_t *usbif;
+        int usb_resp_cons;
+};
+
+struct urb_priv {
+	struct urb *urb;
+        usbif_iso_t *schedule;
+	struct usb_device *dev;
+
+        int in_progress : 1;	        /* QH was queued (not linked in) */
+	int short_control_packet : 1;	/* If we get a short packet during */
+					/*  a control transfer, retrigger */
+					/*  the status phase */
+
+	int status;			/* Final status */
+
+	unsigned long inserttime;	/* In jiffies */
+
+	struct list_head queue_list;	/* P: xhci->frame_list_lock */
+	struct list_head complete_list;	/* P: xhci->complete_list_lock */
+};
+
+/*
+ * Locking in xhci.c
+ *
+ * spinlocks are used extensively to protect the many lists and data
+ * structures we have. It's not that pretty, but it's necessary. We
+ * need to be done with all of the locks (except complete_list_lock) when
+ * we call urb->complete. I've tried to make it simple enough so I don't
+ * have to spend hours racking my brain trying to figure out if the
+ * locking is safe.
+ *
+ * Here's the safe locking order to prevent deadlocks:
+ *
+ * #1 xhci->urb_list_lock
+ * #2 urb->lock
+ * #3 xhci->urb_remove_list_lock, xhci->frame_list_lock, 
+ *   xhci->qh_remove_list_lock
+ * #4 xhci->complete_list_lock
+ *
+ * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
+ * at the lowest level FIRST and NEVER grab locks at the same level at the
+ * same time.
+ * 
+ * So, if you need xhci->urb_list_lock, grab it before you grab urb->lock
+ */
+
+/* -------------------------------------------------------------------------
+   Virtual Root HUB
+   ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_DEVICE		0x00
+#define RH_INTERFACE		0x01
+#define RH_ENDPOINT		0x02
+#define RH_OTHER		0x03
+
+#define RH_CLASS		0x20
+#define RH_VENDOR		0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS		0x0080
+#define RH_CLEAR_FEATURE	0x0100
+#define RH_SET_FEATURE		0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR	0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE		0x0280
+#define RH_GET_INTERFACE	0x0A80
+#define RH_SET_INTERFACE	0x0B00
+#define RH_SYNC_FRAME		0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP		0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION	0x00
+#define RH_PORT_ENABLE		0x01
+#define RH_PORT_SUSPEND		0x02
+#define RH_PORT_OVER_CURRENT	0x03
+#define RH_PORT_RESET		0x04
+#define RH_PORT_POWER		0x08
+#define RH_PORT_LOW_SPEED	0x09
+#define RH_C_PORT_CONNECTION	0x10
+#define RH_C_PORT_ENABLE	0x11
+#define RH_C_PORT_SUSPEND	0x12
+#define RH_C_PORT_OVER_CURRENT	0x13
+#define RH_C_PORT_RESET		0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER	0x00
+#define RH_C_HUB_OVER_CURRENT	0x01
+#define RH_DEVICE_REMOTE_WAKEUP	0x00
+#define RH_ENDPOINT_STALL	0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP		0x00
+
+#define RH_ACK			0x01
+#define RH_REQ_ERR		-1
+#define RH_NACK			0x00
+
+#endif
+
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/usbif.h xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/usbif.h
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/usbif.h	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/arch/xen/drivers/usbif/usbif.h	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,111 @@
+/******************************************************************************
+ * usbif.h
+ * 
+ * Unified block-device I/O interface for Xen guest OSes.
+ * 
+ * Copyright (c) 2003-2004, Keir Fraser
+ */
+
+#ifndef __SHARED_USBIF_H__
+#define __SHARED_USBIF_H__
+
+#define usbif_vdev_t   u16
+#define usbif_sector_t u64
+
+#define USBIF_OP_IO      0
+#define USBIF_OP_PROBE   1 /* Is there a device on this port? */
+#define USBIF_OP_RESET   2 /* Reset a virtual USB port.       */
+
+/* NB. Ring size must be small enough for sizeof(usbif_ring_t) <= PAGE_SIZE. */
+#define USBIF_RING_SIZE        64
+
+/* XXX this does not want to be here!  it really ought to be dynamic but it can
+ * live here for now */
+#define NUM_PORTS 1
+
+typedef struct {
+    unsigned long  id;           /*  0: private guest value, echoed in resp  */
+    u8             operation;    /*  4: USBIF_OP_???                         */
+    u8  __pad1;
+    usbif_vdev_t   port;         /* 6 : guest virtual USB port               */
+    unsigned long  devnum :7;    /* 8 : Device address, as seen by the guest.*/
+    unsigned long  endpoint :4;  /* Device endpoint.                         */
+    unsigned long  direction :1; /* Pipe direction.                          */
+    unsigned long  speed :1;     /* Pipe speed.                              */
+    unsigned long  pipe_type :2; /* Pipe type (iso, bulk, int, ctrl)         */
+    unsigned long  __pad2 :18;
+    unsigned long  transfer_buffer; /* 12: Machine address */
+    unsigned long  length;          /* 16: Buffer length */
+    unsigned long  transfer_flags;  /* 20: For now just pass Linux transfer
+                                     * flags - this may change. */
+    unsigned char setup[8];         /* 22 Embed setup packets directly. */
+    unsigned long  iso_schedule;    /* 30 Machine address of transfer sched (iso
+                                     * only) */
+    unsigned long num_iso;        /* 34 : length of iso schedule */
+    unsigned long timeout;        /* 38: timeout in ms */
+} PACKED usbif_request_t; /* 42 */
+/* Data we need to pass:
+ * - Transparently handle short packets or complain at us?
+ */
+
+typedef struct {
+    unsigned long   id;              /* 0: copied from request         */
+    u8              operation;       /* 4: copied from request         */
+    u8              data;            /* 5: Small chunk of in-band data */
+    s16             status;          /* 6: USBIF_RSP_???               */
+    unsigned long   transfer_mutex;  /* Used for cancelling requests atomically. */
+    unsigned long    length;          /* 8: How much data we really got */
+} PACKED usbif_response_t;
+
+#define USBIF_RSP_ERROR  -1 /* non-specific 'error' */
+#define USBIF_RSP_OKAY    0 /* non-specific 'okay'  */
+
+/*
+ * We use a special capitalised type name because it is _essential_ that all 
+ * arithmetic on indexes is done on an integer type of the correct size.
+ */
+typedef u32 USBIF_RING_IDX;
+
+/*
+ * Ring indexes are 'free running'. That is, they are not stored modulo the
+ * size of the ring buffer. The following macro converts a free-running counter
+ * into a value that can directly index a ring-buffer array.
+ */
+#define MASK_USBIF_IDX(_i) ((_i)&(USBIF_RING_SIZE-1))
+
+typedef struct {
+    USBIF_RING_IDX req_prod;  /*  0: Request producer. Updated by front-end. */
+    USBIF_RING_IDX resp_prod; /*  4: Response producer. Updated by back-end. */
+
+    union {                   /*  8 */
+        usbif_request_t  req;
+        usbif_response_t resp;
+    } PACKED ring[USBIF_RING_SIZE];
+} PACKED usbif_t;
+
+
+
+/*
+ * USBIF_OP_PROBE:
+ * The request format for a probe request is constrained as follows:
+ *  @operation   == USBIF_OP_PROBE
+ *  @nr_segments == size of probe buffer in pages
+ *  @device      == unused (zero)
+ *  @id          == any value (echoed in response message)
+ *  @sector_num  == unused (zero)
+ *  @frame_and_sects == list of page-sized buffers.
+ *                       (i.e., @first_sect == 0, @last_sect == 7).
+ * 
+ * The response is a list of vdisk_t elements copied into the out-of-band
+ * probe buffer. On success the response status field contains the number
+ * of vdisk_t elements.
+ */
+
+typedef struct {
+    unsigned long length; /* IN = expected, OUT = actual */
+    unsigned long buffer_offset;  /* IN offset in buffer specified in main
+                                     packet */
+    unsigned long status; /* OUT Status for this packet. */
+} usbif_iso_t;
+
+#endif /* __SHARED_USBIF_H__ */
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/linux-2.4.28-xen-sparse/drivers/usb/hcd.c xeno-usb.bk/linux-2.4.28-xen-sparse/drivers/usb/hcd.c
--- xen-2.0-testing.bk/linux-2.4.28-xen-sparse/drivers/usb/hcd.c	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/linux-2.4.28-xen-sparse/drivers/usb/hcd.c	2005-01-10 03:13:15 +00:00
@@ -0,0 +1,1511 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ * 
+ * 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, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/uts.h>			/* for UTS_SYSNAME */
+
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+
+#include <linux/usb.h>
+#include "hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver framework
+ *
+ * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
+ * HCD-specific behaviors/bugs.  Think of it as the "upper level" of
+ * some drivers, where the "lower level" is hardware-specific.
+ *
+ * This does error checks, tracks devices and urbs, and delegates to a
+ * "hc_driver" only for code (and data) that really needs to know about
+ * hardware differences.  That includes root hub registers, i/o queues,
+ * and so on ... but as little else as possible.
+ *
+ * Shared code includes most of the "root hub" code (these are emulated,
+ * though each HC's hardware works differently) and PCI glue, plus request
+ * tracking overhead.  The HCD code should only block on spinlocks or on
+ * hardware handshaking; blocking on software events (such as other kernel
+ * threads releasing resources, or completing actions) is all generic.
+ *
+ * Happens the USB 2.0 spec says this would be invisible inside the "USBD",
+ * and includes mostly a "HCDI" (HCD Interface) along with some APIs used
+ * only by the hub driver ... and that neither should be seen or used by
+ * usb client device drivers.
+ *
+ * Contributors of ideas or unattributed patches include: David Brownell,
+ * Roman Weissgaerber, Rory Bolt, ...
+ *
+ * HISTORY:
+ * 2002-sept	Merge some 2.5 updates so we can share hardware level HCD
+ * 	code between the 2.4.20+ and 2.5 trees.
+ * 2002-feb	merge to 2.4.19
+ * 2001-12-12	Initial patch version for Linux 2.5.1 kernel.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* host controllers we manage */
+static LIST_HEAD (hcd_list);
+
+/* used when updating list of hcds */
+static DECLARE_MUTEX (hcd_list_lock);
+
+/* used when updating hcd data */
+static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+
+static struct usb_operations hcd_operations;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sharable chunks of root hub code.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#define KERNEL_REL	((LINUX_VERSION_CODE >> 16) & 0x0ff)
+#define KERNEL_VER	((LINUX_VERSION_CODE >> 8) & 0x0ff)
+
+/* usb 2.0 root hub device descriptor */
+static const u8 usb2_rh_dev_descriptor [18] = {
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x00, 0x02, /*  __u16 bcdUSB; v2.0 */
+
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x01,       /*  __u8  bDeviceProtocol; [ usb 2.0 single TT ]*/
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+
+	0x00, 0x00, /*  __u16 idVendor; */
+ 	0x00, 0x00, /*  __u16 idProduct; */
+	KERNEL_VER, KERNEL_REL, /*  __u16 bcdDevice */
+
+	0x03,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
+
+/* usb 1.1 root hub device descriptor */
+static const u8 usb11_rh_dev_descriptor [18] = {
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x10, 0x01, /*  __u16 bcdUSB; v1.1 */
+
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x00,       /*  __u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+
+	0x00, 0x00, /*  __u16 idVendor; */
+ 	0x00, 0x00, /*  __u16 idProduct; */
+	KERNEL_VER, KERNEL_REL, /*  __u16 bcdDevice */
+
+	0x03,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Configuration descriptors for our root hubs */
+
+static const u8 fs_rh_config_descriptor [] = {
+
+	/* one configuration */
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19, 0x00, /*  __u16 wTotalLength; */
+	0x01,       /*  __u8  bNumInterfaces; (1) */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0x40,       /*  __u8  bmAttributes; 
+				 Bit 7: Bus-powered,
+				     6: Self-powered,
+				     5 Remote-wakwup,
+				     4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+      
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x00,       /*  __u8  if_iInterface; */
+     
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x02, 0x00, /*  __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+	0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const u8 hs_rh_config_descriptor [] = {
+
+	/* one configuration */
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19, 0x00, /*  __u16 wTotalLength; */
+	0x01,       /*  __u8  bNumInterfaces; (1) */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0x40,       /*  __u8  bmAttributes; 
+				 Bit 7: Bus-powered,
+				     6: Self-powered,
+				     5 Remote-wakwup,
+				     4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+      
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x00,       /*  __u8  if_iInterface; */
+     
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x02, 0x00, /*  __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+	0x0c        /*  __u8  ep_bInterval; (256ms -- usb 2.0 spec) */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * helper routine for returning string descriptors in UTF-16LE
+ * input can actually be ISO-8859-1; ASCII is its 7-bit subset
+ */
+static int ascii2utf (char *s, u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *s++;
+		*utf++ = 0;
+	}
+	return retval;
+}
+
+/*
+ * rh_string - provides manufacturer, product and serial strings for root hub
+ * @id: the string ID number (1: serial number, 2: product, 3: vendor)
+ * @pci_desc: PCI device descriptor for the relevant HC
+ * @type: string describing our driver 
+ * @data: return packet in UTF-16 LE
+ * @len: length of the return packet
+ *
+ * Produces either a manufacturer, product or serial number string for the
+ * virtual root hub device.
+ */
+static int rh_string (
+	int		id,
+	struct usb_hcd	*hcd,
+	u8		*data,
+	int		len
+) {
+	char buf [100];
+
+	// language ids
+	if (id == 0) {
+		*data++ = 4; *data++ = 3;	/* 4 bytes string data */
+		*data++ = 0; *data++ = 0;	/* some language id */
+		return 4;
+
+	// serial number
+	} else if (id == 1) {
+		strcpy (buf, hcd->bus->bus_name);
+
+	// product description
+	} else if (id == 2) {
+                strcpy (buf, hcd->product_desc);
+
+ 	// id 3 == vendor description
+	} else if (id == 3) {
+                sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE,
+			hcd->description);
+
+	// unsupported IDs --> "protocol stall"
+	} else
+	    return 0;
+
+	data [0] = 2 * (strlen (buf) + 1);
+	data [1] = 3;	/* type == string */
+	return 2 + ascii2utf (buf, data + 2, len - 2);
+}
+
+
+/* Root hub control transfers execute synchronously */
+static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
+{
+	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+ 	u16		typeReq, wValue, wIndex, wLength;
+	const u8	*bufp = 0;
+	u8		*ubuf = urb->transfer_buffer;
+	int		len = 0;
+
+	typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
+	wValue   = le16_to_cpu (cmd->wValue);
+	wIndex   = le16_to_cpu (cmd->wIndex);
+	wLength  = le16_to_cpu (cmd->wLength);
+
+	if (wLength > urb->transfer_buffer_length)
+		goto error;
+
+	/* set up for success */
+	urb->status = 0;
+	urb->actual_length = wLength;
+	switch (typeReq) {
+
+	/* DEVICE REQUESTS */
+
+	case DeviceRequest | USB_REQ_GET_STATUS:
+		// DEVICE_REMOTE_WAKEUP
+		ubuf [0] = 1; // selfpowered
+		ubuf [1] = 0;
+			/* FALLTHROUGH */
+	case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+	case DeviceOutRequest | USB_REQ_SET_FEATURE:
+		dbg ("no device features yet yet");
+		break;
+	case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+		ubuf [0] = 1;
+			/* FALLTHROUGH */
+	case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		switch (wValue & 0xff00) {
+		case USB_DT_DEVICE << 8:
+			if (hcd->driver->flags & HCD_USB2)
+				bufp = usb2_rh_dev_descriptor;
+			else if (hcd->driver->flags & HCD_USB11)
+				bufp = usb11_rh_dev_descriptor;
+			else
+				goto error;
+			len = 18;
+			break;
+		case USB_DT_CONFIG << 8:
+			if (hcd->driver->flags & HCD_USB2) {
+				bufp = hs_rh_config_descriptor;
+				len = sizeof hs_rh_config_descriptor;
+			} else {
+				bufp = fs_rh_config_descriptor;
+				len = sizeof fs_rh_config_descriptor;
+			}
+			break;
+		case USB_DT_STRING << 8:
+			urb->actual_length = rh_string (
+				wValue & 0xff, hcd,
+				ubuf, wLength);
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case DeviceRequest | USB_REQ_GET_INTERFACE:
+		ubuf [0] = 0;
+			/* FALLTHROUGH */
+	case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+		break;
+	case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+		// wValue == urb->dev->devaddr
+		dbg ("%s root hub device address %d",
+			hcd->bus->bus_name, wValue);
+		break;
+
+	/* INTERFACE REQUESTS (no defined feature/status flags) */
+
+	/* ENDPOINT REQUESTS */
+
+	case EndpointRequest | USB_REQ_GET_STATUS:
+		// ENDPOINT_HALT flag
+		ubuf [0] = 0;
+		ubuf [1] = 0;
+			/* FALLTHROUGH */
+	case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+	case EndpointOutRequest | USB_REQ_SET_FEATURE:
+		dbg ("no endpoint features yet");
+		break;
+
+	/* CLASS REQUESTS (and errors) */
+
+	default:
+		/* non-generic request */
+		urb->status = hcd->driver->hub_control (hcd,
+			typeReq, wValue, wIndex,
+			ubuf, wLength);
+		break;
+error:
+		/* "protocol stall" on error */
+		urb->status = -EPIPE;
+		dbg ("unsupported hub control message (maxchild %d)",
+				urb->dev->maxchild);
+	}
+	if (urb->status) {
+		urb->actual_length = 0;
+		dbg ("CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d",
+			typeReq, wValue, wIndex, wLength, urb->status);
+	}
+	if (bufp) {
+		if (urb->transfer_buffer_length < len)
+			len = urb->transfer_buffer_length;
+		urb->actual_length = len;
+		// always USB_DIR_IN, toward host
+		memcpy (ubuf, bufp, len);
+	}
+
+	/* any errors get returned through the urb completion */
+	usb_hcd_giveback_urb (hcd, urb, 0);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Root Hub interrupt transfers are synthesized with a timer.
+ * Completions are called in_interrupt() but not in_irq().
+ */
+
+static void rh_report_status (unsigned long ptr);
+
+static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) 
+{
+	int	len = 1 + (urb->dev->maxchild / 8);
+
+	/* rh_timer protected by hcd_data_lock */
+	if (timer_pending (&hcd->rh_timer)
+			|| urb->status != -EINPROGRESS
+			|| !HCD_IS_RUNNING (hcd->state)
+			|| urb->transfer_buffer_length < len) {
+		dbg ("not queuing status urb, stat %d", urb->status);
+		return -EINVAL;
+	}
+
+	urb->hcpriv = hcd;	/* nonzero to indicate it's queued */
+	init_timer (&hcd->rh_timer);
+	hcd->rh_timer.function = rh_report_status;
+	hcd->rh_timer.data = (unsigned long) urb;
+	/* USB 2.0 spec says 256msec; this is close enough */
+	hcd->rh_timer.expires = jiffies + HZ/4;
+	add_timer (&hcd->rh_timer);
+	return 0;
+}
+
+/* timer callback */
+
+static void rh_report_status (unsigned long ptr)
+{
+	struct urb	*urb;
+	struct usb_hcd	*hcd;
+	int		length;
+	unsigned long	flags;
+
+	urb = (struct urb *) ptr;
+	spin_lock_irqsave (&urb->lock, flags);
+	if (!urb->dev) {
+		spin_unlock_irqrestore (&urb->lock, flags);
+		return;
+	}
+
+	hcd = urb->dev->bus->hcpriv;
+	if (urb->status == -EINPROGRESS) {
+		if (HCD_IS_RUNNING (hcd->state)) {
+			length = hcd->driver->hub_status_data (hcd,
+					urb->transfer_buffer);
+			spin_unlock_irqrestore (&urb->lock, flags);
+			if (length > 0) {
+				urb->actual_length = length;
+				urb->status = 0;
+				urb->complete (urb);
+			}
+			spin_lock_irqsave (&hcd_data_lock, flags);
+			urb->status = -EINPROGRESS;
+			if (HCD_IS_RUNNING (hcd->state)
+					&& rh_status_urb (hcd, urb) != 0) {
+				/* another driver snuck in? */
+				dbg ("%s, can't resubmit roothub status urb?",
+					hcd->bus->bus_name);
+				spin_unlock_irqrestore (&hcd_data_lock, flags);
+				BUG ();
+			}
+			spin_unlock_irqrestore (&hcd_data_lock, flags);
+		} else
+			spin_unlock_irqrestore (&urb->lock, flags);
+	} else {
+		/* this urb's been unlinked */
+		urb->hcpriv = 0;
+		spin_unlock_irqrestore (&urb->lock, flags);
+
+		usb_hcd_giveback_urb (hcd, urb, 0);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
+{
+	if (usb_pipeint (urb->pipe)) {
+		int		retval;
+		unsigned long	flags;
+
+		spin_lock_irqsave (&hcd_data_lock, flags);
+		retval = rh_status_urb (hcd, urb);
+		spin_unlock_irqrestore (&hcd_data_lock, flags);
+		return retval;
+	}
+	if (usb_pipecontrol (urb->pipe))
+		return rh_call_control (hcd, urb);
+	else
+		return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	del_timer_sync (&hcd->rh_timer);
+	hcd->rh_timer.data = 0;
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+	/* we rely on RH callback code not unlinking its URB! */
+	usb_hcd_giveback_urb (hcd, urb, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PCI
+
+/* PCI-based HCs are normal, but custom bus glue should be ok */
+
+static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+static void hc_died (struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_pci_probe - initialize PCI-based HCDs
+ * @dev: USB Host Controller being probed
+ * @id: pci hotplug id connecting controller to HCD framework
+ * Context: !in_interrupt()
+ *
+ * Allocates basic PCI resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct hc_driver	*driver;
+	unsigned long		resource, len;
+	void			*base;
+	struct usb_bus		*bus;
+	struct usb_hcd		*hcd;
+	int			retval, region;
+	char			buf [8], *bufp = buf;
+
+	if (!id || !(driver = (struct hc_driver *) id->driver_data))
+		return -EINVAL;
+
+	if (pci_enable_device (dev) < 0)
+		return -ENODEV;
+	
+        if (!dev->irq) {
+        	err ("Found HC with no IRQ.  Check BIOS/PCI %s setup!",
+			dev->slot_name);
+   	        return -ENODEV;
+        }
+	
+	if (driver->flags & HCD_MEMORY) {	// EHCI, OHCI
+		region = 0;
+		resource = pci_resource_start (dev, 0);
+		len = pci_resource_len (dev, 0);
+		if (!request_mem_region (resource, len, driver->description)) {
+			dbg ("controller already in use");
+			return -EBUSY;
+		}
+		base = ioremap_nocache (resource, len);
+		if (base == NULL) {
+			dbg ("error mapping memory");
+			retval = -EFAULT;
+clean_1:
+			release_mem_region (resource, len);
+			err ("init %s fail, %d", dev->slot_name, retval);
+			return retval;
+		}
+
+	} else { 				// UHCI
+		resource = len = 0;
+		for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+			if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
+				continue;
+
+			resource = pci_resource_start (dev, region);
+			len = pci_resource_len (dev, region);
+			if (request_region (resource, len,
+					driver->description))
+				break;
+		}
+		if (region == PCI_ROM_RESOURCE) {
+			dbg ("no i/o regions available");
+			return -EBUSY;
+		}
+		base = (void *) resource;
+	}
+
+	// driver->start(), later on, will transfer device from
+	// control by SMM/BIOS to control by Linux (if needed)
+
+	pci_set_master (dev);
+	hcd = driver->hcd_alloc ();
+	if (hcd == NULL){
+		dbg ("hcd alloc fail");
+		retval = -ENOMEM;
+clean_2:
+		if (driver->flags & HCD_MEMORY) {
+			iounmap (base);
+			goto clean_1;
+		} else {
+			release_region (resource, len);
+			err ("init %s fail, %d", dev->slot_name, retval);
+			return retval;
+		}
+	}
+	pci_set_drvdata(dev, hcd);
+	hcd->driver = driver;
+	hcd->description = driver->description;
+	hcd->pdev = dev;
+	printk (KERN_INFO "%s %s: %s\n",
+			hcd->description,  dev->slot_name, dev->name);
+
+#ifndef __sparc__
+	sprintf (buf, "%d", dev->irq);
+#else
+	bufp = __irq_itoa(dev->irq);
+#endif
+	if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
+			!= 0) {
+		err ("request interrupt %s failed", bufp);
+		retval = -EBUSY;
+clean_3:
+		driver->hcd_free (hcd);
+		goto clean_2;
+	}
+	hcd->irq = dev->irq;
+
+	hcd->regs = base;
+	hcd->region = region;
+	printk (KERN_INFO "%s %s: irq %s, %s %p\n",
+		hcd->description,  dev->slot_name, bufp,
+		(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
+		base);
+
+// FIXME simpler: make "bus" be that data, not pointer to it.
+// (fixed in 2.5)
+	bus = usb_alloc_bus (&hcd_operations);
+	if (bus == NULL) {
+		dbg ("usb_alloc_bus fail");
+		retval = -ENOMEM;
+		free_irq (dev->irq, hcd);
+		goto clean_3;
+	}
+	hcd->bus = bus;
+	bus->bus_name = dev->slot_name;
+	hcd->product_desc = dev->name;
+	bus->hcpriv = (void *) hcd;
+
+	INIT_LIST_HEAD (&hcd->dev_list);
+	INIT_LIST_HEAD (&hcd->hcd_list);
+
+	down (&hcd_list_lock);
+	list_add (&hcd->hcd_list, &hcd_list);
+	up (&hcd_list_lock);
+
+	usb_register_bus (bus);
+
+	if ((retval = driver->start (hcd)) < 0)
+		usb_hcd_pci_remove (dev);
+
+	return retval;
+} 
+EXPORT_SYMBOL (usb_hcd_pci_probe);
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pci_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * Store this function in the HCD's struct pci_driver as remove().
+ */
+void usb_hcd_pci_remove (struct pci_dev *dev)
+{
+	struct usb_hcd		*hcd;
+	struct usb_device	*hub;
+
+	hcd = pci_get_drvdata(dev);
+	if (!hcd)
+		return;
+	printk (KERN_INFO "%s %s: remove state %x\n",
+		hcd->description,  dev->slot_name, hcd->state);
+
+	if (in_interrupt ()) BUG ();
+
+	hub = hcd->bus->root_hub;
+	hcd->state = USB_STATE_QUIESCING;
+
+	dbg ("%s: roothub graceful disconnect", hcd->bus->bus_name);
+	usb_disconnect (&hub);
+	// usb_disconnect (&hcd->bus->root_hub);
+
+	hcd->driver->stop (hcd);
+	hcd->state = USB_STATE_HALT;
+
+	free_irq (hcd->irq, hcd);
+	if (hcd->driver->flags & HCD_MEMORY) {
+		iounmap (hcd->regs);
+		release_mem_region (pci_resource_start (dev, 0),
+			pci_resource_len (dev, 0));
+	} else {
+		release_region (pci_resource_start (dev, hcd->region),
+			pci_resource_len (dev, hcd->region));
+	}
+
+	down (&hcd_list_lock);
+	list_del (&hcd->hcd_list);
+	up (&hcd_list_lock);
+
+	usb_deregister_bus (hcd->bus);
+	usb_free_bus (hcd->bus);
+	hcd->bus = NULL;
+
+	hcd->driver->hcd_free (hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_remove);
+
+
+#ifdef	CONFIG_PM
+
+/*
+ * Some "sleep" power levels imply updating struct usb_driver
+ * to include a callback asking hcds to do their bit by checking
+ * if all the drivers can suspend.  Gets involved with remote wakeup.
+ *
+ * If there are pending urbs, then HCs will need to access memory,
+ * causing extra power drain.  New sleep()/wakeup() PM calls might
+ * be needed, beyond PCI suspend()/resume().  The root hub timer
+ * still be accessing memory though ...
+ *
+ * FIXME:  USB should have some power budgeting support working with
+ * all kinds of hubs.
+ *
+ * FIXME:  This assumes only D0->D3 suspend and D3->D0 resume.
+ * D1 and D2 states should do something, yes?
+ *
+ * FIXME:  Should provide generic enable_wake(), calling pci_enable_wake()
+ * for all supported states, so that USB remote wakeup can work for any
+ * devices that support it (and are connected via powered hubs).
+ *
+ * FIXME:  resume doesn't seem to work right any more...
+ */
+
+
+// 2.4 kernels have issued concurrent resumes (w/APM)
+// we defend against that error; PCI doesn't yet.
+
+/**
+ * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
+ * @dev: USB Host Controller being suspended
+ *
+ * Store this function in the HCD's struct pci_driver as suspend().
+ */
+int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
+{
+	struct usb_hcd		*hcd;
+	int			retval;
+
+	hcd = pci_get_drvdata(dev);
+	printk (KERN_INFO "%s %s: suspend to state %d\n",
+		hcd->description,  dev->slot_name, state);
+
+	pci_save_state (dev, hcd->pci_state);
+
+	// FIXME for all connected devices, leaf-to-root:
+	// driver->suspend()
+	// proposed "new 2.5 driver model" will automate that
+
+	/* driver may want to disable DMA etc */
+	retval = hcd->driver->suspend (hcd, state);
+	hcd->state = USB_STATE_SUSPENDED;
+
+ 	pci_set_power_state (dev, state);
+	return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_suspend);
+
+/**
+ * usb_hcd_pci_resume - power management resume of a PCI-based HCD
+ * @dev: USB Host Controller being resumed
+ *
+ * Store this function in the HCD's struct pci_driver as resume().
+ */
+int usb_hcd_pci_resume (struct pci_dev *dev)
+{
+	struct usb_hcd		*hcd;
+	int			retval;
+
+	hcd = pci_get_drvdata(dev);
+	printk (KERN_INFO "%s %s: resume\n",
+		hcd->description,  dev->slot_name);
+
+	/* guard against multiple resumes (APM bug?) */
+	atomic_inc (&hcd->resume_count);
+	if (atomic_read (&hcd->resume_count) != 1) {
+		err ("concurrent PCI resumes for %s", hcd->bus->bus_name);
+		retval = 0;
+		goto done;
+	}
+
+	retval = -EBUSY;
+	if (hcd->state != USB_STATE_SUSPENDED) {
+		dbg ("can't resume, not suspended!");
+		goto done;
+	}
+	hcd->state = USB_STATE_RESUMING;
+
+	pci_set_power_state (dev, 0);
+	pci_restore_state (dev, hcd->pci_state);
+
+	retval = hcd->driver->resume (hcd);
+	if (!HCD_IS_RUNNING (hcd->state)) {
+		dbg ("resume %s failure, retval %d",
+			hcd->bus->bus_name, retval);
+		hc_died (hcd);
+// FIXME:  recover, reset etc.
+	} else {
+		// FIXME for all connected devices, root-to-leaf:
+		// driver->resume ();
+		// proposed "new 2.5 driver model" will automate that
+	}
+
+done:
+	atomic_dec (&hcd->resume_count);
+	return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_resume);
+
+#endif	/* CONFIG_PM */
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic HC operations.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* called from khubd, or root hub init threads for hcd-private init */
+static int hcd_alloc_dev (struct usb_device *udev)
+{
+	struct hcd_dev		*dev;
+	struct usb_hcd		*hcd;
+	unsigned long		flags;
+
+	if (!udev || udev->hcpriv)
+		return -EINVAL;
+	if (!udev->bus || !udev->bus->hcpriv)
+		return -ENODEV;
+	hcd = udev->bus->hcpriv;
+	if (hcd->state == USB_STATE_QUIESCING)
+		return -ENOLINK;
+
+	dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+	memset (dev, 0, sizeof *dev);
+
+	INIT_LIST_HEAD (&dev->dev_list);
+	INIT_LIST_HEAD (&dev->urb_list);
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	list_add (&dev->dev_list, &hcd->dev_list);
+	// refcount is implicit
+	udev->hcpriv = dev;
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void hcd_panic (void *_hcd)
+{
+	struct usb_hcd *hcd = _hcd;
+	hcd->driver->stop (hcd);
+}
+
+static void hc_died (struct usb_hcd *hcd)
+{
+	struct list_head	*devlist, *urblist;
+	struct hcd_dev		*dev;
+	struct urb		*urb;
+	unsigned long		flags;
+	
+	/* flag every pending urb as done */
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	list_for_each (devlist, &hcd->dev_list) {
+		dev = list_entry (devlist, struct hcd_dev, dev_list);
+		list_for_each (urblist, &dev->urb_list) {
+			urb = list_entry (urblist, struct urb, urb_list);
+			dbg ("shutdown %s urb %p pipe %x, current status %d",
+				hcd->bus->bus_name,
+				urb, urb->pipe, urb->status);
+			if (urb->status == -EINPROGRESS)
+				urb->status = -ESHUTDOWN;
+		}
+	}
+	urb = (struct urb *) hcd->rh_timer.data;
+	if (urb)
+		urb->status = -ESHUTDOWN;
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+	if (urb)
+		rh_status_dequeue (hcd, urb);
+
+	/* hcd->stop() needs a task context */
+	INIT_TQUEUE (&hcd->work, hcd_panic, hcd);
+	(void) schedule_task (&hcd->work);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void urb_unlink (struct urb *urb)
+{
+	unsigned long		flags;
+	struct usb_device	*dev;
+
+	/* Release any periodic transfer bandwidth */
+	if (urb->bandwidth)
+		usb_release_bandwidth (urb->dev, urb,
+			usb_pipeisoc (urb->pipe));
+
+	/* clear all state linking urb to this dev (and hcd) */
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	list_del_init (&urb->urb_list);
+	dev = urb->dev;
+	urb->dev = NULL;
+	usb_dec_dev_use (dev);
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+}
+
+
+/* may be called in any context with a valid urb->dev usecount */
+/* caller surrenders "ownership" of urb */
+
+static int hcd_submit_urb (struct urb *urb)
+{
+	int			status;
+	struct usb_hcd		*hcd;
+	struct hcd_dev		*dev;
+	unsigned long		flags;
+	int			pipe, temp, max;
+	int			mem_flags;
+
+	if (!urb || urb->hcpriv || !urb->complete)
+		return -EINVAL;
+
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->bandwidth = 0;
+	INIT_LIST_HEAD (&urb->urb_list);
+
+	if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0)
+		return -ENODEV;
+	hcd = urb->dev->bus->hcpriv;
+	dev = urb->dev->hcpriv;
+	if (!hcd || !dev)
+		return -ENODEV;
+
+	/* can't submit new urbs when quiescing, halted, ... */
+	if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state))
+		return -ESHUTDOWN;
+	pipe = urb->pipe;
+	temp = usb_pipetype (urb->pipe);
+	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe),
+			usb_pipeout (pipe)))
+		return -EPIPE;
+
+	/* NOTE: 2.5 passes this value explicitly in submit() */
+	mem_flags = GFP_ATOMIC;
+
+	/* FIXME there should be a sharable lock protecting us against
+	 * config/altsetting changes and disconnects, kicking in here.
+	 */
+
+	/* Sanity check, so HCDs can rely on clean data */
+	max = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
+	if (max <= 0) {
+		err ("bogus endpoint (bad maxpacket)");
+		return -EINVAL;
+	}
+
+	/* "high bandwidth" mode, 1-3 packets/uframe? */
+	if (urb->dev->speed == USB_SPEED_HIGH) {
+		int	mult;
+		switch (temp) {
+		case PIPE_ISOCHRONOUS:
+		case PIPE_INTERRUPT:
+			mult = 1 + ((max >> 11) & 0x03);
+			max &= 0x03ff;
+			max *= mult;
+		}
+	}
+
+	/* periodic transfers limit size per frame/uframe */
+	switch (temp) {
+	case PIPE_ISOCHRONOUS: {
+		int	n, len;
+
+		if (urb->number_of_packets <= 0)		    
+			return -EINVAL;
+		for (n = 0; n < urb->number_of_packets; n++) {
+			len = urb->iso_frame_desc [n].length;
+			if (len < 0 || len > max) 
+				return -EINVAL;
+		}
+
+		}
+		break;
+	case PIPE_INTERRUPT:
+		if (urb->transfer_buffer_length > max)
+			return -EINVAL;
+	}
+
+	/* the I/O buffer must usually be mapped/unmapped */
+	if (urb->transfer_buffer_length < 0)
+		return -EINVAL;
+
+	if (urb->next) {
+		warn ("use explicit queuing not urb->next");
+		return -EINVAL;
+	}
+
+#ifdef DEBUG
+	/* stuff that drivers shouldn't do, but which shouldn't
+	 * cause problems in HCDs if they get it wrong.
+	 */
+	{
+	unsigned int	orig_flags = urb->transfer_flags;
+	unsigned int	allowed;
+
+	/* enforce simple/standard policy */
+	allowed = USB_ASYNC_UNLINK;	// affects later unlinks
+	allowed |= USB_NO_FSBR;		// only affects UHCI
+	switch (temp) {
+	case PIPE_CONTROL:
+		allowed |= USB_DISABLE_SPD;
+		break;
+	case PIPE_BULK:
+		allowed |= USB_DISABLE_SPD | USB_QUEUE_BULK
+				| USB_ZERO_PACKET | URB_NO_INTERRUPT;
+		break;
+	case PIPE_INTERRUPT:
+		allowed |= USB_DISABLE_SPD;
+		break;
+	case PIPE_ISOCHRONOUS:
+		allowed |= USB_ISO_ASAP;
+		break;
+	}
+	urb->transfer_flags &= allowed;
+
+	/* fail if submitter gave bogus flags */
+	if (urb->transfer_flags != orig_flags) {
+		err ("BOGUS urb flags, %x --> %x",
+			orig_flags, urb->transfer_flags);
+		return -EINVAL;
+	}
+	}
+#endif
+	/*
+	 * Force periodic transfer intervals to be legal values that are
+	 * a power of two (so HCDs don't need to).
+	 *
+	 * FIXME want bus->{intr,iso}_sched_horizon values here.  Each HC
+	 * supports different values... this uses EHCI/UHCI defaults (and
+	 * EHCI can use smaller non-default values).
+	 */
+	switch (temp) {
+	case PIPE_ISOCHRONOUS:
+	case PIPE_INTERRUPT:
+		/* too small? */
+		if (urb->interval <= 0)
+			return -EINVAL;
+		/* too big? */
+		switch (urb->dev->speed) {
+		case USB_SPEED_HIGH:	/* units are microframes */
+			// NOTE usb handles 2^15
+			if (urb->interval > (1024 * 8))
+				urb->interval = 1024 * 8;
+			temp = 1024 * 8;
+			break;
+		case USB_SPEED_FULL:	/* units are frames/msec */
+		case USB_SPEED_LOW:
+			if (temp == PIPE_INTERRUPT) {
+				if (urb->interval > 255)
+					return -EINVAL;
+				// NOTE ohci only handles up to 32
+				temp = 128;
+			} else {
+				if (urb->interval > 1024)
+					urb->interval = 1024;
+				// NOTE usb and ohci handle up to 2^15
+				temp = 1024;
+			}
+			break;
+		default:
+			return -EINVAL;
+		}
+		/* power of two? */
+		while (temp > urb->interval)
+			temp >>= 1;
+		urb->interval = temp;
+	}
+
+
+	/*
+	 * FIXME:  make urb timeouts be generic, keeping the HCD cores
+	 * as simple as possible.
+	 */
+
+	// NOTE:  a generic device/urb monitoring hook would go here.
+	// hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
+	// It would catch submission paths for all urbs.
+
+	/*
+	 * Atomically queue the urb,  first to our records, then to the HCD.
+	 * Access to urb->status is controlled by urb->lock ... changes on
+	 * i/o completion (normal or fault) or unlinking.
+	 */
+
+	// FIXME:  verify that quiescing hc works right (RH cleans up)
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+		usb_inc_dev_use (urb->dev);
+		list_add (&urb->urb_list, &dev->urb_list);
+		status = 0;
+	} else {
+		INIT_LIST_HEAD (&urb->urb_list);
+		status = -ESHUTDOWN;
+	}
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+	if (status)
+		return status;
+
+	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
+	
+	/* For 2.4, don't map bounce buffer if it's a root hub operation. */
+	if (urb->dev == hcd->bus->root_hub) {
+		status = rh_urb_enqueue (hcd, urb);
+	} else {
+#ifdef CONFIG_PCI
+		if (usb_pipecontrol (urb->pipe))
+			urb->setup_dma = pci_map_single (
+					hcd->pdev,
+					urb->setup_packet,
+					sizeof (struct usb_ctrlrequest),
+					PCI_DMA_TODEVICE);
+		if (urb->transfer_buffer_length != 0)
+			urb->transfer_dma = pci_map_single (
+					hcd->pdev,
+					urb->transfer_buffer,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? PCI_DMA_FROMDEVICE
+					    : PCI_DMA_TODEVICE);
+#endif /* CONFIG_PCI */
+		status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+	}
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called in any context */
+static int hcd_get_frame_number (struct usb_device *udev)
+{
+	struct usb_hcd	*hcd = (struct usb_hcd *)udev->bus->hcpriv;
+	return hcd->driver->get_frame_number (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct completion_splice {		// modified urb context:
+	/* did we complete? */
+	struct completion	done;
+
+	/* original urb data */
+	void			(*complete)(struct urb *);
+	void			*context;
+};
+
+static void unlink_complete (struct urb *urb)
+{
+	struct completion_splice	*splice;
+
+	splice = (struct completion_splice *) urb->context;
+
+	/* issue original completion call */
+	urb->complete = splice->complete;
+	urb->context = splice->context;
+	urb->complete (urb);
+
+	/* then let the synchronous unlink call complete */
+	complete (&splice->done);
+}
+
+/*
+ * called in any context; note ASYNC_UNLINK restrictions
+ *
+ * caller guarantees urb won't be recycled till both unlink()
+ * and the urb's completion function return
+ */
+static int hcd_unlink_urb (struct urb *urb)
+{
+	struct hcd_dev			*dev;
+	struct usb_hcd			*hcd = 0;
+	unsigned long			flags;
+	struct completion_splice	splice;
+	int				retval;
+
+	if (!urb)
+		return -EINVAL;
+
+	/*
+	 * we contend for urb->status with the hcd core,
+	 * which changes it while returning the urb.
+	 *
+	 * Caller guaranteed that the urb pointer hasn't been freed, and
+	 * that it was submitted.  But as a rule it can't know whether or
+	 * not it's already been unlinked ... so we respect the reversed
+	 * lock sequence needed for the usb_hcd_giveback_urb() code paths
+	 * (urb lock, then hcd_data_lock) in case some other CPU is now
+	 * unlinking it.
+	 */
+	spin_lock_irqsave (&urb->lock, flags);
+	spin_lock (&hcd_data_lock);
+	if (!urb->hcpriv || urb->transfer_flags & USB_TIMEOUT_KILLED) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	if (!urb->dev || !urb->dev->bus) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	/* giveback clears dev; non-null means it's linked at this level */
+	dev = urb->dev->hcpriv;
+	hcd = urb->dev->bus->hcpriv;
+	if (!dev || !hcd) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	/* Any status except -EINPROGRESS means the HCD has already started
+	 * to return this URB to the driver.  In that case, there's no
+	 * more work for us to do.
+	 *
+	 * There's much magic because of "automagic resubmit" of interrupt
+	 * transfers, stopped only by explicit unlinking.  We won't issue
+	 * an "it's unlinked" callback more than once, but device drivers
+	 * can need to retry (SMP, -EAGAIN) an unlink request as well as
+	 * fake out the "not yet completed" state (set -EINPROGRESS) if
+	 * unlinking from complete().  Automagic eventually vanishes.
+	 *
+	 * FIXME use an URB_UNLINKED flag to match URB_TIMEOUT_KILLED
+	 */
+	if (urb->status != -EINPROGRESS) {
+		if (usb_pipetype (urb->pipe) == PIPE_INTERRUPT)
+			retval = -EAGAIN;
+		else
+			retval = -EBUSY;
+		goto done;
+	}
+
+	/* maybe set up to block on completion notification */
+	if ((urb->transfer_flags & USB_TIMEOUT_KILLED))
+		urb->status = -ETIMEDOUT;
+	else if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
+		if (in_interrupt ()) {
+			dbg ("non-async unlink in_interrupt");
+			retval = -EWOULDBLOCK;
+			goto done;
+		}
+		/* synchronous unlink: block till we see the completion */
+		init_completion (&splice.done);
+		splice.complete = urb->complete;
+		splice.context = urb->context;
+		urb->complete = unlink_complete;
+		urb->context = &splice;
+		urb->status = -ENOENT;
+	} else {
+		/* asynchronous unlink */
+		urb->status = -ECONNRESET;
+	}
+	spin_unlock (&hcd_data_lock);
+	spin_unlock_irqrestore (&urb->lock, flags);
+
+	if (urb == (struct urb *) hcd->rh_timer.data) {
+		rh_status_dequeue (hcd, urb);
+		retval = 0;
+	} else {
+		retval = hcd->driver->urb_dequeue (hcd, urb);
+// FIXME:  if retval and we tried to splice, whoa!!
+if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval);
+	}
+
+    	/* block till giveback, if needed */
+	if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED))
+			&& HCD_IS_RUNNING (hcd->state)
+			&& !retval) {
+		wait_for_completion (&splice.done);
+	} else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) {
+		return -EINPROGRESS;
+	}
+	goto bye;
+done:
+	spin_unlock (&hcd_data_lock);
+	spin_unlock_irqrestore (&urb->lock, flags);
+bye:
+	if (retval)
+		dbg ("%s: hcd_unlink_urb fail %d",
+		    hcd ? hcd->bus->bus_name : "(no bus?)",
+		    retval);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup */
+
+// FIXME:  likely best to have explicit per-setting (config+alt)
+// setup primitives in the usbcore-to-hcd driver API, so nothing
+// is implicit.  kernel 2.5 needs a bunch of config cleanup...
+
+static int hcd_free_dev (struct usb_device *udev)
+{
+	struct hcd_dev		*dev;
+	struct usb_hcd		*hcd;
+	unsigned long		flags;
+
+	if (!udev || !udev->hcpriv)
+		return -EINVAL;
+
+	if (!udev->bus || !udev->bus->hcpriv)
+		return -ENODEV;
+
+	// should udev->devnum == -1 ??
+
+	dev = udev->hcpriv;
+	hcd = udev->bus->hcpriv;
+
+	/* device driver problem with refcounts? */
+	if (!list_empty (&dev->urb_list)) {
+		dbg ("free busy dev, %s devnum %d (bug!)",
+			hcd->bus->bus_name, udev->devnum);
+		return -EINVAL;
+	}
+
+	hcd->driver->free_config (hcd, udev);
+
+	spin_lock_irqsave (&hcd_data_lock, flags);
+	list_del (&dev->dev_list);
+	udev->hcpriv = NULL;
+	spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+	kfree (dev);
+	return 0;
+}
+
+static struct usb_operations hcd_operations = {
+	allocate:		hcd_alloc_dev,
+	get_frame_number:	hcd_get_frame_number,
+	submit_urb:		hcd_submit_urb,
+	unlink_urb:		hcd_unlink_urb,
+	deallocate:		hcd_free_dev,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+{
+	struct usb_hcd		*hcd = __hcd;
+	int			start = hcd->state;
+
+	if (unlikely (hcd->state == USB_STATE_HALT))	/* irq sharing? */
+		return;
+
+	hcd->driver->irq (hcd, r);
+	if (hcd->state != start && hcd->state == USB_STATE_HALT)
+		hc_died (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_giveback_urb - return URB from HCD to device driver
+ * @hcd: host controller returning the URB
+ * @urb: urb being returned to the USB device driver.
+ * @regs: saved hardware registers (ignored on 2.4 kernels)
+ * Context: in_interrupt()
+ *
+ * This hands the URB from HCD to its USB device driver, using its
+ * completion function.  The HCD has freed all per-urb resources
+ * (and is done using urb->hcpriv).  It also released all HCD locks;
+ * the device driver won't cause deadlocks if it resubmits this URB,
+ * and won't confuse things by modifying and resubmitting this one.
+ * Bandwidth and other resources will be deallocated.
+ *
+ * HCDs must not use this for periodic URBs that are still scheduled
+ * and will be reissued.  They should just call their completion handlers
+ * until the urb is returned to the device driver by unlinking.
+ *
+ * NOTE that no urb->next processing is done, even for isochronous URBs.
+ * ISO streaming functionality can be achieved by having completion handlers
+ * re-queue URBs.  Such explicit queuing doesn't discard error reports.
+ */
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+{
+	int is_root_hub_operation;
+
+	/* Work this out here as urb_unlink clears urb->dev */
+	is_root_hub_operation = (urb->dev == hcd->bus->root_hub);
+
+	urb_unlink (urb);
+
+	// NOTE:  a generic device/urb monitoring hook would go here.
+	// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
+	// It would catch exit/unlink paths for all urbs, but non-exit
+	// completions for periodic urbs need hooks inside the HCD.
+	// hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)
+
+	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
+
+#ifdef CONFIG_PCI	
+	/* For 2.4, don't unmap bounce buffer if it's a root hub operation. */
+	if (usb_pipecontrol (urb->pipe) && !is_root_hub_operation)
+		pci_unmap_single (hcd->pdev, urb->setup_dma,
+				sizeof (struct usb_ctrlrequest),
+				PCI_DMA_TODEVICE);
+
+	if ((urb->transfer_buffer_length != 0) && !is_root_hub_operation)
+		pci_unmap_single (hcd->pdev, urb->transfer_dma,
+				urb->transfer_buffer_length,
+				usb_pipein (urb->pipe)
+				    ? PCI_DMA_FROMDEVICE
+				    : PCI_DMA_TODEVICE);
+#endif /* CONFIG_PCI */
+
+	/* pass ownership to the completion handler */
+	urb->complete (urb);
+}
+EXPORT_SYMBOL (usb_hcd_giveback_urb);
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/lowlevel/xu/xu.c xeno-usb.bk/tools/python/xen/lowlevel/xu/xu.c
--- xen-2.0-testing.bk/tools/python/xen/lowlevel/xu/xu.c	2005-01-10 03:18:06 +00:00
+++ xeno-usb.bk/tools/python/xen/lowlevel/xu/xu.c	2005-01-10 03:13:19 +00:00
@@ -287,6 +287,24 @@
         PyDict_SetItemString(dict, #_field, obj);                         \
     } while ( 0 )
 
+#define PSTR2CHAR(_struct, _field)                                        \
+ do {                                                                     \
+     PyObject *obj;                                                       \
+        if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL )     \
+        {                                                                 \
+            if ( PyString_Check(obj) )                                    \
+            {                                                             \
+                char *buffer = PyString_AsString(obj);                    \
+                                                                          \
+                strcpy(((_struct *)&xum->msg.msg[0])->_field,             \
+                        buffer);                                          \
+                /* Should complain about length - think later */          \
+                dict_items_parsed++;                                      \
+            }                                                             \
+        }                                                                 \
+        xum->msg.length = sizeof(_struct);                                \
+ } while ( 0 )
+
 typedef struct {
     PyObject_HEAD;
     control_msg_t msg;
@@ -478,6 +496,52 @@
     case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS):
         C2P(netif_be_driver_status_t, status, Int, Long);
         return dict;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
+        C2P(usbif_fe_interface_status_changed_t, status, Int, Long);
+        C2P(usbif_fe_interface_status_changed_t, evtchn, Int, Long);
+        C2P(usbif_fe_interface_status_changed_t, domid, Int, Long);
+        C2P(usbif_fe_interface_status_changed_t, bandwidth, Int, Long);
+	C2P(usbif_fe_interface_status_changed_t, num_ports, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
+        C2P(usbif_fe_driver_status_changed_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
+        C2P(usbif_fe_interface_connect_t, shmem_frame, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
+        C2P(usbif_be_create_t, domid, Int, Long);
+        C2P(usbif_be_create_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
+        C2P(usbif_be_destroy_t, domid, Int, Long);
+        C2P(usbif_be_destroy_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
+        C2P(usbif_be_connect_t, domid, Int, Long);
+        C2P(usbif_be_connect_t, shmem_frame, Int, Long);
+        C2P(usbif_be_connect_t, evtchn, Int, Long);
+        C2P(usbif_be_connect_t, bandwidth, Int, Long);
+        C2P(usbif_be_connect_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
+        C2P(usbif_be_disconnect_t, domid, Int, Long);
+        C2P(usbif_be_disconnect_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
+        C2P(usbif_be_driver_status_changed_t, status, Int, Long);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
+        C2P(usbif_be_claim_port_t, domid, Int, Long);
+        C2P(usbif_be_claim_port_t, usbif_port, Int, Long);
+        C2P(usbif_be_claim_port_t, status, Int, Long);
+        C2P(usbif_be_claim_port_t, path, String, String);
+        return dict;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
+        C2P(usbif_be_release_port_t, path, String, String);
+        return dict;
     case TYPE(CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET):
         C2P(mem_request_t, target, Int, Long);
         C2P(mem_request_t, status, Int, Long);
@@ -646,6 +710,53 @@
     case TYPE(CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET):
         P2C(mem_request_t, target, u32);
         P2C(mem_request_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
+        P2C(usbif_fe_interface_status_changed_t, status, u32);
+        P2C(usbif_fe_interface_status_changed_t, evtchn, u16);
+        P2C(usbif_fe_interface_status_changed_t, domid, domid_t);
+        P2C(usbif_fe_interface_status_changed_t, bandwidth, u32);
+	P2C(usbif_fe_interface_status_changed_t, num_ports, u32);
+        break;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
+        P2C(usbif_fe_driver_status_changed_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
+        P2C(usbif_fe_interface_connect_t, shmem_frame, memory_t);
+        break;
+    case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
+        P2C(usbif_be_create_t, domid, domid_t);
+        P2C(usbif_be_create_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
+        P2C(usbif_be_destroy_t, domid, domid_t);
+        P2C(usbif_be_destroy_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
+        P2C(usbif_be_connect_t, domid, domid_t);
+        P2C(usbif_be_connect_t, shmem_frame, memory_t);
+        P2C(usbif_be_connect_t, evtchn, u32);
+        P2C(usbif_be_connect_t, bandwidth, u32);
+        P2C(usbif_be_connect_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
+        P2C(usbif_be_disconnect_t, domid, domid_t);
+        P2C(usbif_be_disconnect_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
+        P2C(usbif_be_driver_status_changed_t, status, u32);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
+        P2C(usbif_be_claim_port_t, domid, domid_t);
+        P2C(usbif_be_claim_port_t, usbif_port, u32);
+        P2C(usbif_be_claim_port_t, status, u32);
+        PSTR2CHAR(usbif_be_claim_port_t, path);
+        printf("dict items parsed = %d", dict_items_parsed);
+        break;
+    case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
+        PSTR2CHAR(usbif_be_release_port_t, path);
         break;
     }
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/#XendClient.py# xeno-usb.bk/tools/python/xen/xend/#XendClient.py#
--- xen-2.0-testing.bk/tools/python/xen/xend/#XendClient.py#	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/#XendClient.py#	2004-09-08 21:29:00 +00:00
@@ -0,0 +1,655 @@
+#!/usr/bin/env python
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Client API for the HTTP interface on xend.
+Callable as a script - see main().
+Supports synchronous or asynchronous connection to xend.
+
+This API is the 'control-plane' for xend.
+The 'data-plane' is done separately. For example, consoles
+are accessed via sockets on xend, but the list of consoles
+is accessible via this API.
+"""
+import os
+import sys
+import httplib
+import types
+from StringIO import StringIO
+
+
+from twisted.protocols import http
+from twisted.internet.protocol import ClientCreator
+from twisted.internet.defer import Deferred
+from twisted.internet import reactor
+
+from encode import *
+import sxp
+import PrettyPrint
+
+DEBUG = 0
+
+class XendError(RuntimeError):
+    """Error class for 'expected errors' when talking to xend.
+    """
+    pass
+
+def fileof(val):
+    """Converter for passing configs or other 'large' data.
+    Handles lists, files directly.
+    Assumes a string is a file name and passes its contents.
+    """
+    if isinstance(val, types.ListType):
+        return sxp.to_string(val)
+    if isinstance(val, types.StringType):
+        return file(val)
+    if hasattr(val, 'readlines'):
+        return val
+    raise XendError('cannot convert value')
+
+# todo: need to sort of what urls/paths are using for objects.
+# e.g. for domains at the moment return '0'.
+# should probably return abs path w.r.t. server, e.g. /xend/domain/0.
+# As an arg, assume abs path is obj uri, otherwise just id.
+
+# Function to convert to full url: Xend.uri(path), e.g.
+# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0
+# And should accept urls for ids?
+
+class URL:
+    """A URL.
+    """
+
+    def __init__(self, proto='http', host='localhost', port=None, path='', query=None, frag=None):
+        self.proto = proto
+        self.host = host
+        if port: port = int(port)
+        self.port = port
+        self.path = path
+        self.query = query
+        self.frag = frag
+
+    def url(self):
+        """Get the full URL string including protocol, location and the full path.
+        """
+        return self.proto + '://' + self.location() + self.fullpath()
+
+    def location(self):
+        """Get the location part of the URL, including host and port, if present.
+        """
+        if self.port:
+            return self.host + ':' + str(self.port)
+        else:
+            return self.host
+
+    def fullpath(self):
+        """Get the full path part of the URL, including query and fragment if present.
+        """
+        u = [ self.path ]
+        if self.query:
+            u.append('?')
+            u.append(self.query)
+        if self.frag:
+            u.append('#')
+            u.append(self.frag)
+        return ''.join(u)
+
+    def relative(self, path='', query=None, frag=None):
+        """Create a URL relative to this one.
+        """
+        return URL(proto=self.proto,
+                   host=self.host,
+                   port=self.port,
+                   path=self.path + path,
+                   query=query,
+                   frag=frag)
+
+class XendRequest:
+    """A request to xend.
+    """
+
+    def __init__(self, url, method, args):
+        """Create a request. Sets up the headers, argument data, and the
+        url.
+
+        @param url:    the url to request
+        @param method: request method, GET or POST
+        @param args:   dict containing request args, if any
+        """
+        if url.proto != 'http':
+            raise ValueError('Invalid protocol: ' + url.proto)
+        (hdr, data) = encode_data(args)
+        if args and method == 'GET':
+            url.query = data
+            data = None
+        if method == "POST" and url.path.endswith('/'):
+            url.path = url.path[:-1]
+
+        self.headers = hdr
+        self.data = data
+        self.url = url
+        self.method = method
+
+class XendClientProtocol:
+    """Abstract class for xend clients.
+    """
+    def xendRequest(self, url, method, args=None):
+        """Make a request to xend.
+        Implement in a subclass.
+
+        @param url:    xend request url
+        @param method: http method: POST or GET
+        @param args:   request arguments (dict)
+        """
+        raise NotImplementedError()
+
+    def xendGet(self, url, args=None):
+        """Make a xend request using HTTP GET.
+        Requests using GET are usually 'safe' and may be repeated without
+        nasty side-effects.
+
+        @param url:    xend request url
+        @param data:   request arguments (dict)
+        """
+        return self.xendRequest(url, "GET", args)
+
+    def xendPost(self, url, args):
+        """Make a xend request using HTTP POST.
+        Requests using POST potentially cause side-effects, and should
+        not be repeated unless you really want to repeat the side
+        effect.
+
+        @param url:    xend request url
+        @param args:   request arguments (dict)
+        """
+        return self.xendRequest(url, "POST", args)
+
+    def handleStatus(self, version, status, message):
+        """Handle the status returned from the request.
+        """
+        status = int(status)
+        if status in [ http.NO_CONTENT ]:
+            return None
+        if status not in [ http.OK, http.CREATED, http.ACCEPTED ]:
+            return self.handleException(XendError(message))
+        return 'ok'
+
+    def handleResponse(self, data):
+        """Handle the data returned in response to the request.
+        """
+        if data is None: return None
+        type = self.getHeader('Content-Type')
+        if type != sxp.mime_type:
+            return data
+        try:
+            pin = sxp.Parser()
+            pin.input(data);
+            pin.input_eof()
+            val = pin.get_val()
+        except sxp.ParseError, err:
+            return self.handleException(err)
+        if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
+            err = XendError(val[1])
+            return self.handleException(err)
+        return val
+
+    def handleException(self, err):
+        """Handle an exception during the request.
+        May be overridden in a subclass.
+        """
+        raise err
+
+    def getHeader(self, key):
+        """Get a header from the response.
+        Case is ignored in the key.
+
+        @param key: header key
+        @return: header
+        """
+        raise NotImplementedError()
+
+class SynchXendClientProtocol(XendClientProtocol):
+    """A synchronous xend client. This will make a request, wait for
+    the reply and return the result.
+    """
+
+    resp = None
+
+    def xendRequest(self, url, method, args=None):
+        """Make a request to xend.
+
+        @param url:    xend request url
+        @param method: http method: POST or GET
+        @param args:   request arguments (dict)
+        """
+        self.request = XendRequest(url, method, args)
+        conn = httplib.HTTPConnection(url.location())
+        if DEBUG: conn.set_debuglevel(1)
+        conn.request(method, url.fullpath(), self.request.data, self.request.headers)
+        resp = conn.getresponse()
+        self.resp = resp
+        val = self.handleStatus(resp.version, resp.status, resp.reason)
+        if val is None:
+            data = None
+        else:
+            data = resp.read()
+        conn.close()
+        val = self.handleResponse(data)
+        return val
+
+    def getHeader(self, key):
+        return self.resp.getheader(key)
+
+
+class AsynchXendClient(http.HTTPClient):
+    """A subclass of twisted's HTTPClient to deal with a connection to xend.
+    Makes the request when connected, and delegates handling responses etc.
+    to its protocol (usually an AsynchXendClientProtocol instance).
+    """
+    def __init__(self, protocol, request):
+        self.protocol = protocol
+        self.request = request
+
+    def connectionMade(self):
+        request = self.request
+        url = self.request.url
+        self.sendCommand(request.method, url.fullpath())
+        self.sendHeader('Host', url.location())
+        for (k, v) in request.headers.items():
+            self.sendHeader(k, v)
+        if request.data:
+            self.sendHeader('Content-Length', len(request.data))
+        self.endHeaders()
+        if request.data:
+            self.transport.write(request.data)
+
+    def handleStatus(self, version, status, message):
+        return self.protocol.handleStatus(version, status, message)
+
+    def handleHeader(self, key, val):
+        return self.protocol.handleHeader(key, val)
+
+    def handleResponse(self, data):
+        return self.protocol.handleResponse(data)
+
+class AsynchXendClientProtocol(XendClientProtocol):
+    """An asynchronous xend client. Uses twisted to connect to xend
+    and make the request. It does not block waiting for the result,
+    but sets up a deferred that is called when the result becomes available.
+
+    Uses AsynchXendClient to manage the connection.
+    """
+    def __init__(self):
+        self.err = None
+        self.headers = {}
+
+    def xendRequest(self, url, method, args=None):
+        """Make a request to xend. The returned deferred is called when
+        the result is available.
+        
+        @param url:    xend request url
+        @param method: http method: POST or GET
+        @param args:   request arguments (dict)
+        @return: deferred
+        """
+        request = XendRequest(url, method, args)
+        self.deferred = Deferred()
+        clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
+        clientCreator.connectTCP(url.host, url.port)
+        return self.deferred
+
+    def callErrback(self, err):
+        if not self.deferred.called:
+            self.err = err
+            self.deferred.errback(err)
+        return err
+
+    def callCallback(self, val):
+        if not self.deferred.called:
+            self.deferred.callback(val)
+        return val
+
+    def handleException(self, err):
+        return self.callErrback(err)
+
+    def handleHeader(self, key, val):
+        self.headers[key.lower()] = val
+
+    def getHeader(self, key):
+        return self.headers.get(key.lower())
+
+    def handleResponse(self, data):
+        if self.err: return self.err
+        val = XendClientProtocol.handleResponse(self, data)
+        if isinstance(val, Exception):
+            self.callErrback(val)
+        else:
+            self.callCallback(val)
+        return val
+        
+class Xend:
+    """Client interface to Xend.
+    """
+
+    """Default location of the xend server."""
+    SRV_DEFAULT = "localhost:8000"
+
+    """Environment variable to set the location of xend."""
+    SRV_VAR = "XEND"
+
+    """Default path to the xend root on the server."""
+    ROOT_DEFAULT = "/xend/"
+
+    """Environment variable to set the xend root path."""
+    ROOT_VAR = "XEND_ROOT"
+
+    def __init__(self, client=None, srv=None, root=None):
+        """Create a xend client interface.
+        If the client protocol is not specified, the default
+        is to use a synchronous protocol.
+
+        @param client:  client protocol to use
+        @param srv:     server host, and optional port (format host:port)
+        @param root:    xend root path on the server
+        """
+        if client is None:
+            client = SynchXendClientProtocol()
+        self.client = client
+        self.bind(srv, root)
+
+    def default_server(self):
+        """Get the default location of the xend server.
+        """
+        return os.getenv(self.SRV_VAR, self.SRV_DEFAULT)
+
+    def default_root(self):
+        """Get the default root path on the xend server.
+        """
+        return os.getenv(self.ROOT_VAR, self.ROOT_DEFAULT)
+
+    def bind(self, srv=None, root=None):
+        """Bind to a given server.
+
+        @param srv:  server location (host:port)
+        @param root: xend root path on the server
+        """
+        if srv is None: srv = self.default_server()
+        if root is None: root = self.default_root()
+        if not root.endswith('/'): root += '/'
+        (host, port) = srv.split(':', 1)
+        self.url = URL(host=host, port=port, path=root)
+
+    def xendGet(self, url, args=None):
+        return self.client.xendGet(url, args)
+
+    def xendPost(self, url, data):
+        return self.client.xendPost(url, data)
+
+    def nodeurl(self, id=''):
+        return self.url.relative('node/' + str(id))
+
+    def domainurl(self, id=''):
+        return self.url.relative('domain/' + str(id))
+
+    def consoleurl(self, id=''):
+        return self.url.relative('console/' + str(id))
+
+    def deviceurl(self, id=''):
+        return self.url.relative('device/' + str(id))
+
+    def vneturl(self, id=''):
+        return self.url.relative('vnet/' + str(id))
+
+    def eventurl(self, id=''):
+        return self.url.relative('event/' + str(id))
+
+    def xend(self):
+        return self.xendGet(self.url)
+
+    def xend_node(self):
+        return self.xendGet(self.nodeurl())
+        
+    def xend_node_shutdown(self):
+        return self.xendPost(self.nodeurl(),
+                             {'op'      : 'shutdown'})
+                
+    def xend_node_restart(self):
+        return self.xendPost(self.nodeurl(),
+                             {'op'      : 'reboot'})
+
+    def xend_node_dmesg(self):
+        return self.xendGet(self.nodeurl('dmesg'))
+
+    def xend_node_log(self):
+        return self.xendGet(self.nodeurl('log'))
+
+    def xend_node_cpu_rrobin_slice_set(self, slice):
+        return self.xendPost(self.nodeurl(),
+                             {'op'      : 'cpu_rrobin_slice_set',
+                              'slice'   : slice })
+
+    def xend_node_cpu_bvt_slice_set(self, ctx_allow):
+        return self.xendPost(self.nodeurl(),
+                             {'op'      : 'cpu_bvt_slice_set',
+                              'ctx_allow' : ctx_allow })
+
+    def xend_node_cpu_fbvt_slice_set(self, ctx_allow):
+        return self.xendPost(self.nodeurl(),
+                             {'op'      : 'cpu_fbvt_slice_set',
+                              'ctx_allow' : ctx_allow })
+
+    def xend_domains(self):
+        return self.xendGet(self.domainurl())
+
+    def xend_domain_create(self, conf):
+        return self.xendPost(self.domainurl(),
+                             {'op'      : 'create',
+                              'config'  : fileof(conf) })
+
+    def xend_domain_restore(self, filename):
+        return self.xendPost(self.domainurl(),
+                             {'op'      : 'restore',
+                              'file'    : filename })
+
+    def xend_domain_configure(self, id, conf):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'configure',
+                              'config'  : fileof(conf) })
+
+    def xend_domain(self, id):
+        return self.xendGet(self.domainurl(id))
+
+    def xend_domain_unpause(self, id):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'unpause' })
+
+    def xend_domain_pause(self, id):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'pause' })
+
+    def xend_domain_shutdown(self, id, reason):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'shutdown',
+                              'reason'  : reason })
+
+    def xend_domain_destroy(self, id, reason):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'destroy',
+                              'reason'  : reason })
+
+    def xend_domain_save(self, id, filename):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'save',
+                              'file'    : filename })
+
+    def xend_domain_migrate(self, id, dst, live=0):
+        return self.xendPost(self.domainurl(id),
+                             {'op'         : 'migrate',
+                              'destination': dst,
+                              'live'       : live })
+
+    def xend_domain_pincpu(self, id, cpu):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'pincpu',
+                              'cpu'     : cpu })
+
+    def xend_domain_cpu_bvt_set(self, id, mcuadv, warpback, warpvalue, warpl, warpu):
+        return self.xendPost(self.domainurl(id),
+                             {'op'       : 'cpu_bvt_set',
+                              'mcuadv'   : mcuadv,
+                              'warpback' : warpback,
+                              'warpvalue': warpvalue,
+                              'warpl'    : warpl,
+                              'warpu'    : warpu })
+
+    def xend_domain_cpu_fbvt_set(self, id, mcuadv, warp, warpl, warpu):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'cpu_fbvt_set',
+                              'mcuadv'  : mcuadv,
+                              'warp'    : warp,
+                              'warpl'   : warpl,
+                              'warpu'   : warpu })
+
+    def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'cpu_atropos_set',
+                              'period'  : period,
+                              'slice'   : slice,
+                              'latency' : latency,
+                              'xtratime': xtratime })
+
+    def xend_domain_maxmem_set(self, id, memory):
+        return self.xendPost(self.domainurl(id),
+                             { 'op'     : 'maxmem_set',
+                               'memory' : memory })
+
+    def xend_domain_vifs(self, id):
+        return self.xendGet(self.domainurl(id),
+                            { 'op'      : 'vifs' })
+
+    def xend_domain_vif(self, id, vif):
+        return self.xendGet(self.domainurl(id),
+                            { 'op'      : 'vif',
+                              'vif'     : vif })
+
+    def xend_domain_vbds(self, id):
+        return self.xendGet(self.domainurl(id),
+                            {'op'       : 'vbds'})
+
+    def xend_domain_vbd(self, id, vbd):
+        return self.xendGet(self.domainurl(id),
+                            {'op'       : 'vbd',
+                             'vbd'      : vbd })
+
+    def xend_domain_device_create(self, id, config):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'device_create',
+                              'config'  : fileof(config) })
+
+    def xend_domain_device_destroy(self, id, type, idx):
+        return self.xendPost(self.domainurl(id),
+                             {'op'      : 'device_destroy',
+                              'type'    : type,
+                              'index'   : idx })
+
+    def xend_consoles(self):
+        return self.xendGet(self.consoleurl())
+
+    def xend_console(self, id):
+        return self.xendGet(self.consoleurl(id))
+
+    def xend_console_disconnect(self, id):
+        return self.xendPost(self.consoleurl(id),
+                             {'op'      : 'disconnect'})
+
+    def xend_vnets(self):
+        return self.xendGet(self.vneturl())
+
+    def xend_vnet_create(self, conf):
+        return self.xendPost(self.vneturl(),
+                             {'op'      : 'create',
+                              'config'  : fileof(conf) })
+
+    def xend_vnet(self, id):
+        return self.xendGet(self.vneturl(id))
+
+    def xend_vnet_delete(self, id):
+        return self.xendPost(self.vneturl(id),
+                              {'op'     : 'delete' })
+
+    def xend_domain_
+
+
+    def xend_event_inject(self, sxpr):
+        val = self.xendPost(self.eventurl(),
+                             {'op'      : 'inject',
+                              'event'   : fileof(sxpr) })
+
+def xendmain(srv, asynch, fn, args):
+    if asynch:
+        client = AsynchXendClientProtocol()
+    else:
+        client = None
+    xend = Xend(srv=srv, client=client)
+    xend.rc = 0
+    try:
+        v = getattr(xend, fn)(*args)
+    except XendError, err:
+        print 'ERROR:', err
+        return 1
+    if asynch:
+        def cbok(val):
+            PrettyPrint.prettyprint(val)
+            reactor.stop()
+        def cberr(err):
+            print 'ERROR:', err
+            xend.rc = 1
+            reactor.stop()
+        v.addCallback(cbok)
+        v.addErrback(cberr)
+        reactor.run()
+        return xend.rc
+    else:
+        PrettyPrint.prettyprint(v)
+        return 0
+
+def main(argv):
+    """Call an API function:
+
+    python XendClient.py fn args...
+
+    The leading 'xend_' on the function can be omitted.
+    Example:
+
+python XendClient.py domains
+    (0 8)
+python XendClient.py domain 0
+    (domain (id 0) (name Domain-0) (memory 128))
+    """
+    global DEBUG
+    from getopt import getopt
+    short_options = 'x:ad'
+    long_options = ['xend=', 'asynch', 'debug']
+    (options, args) = getopt(argv[1:], short_options, long_options)
+    srv = None
+    asynch = 0
+    for k, v in options:
+        if k in ['-x', '--xend']:
+            srv = v
+        elif k in ['-a', '--asynch']:
+            asynch = 1
+        elif k in ['-d', '--debug']:
+            DEBUG = 1
+    if len(args):
+        fn = args[0]
+        args = args[1:]
+    else:
+        fn = 'xend'
+        args = []
+    if not fn.startswith('xend'):
+        fn = 'xend_' + fn
+    sys.exit(xendmain(srv, asynch, fn, args))
+
+if __name__ == "__main__":
+    main(sys.argv)
+else:    
+    server = Xend()
+    aserver = Xend( AsynchXendClientProtocol() )
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/XendDomainInfo.py xeno-usb.bk/tools/python/xen/xend/XendDomainInfo.py
--- xen-2.0-testing.bk/tools/python/xen/xend/XendDomainInfo.py	2005-01-10 03:18:06 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/XendDomainInfo.py	2005-01-10 03:13:20 +00:00
@@ -650,6 +650,7 @@
         """
         self.release_vifs()
         self.release_vbds()
+        self.release_usbifs()
         
         self.devices = {}
         self.device_index = {}
@@ -674,6 +675,15 @@
             log.debug("Destroying vbds for domain %d", self.dom)
             ctrl.destroy()
 
+    def release_usbifs(self):
+        """Release vm virtual USB devices (usbifs).
+        """
+        if self.dom is None: return
+        ctrl = xend.usbif_get(self.dom)
+        if ctrl:
+            log.debug("Destroying usbifs for domain %d", self.dom)
+            ctrl.destroy()
+
     def show(self):
         """Print virtual machine info.
         """
@@ -963,6 +973,8 @@
                 self.blkif_backend = 1
             elif name == 'netif':
                 self.netif_backend = 1
+            elif name == 'usbif':
+                self.usbif_backend = 1
             else:
                 raise VmError('invalid backend type:' + str(name))
 
@@ -1117,6 +1129,23 @@
     defer.addCallback(cbok)
     return defer
 
+def vm_dev_usb(vm, val, index):
+    """Attach the relevant physical ports to the domains' USB interface.
+
+    @param vm:    virtual machine
+    @param val:   USB interface config
+    @param index: USB interface index
+    @return: deferred
+    """
+    ctrl = xend.usbif_create(vm.dom, recreate=vm.recreate)
+    log.debug("Creating USB interface dom=%d", vm.dom)
+    defer = ctrl.attachDevice(val, recreate=vm.recreate)
+    def cbok(path):
+        vm.add_device('usb', val[1][1])
+        return path
+    defer.addCallback(cbok)
+    return defer
+
 def vm_dev_vbd(vm, val, index, change=0):
     """Create a virtual block device (vbd).
 
@@ -1220,6 +1249,7 @@
 add_device_handler('vif',  vm_dev_vif)
 add_device_handler('vbd',  vm_dev_vbd)
 add_device_handler('pci',  vm_dev_pci)
+add_device_handler('usb',  vm_dev_usb)
 
 # Ignore the fields we already handle.
 add_config_handler('name',       vm_field_ignore)
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/server/SrvDaemon.py xeno-usb.bk/tools/python/xen/xend/server/SrvDaemon.py
--- xen-2.0-testing.bk/tools/python/xen/xend/server/SrvDaemon.py	2005-01-10 03:18:06 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/server/SrvDaemon.py	2005-01-10 03:13:20 +00:00
@@ -40,6 +40,7 @@
 import channel
 import blkif
 import netif
+import usbif
 import console
 import domain
 from params import *
@@ -261,6 +262,7 @@
         val += self.daemon.consoles()
         val += self.daemon.blkifs()
         val += self.daemon.netifs()
+        val += self.daemon.usbifs()
         return val
 
     def op_sys_subscribe(self, name, v):
@@ -617,6 +619,7 @@
         self.domainCF = domain.DomainControllerFactory()
         self.blkifCF = blkif.BlkifControllerFactory()
         self.netifCF = netif.NetifControllerFactory()
+        self.usbifCF = usbif.UsbifControllerFactory()
         self.consoleCF = console.ConsoleControllerFactory()
 
     def listenEvent(self):
@@ -682,6 +685,15 @@
 
     def netif_get(self, dom):
         return self.netifCF.getControllerByDom(dom)
+
+    def usbif_create(self, dom, recreate=0):
+        return self.usbifCF.getController(dom)
+    
+    def usbifs(self):
+        return [ x.sxpr() for x in self.usbifCF.getControllers() ]
+
+    def usbif_get(self, dom):
+        return self.usbifCF.getControllerByDom(dom)
 
     def console_create(self, dom, console_port=None):
         """Create a console for a domain.
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/server/SrvUsbif.py xeno-usb.bk/tools/python/xen/xend/server/SrvUsbif.py
--- xen-2.0-testing.bk/tools/python/xen/xend/server/SrvUsbif.py	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/server/SrvUsbif.py	2005-01-10 03:13:20 +00:00
@@ -0,0 +1,249 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from twisted.protocols import http
+
+from xen.xend import sxp
+from xen.xend import XendDomain
+from xen.xend import XendConsole
+from xen.xend import PrettyPrint
+from xen.xend.Args import FormFn
+
+from SrvDir import SrvDir
+
+class SrvDomain(SrvDir):
+    """Service managing a single domain.
+    """
+
+    def __init__(self, dom):
+        SrvDir.__init__(self)
+        self.dom = dom
+        self.xd = XendDomain.instance()
+        self.xconsole = XendConsole.instance()
+
+    def op_configure(self, op, req):
+        """Configure an existing domain.
+        Configure is unusual in that it requires a domain id,
+        not a domain name.
+        """
+        fn = FormFn(self.xd.domain_configure,
+                    [['dom', 'int'],
+                     ['config', 'sxpr']])
+        deferred = fn(req.args, {'dom': self.dom.dom})
+        deferred.addErrback(self._op_configure_err, req)
+        return deferred
+
+    def _op_configure_err(self, err, req):
+        req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+        return str(err)
+        
+    def op_unpause(self, op, req):
+        val = self.xd.domain_unpause(self.dom.name)
+        return val
+        
+    def op_pause(self, op, req):
+        val = self.xd.domain_pause(self.dom.name)
+        return val
+
+    def op_shutdown(self, op, req):
+        fn = FormFn(self.xd.domain_shutdown,
+                    [['dom', 'str'],
+                     ['reason', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
+        req.setResponseCode(http.ACCEPTED)
+        req.setHeader("Location", "%s/.." % req.prePathURL())
+        return val
+
+    def op_destroy(self, op, req):
+        fn = FormFn(self.xd.domain_destroy,
+                    [['dom', 'str'],
+                     ['reason', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
+        req.setHeader("Location", "%s/.." % req.prePathURL())
+        return val
+
+    def op_save(self, op, req):
+        fn = FormFn(self.xd.domain_save,
+                    [['dom', 'str'],
+                     ['file', 'str']])
+        deferred = fn(req.args, {'dom': self.dom.id})
+        deferred.addCallback(self._op_save_cb, req)
+        deferred.addErrback(self._op_save_err, req)
+        return deferred
+
+    def _op_save_cb(self, val, req):
+        return 0
+
+    def _op_save_err(self, err, req):
+        req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+        return str(err)
+        
+    def op_migrate(self, op, req):
+        fn = FormFn(self.xd.domain_migrate,
+                    [['dom', 'str'],
+                     ['destination', 'str'],
+                     ['live', 'int']])
+        deferred = fn(req.args, {'dom': self.dom.id})
+        print 'op_migrate>', deferred
+        deferred.addCallback(self._op_migrate_cb, req)
+        deferred.addErrback(self._op_migrate_err, req)
+        return deferred
+
+    def _op_migrate_cb(self, info, req):
+        print '_op_migrate_cb>', info, req
+        #req.setResponseCode(http.ACCEPTED)
+        host = info.dst_host
+        port = info.dst_port
+        dom  = info.dst_dom
+        url = "http://%s:%d/xend/domain/%d" % (host, port, dom)
+        req.setHeader("Location", url)
+        print '_op_migrate_cb> url=', url
+        return url
+
+    def _op_migrate_err(self, err, req):
+        print '_op_migrate_err>', err, req
+        req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+        return str(err)
+
+    def op_pincpu(self, op, req):
+        fn = FormFn(self.xd.domain_pincpu,
+                    [['dom', 'str'],
+                     ['cpu', 'int']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def op_cpu_bvt_set(self, op, req):
+        fn = FormFn(self.xd.domain_cpu_bvt_set,
+                    [['dom', 'str'],
+                     ['mcuadv', 'int'],
+                     ['warpback', 'int'],
+                     ['warpvalue', 'int'],
+                     ['warpl', 'long'],
+                     ['warpu', 'long']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+    
+    def op_cpu_fbvt_set(self, op, req):
+        fn = FormFn(self.xd.domain_cpu_fbvt_set,
+                    [['dom', 'str'],
+                     ['mcuadv', 'int'],
+                     ['warp', 'int'],
+                     ['warpl', 'int'],
+                     ['warpu', 'int']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def op_cpu_atropos_set(self, op, req):
+        fn = FormFn(self.xd.domain_cpu_atropos_set,
+                    [['dom', 'str'],
+                     ['period', 'int'],
+                     ['slice', 'int'],
+                     ['latency', 'int'],
+                     ['xtratime', 'int']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def op_maxmem_set(self, op, req):
+        fn = FormFn(self.xd.domain_maxmem_set,
+                    [['dom', 'str'],
+                     ['memory', 'int']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def op_device_create(self, op, req):
+        fn = FormFn(self.xd.domain_device_create,
+                    [['dom', 'str'],
+                     ['config', 'sxpr']])
+        d = fn(req.args, {'dom': self.dom.id})
+        return d
+
+    def op_device_destroy(self, op, req):
+        fn = FormFn(self.xd.domain_device_destroy,
+                    [['dom', 'str'],
+                     ['type', 'str'],
+                     ['idx', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+                
+    def op_vifs(self, op, req):
+        devs = self.xd.domain_vif_ls(self.dom.id)
+        return [ dev.sxpr() for dev in devs ]
+
+    def op_vif(self, op, req):
+        fn = FormFn(self.xd.domain_vif_get,
+                    [['dom', 'str'],
+                     ['vif', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def op_vbds(self, op, req):
+        devs = self.xd.domain_vbd_ls(self.dom.id)
+        return [ dev.sxpr() for dev in devs ]
+
+    def op_vbd(self, op, req):
+        fn = FormFn(self.xd.domain_vbd_get,
+                    [['dom', 'str'],
+                     ['vbd', 'str']])
+        val = fn(req.args, {'dom': self.dom.id})
+        return val
+
+    def render_POST(self, req):
+        return self.perform(req)
+        
+    def render_GET(self, req):
+        op = req.args.get('op')
+        if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd']:
+            return self.perform(req)
+        if self.use_sxp(req):
+            req.setHeader("Content-Type", sxp.mime_type)
+            sxp.show(self.dom.sxpr(), out=req)
+        else:
+            req.write('<html><head></head><body>')
+            self.print_path(req)
+            #self.ls()
+            req.write('<p>%s</p>' % self.dom)
+            if self.dom.console:
+                cinfo = self.dom.console
+                cid = str(cinfo.console_port)
+                #todo: Local xref: need to know server prefix.
+                req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
+                          % (cid, cid))
+                req.write('<p><a href="%s">Connect to console</a></p>'
+                          % cinfo.uri())
+            if self.dom.config:
+                req.write("<code><pre>")
+                PrettyPrint.prettyprint(self.dom.config, out=req)
+                req.write("</pre></code>")
+            self.form(req)
+            req.write('</body></html>')
+        return ''
+
+    def form(self, req):
+        url = req.prePathURL()
+        req.write('<form method="post" action="%s">' % url)
+        req.write('<input type="submit" name="op" value="unpause">')
+        req.write('<input type="submit" name="op" value="pause">')
+        req.write('</form>')
+
+        req.write('<form method="post" action="%s">' % url)
+        req.write('<input type="submit" name="op" value="destroy">')
+        req.write('<input type="radio" name="reason" value="halt" checked>Halt')
+        req.write('<input type="radio" name="reason" value="reboot">Reboot')
+        req.write('</form>')
+
+        req.write('<form method="post" action="%s">' % url)
+        req.write('<input type="submit" name="op" value="shutdown">')
+        req.write('<input type="radio" name="reason" value="poweroff" checked>Poweroff')
+        req.write('<input type="radio" name="reason" value="halt">Halt')
+        req.write('<input type="radio" name="reason" value="reboot">Reboot')
+        req.write('</form>')
+        
+        req.write('<form method="post" action="%s">' % url)
+        req.write('<br><input type="submit" name="op" value="save">')
+        req.write(' To file: <input type="text" name="file">')
+        req.write('</form>')
+        
+        req.write('<form method="post" action="%s">' % url)
+        req.write('<br><input type="submit" name="op" value="migrate">')
+        req.write(' To host: <input type="text" name="destination">')
+        req.write('<input type="checkbox" name="live" value="1">Live')
+        req.write('</form>')
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/server/messages.py xeno-usb.bk/tools/python/xen/xend/server/messages.py
--- xen-2.0-testing.bk/tools/python/xen/xend/server/messages.py	2005-01-10 03:18:07 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/server/messages.py	2005-01-10 03:13:20 +00:00
@@ -189,6 +189,70 @@
 msg_formats.update(netif_formats)
 
 #============================================================================
+# USB interface message types.
+#============================================================================
+
+CMSG_USBIF_BE = 8
+CMSG_USBIF_FE = 9
+
+CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED = 0
+
+CMSG_USBIF_FE_DRIVER_STATUS_CHANGED = 32
+CMSG_USBIF_FE_INTERFACE_CONNECT     = 33
+CMSG_USBIF_FE_INTERFACE_DISCONNECT  = 34
+
+USBIF_DRIVER_STATUS_DOWN = 0
+USBIF_DRIVER_STATUS_UP   = 1
+
+USBIF_INTERFACE_STATUS_DESTROYED    = 0 #/* Interface doesn't exist.    */
+USBIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+USBIF_INTERFACE_STATUS_CONNECTED    = 2 #/* Exists and is connected.    */
+
+CMSG_USBIF_BE_CREATE = 0
+CMSG_USBIF_BE_DESTROY = 1
+CMSG_USBIF_BE_CONNECT = 2
+
+CMSG_USBIF_BE_DISCONNECT = 3
+CMSG_USBIF_BE_CLAIM_PORT = 4
+CMSG_USBIF_BE_RELEASE_PORT = 5
+
+CMSG_USBIF_BE_DRIVER_STATUS_CHANGED = 32
+
+USBIF_BE_STATUS_OKAY = 0
+USBIF_BE_STATUS_ERROR = 1
+
+USBIF_BE_STATUS_INTERFACE_EXISTS = 2
+USBIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
+USBIF_BE_STATUS_INTERFACE_CONNECTED = 4
+USBIF_BE_STATUS_OUT_OF_MEMORY = 7
+USBIF_BE_STATUS_MAPPING_ERROR = 9
+
+usbif_formats = {
+    'usbif_be_create_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE),
+    'usbif_be_destroy_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY),
+    'usbif_be_connect_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT),
+    'usbif_be_disconnect_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT),
+    'usbif_be_claim_port_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT),
+    'usbif_be_release_port_t':
+    (CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT),
+    'usbif_fe_interface_status_changed_t':
+    (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED),
+    'usbif_fe_driver_status_changed_t':
+    (CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED),
+    'usbif_fe_interface_connect_t':
+    (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT),
+    'usbif_fe_interface_disconnect_t':
+    (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT)
+    }
+    
+msg_formats.update(usbif_formats)
+
+#============================================================================
 # Domain shutdown message types.
 #============================================================================
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xend/server/usbif.py xeno-usb.bk/tools/python/xen/xend/server/usbif.py
--- xen-2.0-testing.bk/tools/python/xen/xend/server/usbif.py	1970-01-01 00:00:00 +00:00
+++ xeno-usb.bk/tools/python/xen/xend/server/usbif.py	2005-01-10 03:13:20 +00:00
@@ -0,0 +1,368 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+# Copyright (C) 2004 Intel Research Cambridge
+# Copyright (C) 2004 Mark Williamson <mark.williamson@cl.cam.ac.uk>
+"""Support for virtual USB hubs.
+"""
+
+from twisted.internet import defer
+#defer.Deferred.debug = 1
+
+from xen.xend import sxp
+from xen.xend.XendLogging import log
+from xen.xend.XendError import XendError
+
+import channel
+import controller
+from messages import *
+
+class UsbifBackendController(controller.BackendController):
+    """ Handler for the 'back-end' channel to a USB hub domain.
+    Must be connected using connect() before it can be used.
+    Do not create directly - use getBackend() on the UsbifController.
+    """
+
+    def __init__(self, ctrl, dom):
+        controller.BackendController.__init__(self, ctrl, dom)
+        self.connected = 0
+        self.evtchn = None
+        self.addMethod(CMSG_USBIF_BE,
+                       CMSG_USBIF_BE_DRIVER_STATUS_CHANGED,
+                       self.recv_be_driver_status_changed)
+        self.registerChannel()
+
+    def __str__(self):
+        return '<UsbifBackendController %d>' % (self.dom)
+
+    def recv_be_driver_status_changed(self, msg, req):
+        """Request handler for be_driver_status_changed messages.
+        
+        @param msg: message
+        @type  msg: xu message
+        @param req: request flag (true if the msg is a request)
+        @type  req: bool
+        """
+        val = unpackMsg('usbif_be_driver_status_changed_t', msg)
+        status = val['status']
+
+class UsbifBackendInterface(controller.BackendInterface):
+    """Handler for the 'back-end' channel to a network device driver domain
+    on behalf of a front-end domain.
+
+    Each network device is handled separately, so we add no functionality
+    here.
+    """
+    def __init__(self, ctrl, dom):
+        controller.BackendInterface.__init__(self, ctrl, dom, 0)
+        self.connected = 0
+        self.connecting = False
+
+    def connect(self, recreate=0):
+        """Connect the controller to the usbif control interface.
+
+        @param recreate: true if after xend restart
+        @return: deferred
+        """
+        log.debug("Connecting usbif %s", str(self))
+        if recreate or self.connected or self.connecting:
+            d = defer.succeed(self)
+        else:
+            self.connecting = True
+            d = self.send_be_create()
+            d.addCallback(self.respond_be_create)
+        return d
+        
+    def send_be_create(self):
+        d = defer.Deferred()
+        msg = packMsg('usbif_be_create_t',
+                      { 'domid'        : self.controller.dom })
+        self.writeRequest(msg, response=d)
+        return d
+
+    def respond_be_create(self, msg):
+        val = unpackMsg('usbif_be_create_t', msg)
+        log.debug('>UsbifBackendController>respond_be_create> %s', str(val))
+        self.connected = True
+        return self
+    
+    def destroy(self):
+        """Disconnect from the usbif control interface and destroy it.
+        """
+        def cb_destroy(val):
+            self.send_be_destroy()
+        d = defer.Deferred()
+        d.addCallback(cb_destroy)
+        self.send_be_disconnect(response=d)
+        
+    def send_be_disconnect(self, response=None):
+        log.debug('>UsbifBackendController>send_be_disconnect> %s', str(self))
+        msg = packMsg('usbif_be_disconnect_t',
+                      { 'domid'        : self.controller.dom })
+        self.writeRequest(msg, response=response)
+
+    def send_be_destroy(self, response=None):
+        log.debug('>UsbifBackendController>send_be_destroy> %s', str(self))
+        msg = packMsg('usbif_be_destroy_t',
+                      { 'domid'        : self.controller.dom })
+        self.writeRequest(msg, response=response)
+
+    def send_be_claim_port(self, path):
+        d=defer.Deferred()
+        log.debug(">UsbifBackendController>send_be_claim_port> about to claim port %s" % path)
+        def cb(blah): log.debug(">UsbifBackendController> Claim port completed")
+        d.addCallback(cb)
+        msg = packMsg('usbif_be_claim_port_t',
+                      { 'domid'        : self.controller.dom,
+                        'path'         : path,
+                        'usbif_port'   : self.controller.devices[path],
+                        'status'       : 0})
+        self.writeRequest(msg, response=d)
+        # No need to add any callbacks, since the guest polls its virtual ports
+        # anyhow, somewhat like a UHCI controller ;-)
+        return d
+
+    def send_be_release_port(self, path):
+        d=defer.Deferred()
+        def cb(blah): log.debug(">UsbifBackendController> Release port completed")
+        d.addCallback(cb)
+        msg = packMsg('usbif_be_release_port_t',
+                      { 'domid'        : self.controller.dom,
+                        'path'         : path })
+        self.writeRequest(msg, response)        
+        # No need to add any callbacks, since the guest polls its virtual ports
+        # anyhow, somewhat like a UHCI controller ;-)
+    
+    def connectInterface(self, val):
+        self.evtchn = channel.eventChannel(0, self.controller.dom)
+        log.debug(">UsbifBackendController>connectInterface> connecting usbif to event channel %s ports=%d:%d",
+                  str(self), self.evtchn['port1'], self.evtchn['port2'])
+        msg = packMsg('usbif_be_connect_t',
+                      { 'domid'        : self.controller.dom,
+                        'evtchn'       : self.evtchn['port1'],
+                        'shmem_frame'  : val['shmem_frame'],
+                        'bandwidth'    : 500 # XXX fix bandwidth!
+                        })
+        d = defer.Deferred()
+        d.addCallback(self.respond_be_connect)
+        self.writeRequest(msg, response=d)
+
+    def respond_be_connect(self, msg):
+        """Response handler for a be_connect message.
+
+        @param msg: message
+        @type  msg: xu message
+        """
+        val = unpackMsg('usbif_be_connect_t', msg)
+        log.debug('>UsbifBackendController>respond_be_connect> %s, %s', str(self), str(val))
+        d = defer.Deferred()
+        def cb(blah):
+            log.debug(">UsbifBackendController> Successfully connected USB interface for domain %d" % self.controller.dom)
+            self.controller.claim_ports()
+        d.addCallback(cb)
+        self.send_fe_interface_status_changed(d)
+            
+    def send_fe_interface_status_changed(self, response=None):
+        msg = packMsg('usbif_fe_interface_status_changed_t',
+                      { 'status' : USBIF_INTERFACE_STATUS_CONNECTED,
+                        'domid'  : 0, ## FIXME: should be domid of backend
+                        'evtchn' : self.evtchn['port2'],
+                        'bandwidth' : 500,
+                        'num_ports'    : len(self.controller.devices.keys())})
+        self.controller.writeRequest(msg, response=response)
+
+        
+class UsbifControllerFactory(controller.SplitControllerFactory):
+    """Factory for creating USB interface controllers.
+    """
+
+    def __init__(self):
+        controller.ControllerFactory.__init__(self)
+        self.backendControllers = {}
+
+    def createController(self, dom, recreate=0):
+        """Create a USB device controller for a domain.
+
+        @param dom: domain
+        @type  dom: int
+        @param recreate: if true it's a recreate (after xend restart)
+        @type  recreate: bool
+        @return: block device controller
+        @rtype: UsbifController
+        """
+        usbif = self.getControllerByDom(dom)
+        if usbif is None:
+            usbif = UsbifController(self, dom)
+            self.addController(usbif)
+        return usbif
+
+    def getDomainDevices(self, dom):
+        """Get the block devices for a domain.
+
+        @param dom: domain
+        @type  dom: int
+        @return: devices
+        @rtype:  [device]
+        """
+        usbif = self.getControllerByDom(dom)
+        return (usbif and usbif.getDevices()) or []
+
+    def getDomainDevice(self, dom, vdev):
+        """Get a block device from a domain.
+
+        @param dom: domain
+        @type  dom: int
+        @param vdev: device index
+        @type  vdev: int
+        @return: device
+        @rtype:  device
+        """
+        usbif = self.getControllerByDom(dom)
+        return (usbif and usbif.getDevice(vdev)) or None
+    
+    def createBackendInterface(self, ctrl, dom, handle):
+        """Create a network device backend interface.
+
+        @param ctrl: controller
+        @param dom: backend domain
+        @param handle: interface handle
+        @return: backend interface
+        """
+        return UsbifBackendInterface(ctrl, dom)
+
+    def getBackendController(self, dom):
+        """Get the backend controller for a domain, creating
+        if necessary.
+
+        @param dom: backend domain
+        @return: backend controller
+        """
+        b = self.getBackendControllerByDomain(dom)
+        if b is None:
+            b = self.createBackendController(dom)
+            self.backendControllers[b.dom] = b
+        return b
+
+    def createBackendController(self, dom):
+        return UsbifBackendController(self, dom)
+
+class UsbifController(controller.SplitController):
+    """USB device interface controller. Handles all USB devices
+    for a domain.
+    """
+    
+    def __init__(self, factory, dom):
+        """Create a USB device controller.
+        Do not call directly - use createController() on the factory instead.
+        """
+        controller.SplitController.__init__(self, factory, dom)
+        self.num_ports = 0
+        self.devices = {}
+        self.addMethod(CMSG_USBIF_FE,
+                       CMSG_USBIF_FE_DRIVER_STATUS_CHANGED,
+                       self.recv_fe_driver_status_changed)
+        self.addMethod(CMSG_USBIF_FE,
+                       CMSG_USBIF_FE_INTERFACE_CONNECT,
+                       self.recv_fe_interface_connect)
+        self.registerChannel()
+        try:
+            self.backendDomain = 0 #int(sxp.child_value(config, 'backend', '0')) TODO: configurable backends
+        except:
+            raise XendError('invalid backend domain')
+
+
+    def sxpr(self):
+        val = ['usbif', ['dom', self.dom]]
+        return val
+
+    def createBackend(self, dom, handle):
+        return UsbifBackendController(self, dom, handle)
+
+    def getDevices(self):
+        return self.devices.values()
+
+    def attachDevice(self, path, recreate=0):
+        """Add privileges for a particular device to the domain.
+        @param path: the Linux-style path to the device port
+        """
+        self.devices[path[1][1]] = self.num_ports
+        self.num_ports += 1
+        log.debug(">UsbifController>attachDevice> device: %s, port: %d" %
+                  (str(path), self.num_ports ) )
+
+        backend =self.getBackendInterface(self.backendDomain)
+
+        def cb(blah):
+            log.debug(">UsbifController> Backend created")
+            pass
+        d = backend.connect()
+        d.addCallback(cb) # Chaining the claim port operation
+        return d
+
+
+    def removeDevice(self, path):
+        self.delDevice(path)
+        backend = self.getBackendInterface(self.backendDomain)
+        return backend.send_be_release_port(path)
+
+    def delDevice(self, path):
+        if path in self.devices:
+            del self.devices[path]
+
+    def attachPort(self, path, recreate=0):
+        """Attach a device to the specified interface.
+        On success the returned deferred will be called with the device.
+
+        @return: deferred
+        @rtype:  Deferred
+        """
+        return self.attachDevice(path)
+
+    def destroy(self):
+        """Destroy the controller and all devices.
+        """
+        log.debug("Destroying usbif domain=%d", self.dom)
+        self.destroyBackends()
+
+    def destroyDevices(self):
+        """Destroy all devices.
+        """
+        for path in self.getDevices():
+            self.removeDevice(path)
+
+    def destroyBackends(self):
+        for backend in self.getBackendInterfaces():
+            backend.destroy()
+
+    def recv_fe_driver_status_changed(self, msg, req):
+        val = unpackMsg('usbif_fe_driver_status_changed_t', msg)
+        log.debug('>UsbifController>recv_fe_driver_status_changed> %s', str(val))
+        # For each backend?
+        msg = packMsg('usbif_fe_interface_status_changed_t',
+                      { 'status' : USBIF_INTERFACE_STATUS_DISCONNECTED,
+                        'domid'  : 0, ## FIXME: should be domid of backend
+                        'evtchn' : 0 })
+        d = defer.Deferred()
+        d.addCallback(self.disconnected_resp)
+        self.writeRequest(msg)
+
+    def disconnected_resp(self, msg):
+        val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
+        if val['status'] != USBIF_INTERFACE_STATUS_DISCONNECTED:
+            log.error(">UsbifController>disconnected_resp> unexpected status change")
+        else:
+            log.debug(">UsbifController>disconnected_resp> interface disconnected OK")
+
+    def recv_fe_interface_connect(self, msg, req):
+        val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
+        log.debug(">UsbifController>recv_fe_interface_connect> notifying backend")
+        backend = self.getBackendInterfaceByHandle(0)
+        if backend:
+            d = backend.connectInterface(val)
+        else:
+            log.error('>UsbifController>recv_fe_interface_connect> unknown interface')
+
+    def claim_ports(self):
+        backend = self.getBackendInterfaceByHandle(0)
+        for path in self.devices.keys():
+            log.debug(">UsbifController>claim_ports> claiming port... %s" % path)
+            backend.send_be_claim_port(path)
+
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/tools/python/xen/xm/create.py xeno-usb.bk/tools/python/xen/xm/create.py
--- xen-2.0-testing.bk/tools/python/xen/xm/create.py	2005-01-10 03:18:07 +00:00
+++ xeno-usb.bk/tools/python/xen/xm/create.py	2005-01-04 02:17:35 +00:00
@@ -147,6 +147,11 @@
          For example '-pci c0,02,1a'.
          The option may be repeated to add more than one pci device.""")
 
+gopts.var('usb', val='PATH',
+          fn=append_value, default=[],
+          use="""Add a physical USB port to a domain, as specified by the path
+          to that port.  This option may be repeated to add more than one port.""")
+
 gopts.var('ipaddr', val="IPADDR",
           fn=append_value, default=[],
           use="Add an IP address to the domain.")
@@ -254,6 +259,11 @@
         config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
         config_devs.append(['device', config_pci])
 
+def configure_usb(config_devs, vals):
+    for path in vals.usb:
+        config_usb = ['usb', ['path', path]]
+        config_devs.append(['device', config_usb])
+
 def randomMAC():
     """Generate a random MAC address.
 
@@ -337,6 +347,7 @@
     configure_disks(config_devs, vals)
     configure_pci(config_devs, vals)
     configure_vifs(config_devs, vals)
+    configure_usb(config_devs, vals)
     config += config_devs
     return config
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/xen/include/public/io/domain_controller.h xeno-usb.bk/xen/include/public/io/domain_controller.h
--- xen-2.0-testing.bk/xen/include/public/io/domain_controller.h	2005-01-10 03:18:11 +00:00
+++ xeno-usb.bk/xen/include/public/io/domain_controller.h	2005-01-10 03:13:24 +00:00
@@ -54,7 +54,8 @@
 #define CMSG_NETIF_FE       4  /* Network-device frontend */
 #define CMSG_SHUTDOWN       6  /* Shutdown messages       */
 #define CMSG_MEM_REQUEST    7  /* Memory reservation reqs */
-
+#define CMSG_USBIF_BE       8  /* USB controller backend  */
+#define CMSG_USBIF_FE       9  /* USB controller frontend */
 
 /******************************************************************************
  * CONSOLE DEFINITIONS
@@ -543,6 +544,208 @@
     u32        status;        /*  0: NETIF_DRIVER_STATUS_??? */
 } PACKED netif_be_driver_status_t; /* 4 bytes */
 
+
+
+/******************************************************************************
+ * USB-INTERFACE FRONTEND DEFINITIONS
+ */
+
+/* Messages from domain controller to guest. */
+#define CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED   0
+
+/* Messages from guest to domain controller. */
+#define CMSG_USBIF_FE_DRIVER_STATUS_CHANGED     32
+#define CMSG_USBIF_FE_INTERFACE_CONNECT         33
+#define CMSG_USBIF_FE_INTERFACE_DISCONNECT      34
+/*
+ * CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED:
+ *  Notify a guest about a status change on one of its block interfaces.
+ *  If the interface is DESTROYED or DOWN then the interface is disconnected:
+ *   1. The shared-memory frame is available for reuse.
+ *   2. Any unacknowledged messages pending on the interface were dropped.
+ */
+#define USBIF_INTERFACE_STATUS_DESTROYED    0 /* Interface doesn't exist.    */
+#define USBIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
+#define USBIF_INTERFACE_STATUS_CONNECTED    2 /* Exists and is connected.    */
+typedef struct {
+    u32 status; /*  0 */
+    u16 evtchn; /*  4: (only if status == BLKIF_INTERFACE_STATUS_CONNECTED). */
+    domid_t domid; /* 6: status != BLKIF_INTERFACE_STATUS_DESTROYED */
+    u32 bandwidth; /* 8 */
+    u32 num_ports; /* 12 */
+} PACKED usbif_fe_interface_status_changed_t; /* 12 bytes */
+
+/*
+ * CMSG_USBIF_FE_DRIVER_STATUS_CHANGED:
+ *  Notify the domain controller that the front-end driver is DOWN or UP.
+ *  When the driver goes DOWN then the controller will send no more
+ *  status-change notifications.
+ *  If the driver goes DOWN while interfaces are still UP, the domain
+ *  will automatically take the interfaces DOWN.
+ * 
+ *  NB. The controller should not send an INTERFACE_STATUS_CHANGED message
+ *  for interfaces that are active when it receives an UP notification. We
+ *  expect that the frontend driver will query those interfaces itself.
+ */
+#define USBIF_DRIVER_STATUS_DOWN   0
+#define USBIF_DRIVER_STATUS_UP     1
+typedef struct {
+    /* IN */
+    u32 status;        /*  0: USBIF_DRIVER_STATUS_??? */
+} PACKED usbif_fe_driver_status_changed_t; /* 4 bytes */
+
+/*
+ * CMSG_USBIF_FE_INTERFACE_CONNECT:
+ *  If successful, the domain controller will acknowledge with a
+ *  STATUS_CONNECTED message.
+ */
+typedef struct {
+    u32      __pad;
+    memory_t shmem_frame; /*  8 */
+    MEMORY_PADDING;
+} PACKED usbif_fe_interface_connect_t; /* 16 bytes */
+
+/*
+ * CMSG_BLKIF_FE_INTERFACE_DISCONNECT:
+ *  If successful, the domain controller will acknowledge with a
+ *  STATUS_DISCONNECTED message.
+ */
+typedef struct {} PACKED usbif_fe_interface_disconnect_t; /* 4 bytes */
+
+
+/******************************************************************************
+ * USB-INTERFACE BACKEND DEFINITIONS
+ */
+
+/* Messages from domain controller. */
+#define CMSG_USBIF_BE_CREATE       0  /* Create a new block-device interface. */
+#define CMSG_USBIF_BE_DESTROY      1  /* Destroy a block-device interface.    */
+#define CMSG_USBIF_BE_CONNECT      2  /* Connect i/f to remote driver.        */
+#define CMSG_USBIF_BE_DISCONNECT   3  /* Disconnect i/f from remote driver.   */
+#define CMSG_USBIF_BE_CLAIM_PORT   4  /* Claim host port for a domain.        */
+#define CMSG_USBIF_BE_RELEASE_PORT 5  /* Release host port.                   */
+/* Messages to domain controller. */
+#define CMSG_USBIF_BE_DRIVER_STATUS_CHANGED 32
+
+/* Non-specific 'okay' return. */
+#define USBIF_BE_STATUS_OKAY                0
+/* Non-specific 'error' return. */
+#define USBIF_BE_STATUS_ERROR               1
+/* The following are specific error returns. */
+#define USBIF_BE_STATUS_INTERFACE_EXISTS    2
+#define USBIF_BE_STATUS_INTERFACE_NOT_FOUND 3
+#define USBIF_BE_STATUS_INTERFACE_CONNECTED 4
+#define USBIF_BE_STATUS_OUT_OF_MEMORY       7
+#define USBIF_BE_STATUS_MAPPING_ERROR       9
+
+/* This macro can be used to create an array of descriptive error strings. */
+#define USBIF_BE_STATUS_ERRORS {    \
+    "Okay",                         \
+    "Non-specific error",           \
+    "Interface already exists",     \
+    "Interface not found",          \
+    "Interface is still connected", \
+    "Out of memory",                \
+    "Could not map domain memory" }
+
+/*
+ * CMSG_USBIF_BE_CREATE:
+ *  When the driver sends a successful response then the interface is fully
+ *  created. The controller will send a DOWN notification to the front-end
+ *  driver.
+ */
+typedef struct { 
+    /* IN */
+    domid_t    domid;         /*  0: Domain attached to new interface.   */
+    u16        __pad;
+    /* OUT */
+    u32        status;        /*  8 */
+} PACKED usbif_be_create_t; /* 12 bytes */
+
+/*
+ * CMSG_USBIF_BE_DESTROY:
+ *  When the driver sends a successful response then the interface is fully
+ *  torn down. The controller will send a DESTROYED notification to the
+ *  front-end driver.
+ */
+typedef struct { 
+    /* IN */
+    domid_t    domid;         /*  0: Identify interface to be destroyed. */
+    u16        __pad;
+    /* OUT */
+    u32        status;        /*  8 */
+} PACKED usbif_be_destroy_t; /* 12 bytes */
+
+/*
+ * CMSG_USBIF_BE_CONNECT:
+ *  When the driver sends a successful response then the interface is fully
+ *  connected. The controller will send a CONNECTED notification to the
+ *  front-end driver.
+ */
+typedef struct { 
+    /* IN */
+    domid_t    domid;         /*  0: Domain attached to new interface.   */
+    u16        __pad;
+    memory_t   shmem_frame;   /*  8: Page cont. shared comms window.     */
+    MEMORY_PADDING;
+    u32        evtchn;        /* 16: Event channel for notifications.    */
+    u32        bandwidth;     /* 20: Bandwidth allocated for isoch / int - us
+                               * per 1ms frame (ie between 0 and 900 or 800
+                               * depending on USB version). */
+    /* OUT */
+    u32        status;        /* 24 */
+} PACKED usbif_be_connect_t;  /* 28 bytes */
+
+/*
+ * CMSG_USBIF_BE_DISCONNECT:
+ *  When the driver sends a successful response then the interface is fully
+ *  disconnected. The controller will send a DOWN notification to the front-end
+ *  driver.
+ */
+typedef struct { 
+    /* IN */
+    domid_t    domid;         /*  0: Domain attached to new interface.   */
+    u16        __pad;
+    /* OUT */
+    u32        status;        /*  8 */
+} PACKED usbif_be_disconnect_t; /* 12 bytes */
+
+/*
+ * CMSG_USBIF_BE_DRIVER_STATUS_CHANGED:
+ *  Notify the domain controller that the back-end driver is DOWN or UP.
+ *  If the driver goes DOWN while interfaces are still UP, the controller
+ *  will automatically send DOWN notifications.
+ */
+typedef struct {
+    u32        status;        /*  0: USBIF_DRIVER_STATUS_??? */
+} PACKED usbif_be_driver_status_changed_t; /* 4 bytes */
+
+#define USB_PATH_LEN 16
+
+/*
+ * CMSG_USBIF_BE_CLAIM_PORT:
+ * Instruct the backend driver to claim any device plugged into the specified
+ * host port and to allow the specified domain to control that port.
+ */
+typedef struct 
+{
+    /* IN */
+    domid_t  domid;        /* 0:  which domain                 */
+    u32      usbif_port;   /* 6:  port on the virtual root hub */
+    u32      status;       /* 10: status of operation          */
+    char path[USB_PATH_LEN]; /* Currently specified in the Linux style - may need to be
+                    * converted to some OS-independent format at some stage. */
+} PACKED usbif_be_claim_port_t;
+
+/*
+ * CMSG_USBIF_BE_RELEASE_PORT: 
+ * Instruct the backend driver to release any device plugged into the specified
+ * host port.
+ */
+typedef struct
+{
+    char     path[USB_PATH_LEN];
+} PACKED usbif_be_release_port_t;
 
 /******************************************************************************
  * SHUTDOWN DEFINITIONS
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet xen-2.0-testing.bk/xen/include/public/xen.h xeno-usb.bk/xen/include/public/xen.h
--- xen-2.0-testing.bk/xen/include/public/xen.h	2005-01-10 03:18:10 +00:00
+++ xeno-usb.bk/xen/include/public/xen.h	2005-01-04 02:17:41 +00:00
@@ -419,7 +419,7 @@
 #define SIF_INITDOMAIN    (1<<1)  /* Is this the initial control domain? */
 #define SIF_BLK_BE_DOMAIN (1<<4)  /* Is this a block backend domain? */
 #define SIF_NET_BE_DOMAIN (1<<5)  /* Is this a net backend domain? */
-
+#define SIF_USB_BE_DOMAIN (1<<6)  /* Is this a usb backend domain? */
 /* For use in guest OSes. */
 extern shared_info_t *HYPERVISOR_shared_info;
 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-10 17:03 [PATCH] USB virtualisation (experimental) Mark Williamson
  2005-01-11  0:24 ` Nivedita Singhvi
  2005-01-14  1:38 ` [updated] " Mark A. Williamson
@ 2005-01-14  2:49 ` Mark Williamson
  2005-01-14 14:01   ` Nuutti Kotivuori
  2005-01-14 18:59   ` Adam Heath
  2 siblings, 2 replies; 15+ messages in thread
From: Mark Williamson @ 2005-01-14  2:49 UTC (permalink / raw)
  To: xen-devel

All,

I have updated the USB virtualisation patch at:
http://www.cambridge.intel-research.net/~mwilli2/xen-2.0-testing-usb.patch

Description:
* Give guest operating systems under Xen access to USB devices plugged into 
the host.  Adds a backend driver (for domain 0), a frontend driver (for 
guests) and extra support code in the tools.

Features:
* USB mass storage devices are now fully working (new!)
* USB isochronous transfers (larger buffers now supported, provided they are 
machine-contiguous)

Still to do:
* proper support for hot plugging of the physical devices
* stronger checking for iso transfers in the backend
* a little more work needed to make it work for all USB devices
* iron out a few lingering backend bugs

I'm intending to get this all done as soon as possible and then push into the 
-unstable tree.

Cheers,
Mark

On Monday 10 January 2005 17:03, Mark Williamson wrote:
> Hi all,
>
> A number of people have expressed interest in the USB virtualisation code.
> I'm making a patch against 2.0-testing available for those who wish to have
> a play with it.
>
> Description:
> USB virtualisation allows otherwise unprivileged domains to be given
> control of a USB port on the host.  The unprivileged domain sees a virtual
> USB host controller with only the ports it has been granted access to.  For
> instance, you might dedicate a USB webcam to one domain, a USB pendrive to
> another domain and a USB CD-burner to another.
>
> This patch includes the kernel modifications (backend and frontend drivers)
> to 2.4.28 and modifications to the userspace toolset.  This is sufficient
> to set up USB virtualisation for 2.4.28 on xen-2.0-testing.
>
> There are still a few sticky bugs that are really annoying me at the moment
> but it is possible to mount USB flash drives, capture realtime video from a
> webcam, etc. in a domU by virtualising through dom0.
>
> Disclaimers:
> * Don't use it on production systems
> * Don't trust it with data you care about
> * Some USB device drivers may need tweaking - if you have problems with a
> device, let me know
> * YMMV but it works for me ;-)
>
> Installation:
> 1) download the patch from
> http://www.cambridge.intel-research.net/~mwilli2/xen-2.0-testing-usb.patch
> 2) apply to a fresh xen-2.0-testing.bk using
> patch -p1 < xen-2.0-testing-usb.patch
> (say yes to all the SCCS checkouts)
> 3) set up some appropriate kernel configs in
> install/boot/config-2.4.28-xen{0,U}.  You should enable
> CONFIG_XEN_USB_FRONTEND, CONFIG_USB in the configuration for any devices
> you want to run.
> Don't (!) build drivers for devices you want to virtualise into dom0's
> kernel, or dom0 will grab the device before the backend gets a chance to
> export it. 4) build & install the updated kernel & tools on your test
> machine.  Note again you _have_ to use the 2.4 kernels, 2.6 isn't supported
> (yet). 5) for frontend domains, add a
> usb = [ 'port_number', 'port_number', ... ]
> to the config file to give a domain control of those physical USB ports. 
> You can specify ports on a hub by giving the path through the device tree
> e.g. '1/2' = second port on the hub plugged into the first port on the
> host.
>
> Any questions, comments, problems, success reports - let us know!
>
> Cheers,
> Mark
>
>
> -------------------------------------------------------
> The SF.Net email is sponsored by: Beat the post-holiday blues
> Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
> It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/xen-devel


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-14  2:49 ` Mark Williamson
@ 2005-01-14 14:01   ` Nuutti Kotivuori
  2005-01-14 14:07     ` Mark Williamson
  2005-01-14 18:59   ` Adam Heath
  1 sibling, 1 reply; 15+ messages in thread
From: Nuutti Kotivuori @ 2005-01-14 14:01 UTC (permalink / raw)
  To: xen-devel

Mark Williamson wrote:
> * USB mass storage devices are now fully working (new!)

Woo, nice! I have an external MP3 player that is an USB mass storage
hard drive - if the drive happens to spin down when plugging the cable
in, it manages to freeze the USB subsystem in the kernel. Now I can
just put it in a separate virtual machine and reboot it :-)

-- Naked




-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-14 14:01   ` Nuutti Kotivuori
@ 2005-01-14 14:07     ` Mark Williamson
  2005-01-15 15:37       ` Nuutti Kotivuori
  0 siblings, 1 reply; 15+ messages in thread
From: Mark Williamson @ 2005-01-14 14:07 UTC (permalink / raw)
  To: xen-devel; +Cc: Nuutti Kotivuori

> Woo, nice! I have an external MP3 player that is an USB mass storage
> hard drive - if the drive happens to spin down when plugging the cable

Hmm, that's a nasty problem - I wonder what causes it.

> in, it manages to freeze the USB subsystem in the kernel. Now I can
> just put it in a separate virtual machine and reboot it :-)

If it's freezing the USB subsystem (and nothing else), it implies a problem 
with the USB core and / or with the host controller driver.  If it's the 
latter, you'd have to run the driver for your (hardware) host controller in 
the domain so that could be restarted too - the appropriate tool for that 
would be the existing PCI driver domains stuff.

What kind of host controller do you have?  (or if you don't know, do you have 
USB 2.0?).  Do all other USB devices freeze as well when you plug in the MP3 
player?  And do you get any interesting output from dmesg regarding the 
error?

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-14  2:49 ` Mark Williamson
  2005-01-14 14:01   ` Nuutti Kotivuori
@ 2005-01-14 18:59   ` Adam Heath
  2005-01-14 19:02     ` Mark Williamson
  1 sibling, 1 reply; 15+ messages in thread
From: Adam Heath @ 2005-01-14 18:59 UTC (permalink / raw)
  To: Mark Williamson; +Cc: xen-devel@lists.sourceforge.net

On Fri, 14 Jan 2005, Mark Williamson wrote:

> All,
>
> I have updated the USB virtualisation patch at:
> http://www.cambridge.intel-research.net/~mwilli2/xen-2.0-testing-usb.patch
>
> Description:
> * Give guest operating systems under Xen access to USB devices plugged into
> the host.  Adds a backend driver (for domain 0), a frontend driver (for
> guests) and extra support code in the tools.
>
> Features:
> * USB mass storage devices are now fully working (new!)
> * USB isochronous transfers (larger buffers now supported, provided they are
> machine-contiguous)
>
> Still to do:
> * proper support for hot plugging of the physical devices
> * stronger checking for iso transfers in the backend
> * a little more work needed to make it work for all USB devices
> * iron out a few lingering backend bugs
>
> I'm intending to get this all done as soon as possible and then push into the
> -unstable tree.

Could it be possible to emulate a USB device in dom0 userspace?


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-14 18:59   ` Adam Heath
@ 2005-01-14 19:02     ` Mark Williamson
  0 siblings, 0 replies; 15+ messages in thread
From: Mark Williamson @ 2005-01-14 19:02 UTC (permalink / raw)
  To: Adam Heath; +Cc: Mark Williamson, xen-devel@lists.sourceforge.net

> Could it be possible to emulate a USB device in dom0 userspace?

Yes, that's doable in principle.  You could, for instance, connect the 
guest's USB frontend to a userspace process that implemented:
a) the Xen USBif protocol (as implemented by the current backend driver)
b) software emulation of one or more actual USB devices per domain

This should actually be simpler to achieve than implementing virtual USB 
devices for a whole-machine emulator like Bochs,  QEmu, VMWare, etc. because 
the paravirtualised interface is higher-level.

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-14 14:07     ` Mark Williamson
@ 2005-01-15 15:37       ` Nuutti Kotivuori
  2005-01-15 15:56         ` Jan Kundrát
  2005-01-15 16:23         ` Mark Williamson
  0 siblings, 2 replies; 15+ messages in thread
From: Nuutti Kotivuori @ 2005-01-15 15:37 UTC (permalink / raw)
  To: xen-devel

Mark Williamson wrote:
>> Woo, nice! I have an external MP3 player that is an USB mass
>> storage hard drive - if the drive happens to spin down when
>> plugging the cable
>
> Hmm, that's a nasty problem - I wonder what causes it.

I'd say it is just a trivial bug in the kernel USB stuff - expecting
some reply to always arrive, when it doesn't if the drive is unplugged
at the unfortunate moment (or the drive dies, as in this case).

>> in, it manages to freeze the USB subsystem in the kernel. Now I can
>> just put it in a separate virtual machine and reboot it :-)
>
> If it's freezing the USB subsystem (and nothing else), it implies a
> problem with the USB core and / or with the host controller driver.
> If it's the latter, you'd have to run the driver for your (hardware)
> host controller in the domain so that could be restarted too - the
> appropriate tool for that would be the existing PCI driver domains
> stuff.
>
> What kind of host controller do you have?  (or if you don't know, do
> you have USB 2.0?).  Do all other USB devices freeze as well when
> you plug in the MP3 player?  And do you get any interesting output
> from dmesg regarding the error?

Dmesg of boot:

ohci_hcd: 2004 Feb 02 USB 1.1 'Open' Host Controller (OHCI) Driver (PCI)
ACPI: PCI interrupt 0000:00:02.0[A] -> GSI 22 (level, high) -> IRQ 177
ohci_hcd 0000:00:02.0: nVidia Corporation nForce2 USB Controller
PCI: Setting latency timer of device 0000:00:02.0 to 64
ohci_hcd 0000:00:02.0: irq 177, pci mem f896e000
ohci_hcd 0000:00:02.0: new USB bus registered, assigned bus number 1
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 3 ports detected
ACPI: PCI interrupt 0000:00:02.1[B] -> GSI 21 (level, high) -> IRQ 185
ohci_hcd 0000:00:02.1: nVidia Corporation nForce2 USB Controller (#2)
PCI: Setting latency timer of device 0000:00:02.1 to 64
ohci_hcd 0000:00:02.1: irq 185, pci mem f8970000
ohci_hcd 0000:00:02.1: new USB bus registered, assigned bus number 2
hub 2-0:1.0: USB hub found
hub 2-0:1.0: 3 ports detected
ACPI: PCI interrupt 0000:00:02.2[C] -> GSI 20 (level, high) -> IRQ 193
ehci_hcd 0000:00:02.2: nVidia Corporation nForce2 USB Controller
PCI: Setting latency timer of device 0000:00:02.2 to 64
ehci_hcd 0000:00:02.2: irq 193, pci mem f8972000
ehci_hcd 0000:00:02.2: new USB bus registered, assigned bus number 3
PCI: cache line size of 64 is not supported by device 0000:00:02.2
ehci_hcd 0000:00:02.2: USB 2.0 enabled, EHCI 1.00, driver 2004-May-10
hub 3-0:1.0: USB hub found
hub 3-0:1.0: 6 ports detected
ieee1394: Initialized config rom entry `ip1394'
ohci1394: $Rev: 1223 $ Ben Collins <bcollins@debian.org>
ACPI: PCI interrupt 0000:00:0d.0[A] -> GSI 22 (level, high) -> IRQ 177
PCI: Setting latency timer of device 0000:00:0d.0 to 64
ohci1394: fw-host0: OHCI-1394 1.1 (PCI): IRQ=[177]  MMIO=[e2084000-e20847ff]  Max Packet=[2048]
ohci_hcd 0000:00:02.0: wakeup

Dmesg of successful plugging in of the drive:

usb 3-5: new high speed USB device using address 3
scsi0 : SCSI emulation for USB Mass Storage devices
  Vendor: FUJITSU   Model: MHR2040AT         Rev: 40BA
  Type:   Direct-Access                      ANSI SCSI revision: 02
SCSI device sda: 78140160 512-byte hdwr sectors (40008 MB)
sda: assuming drive cache: write through
 /dev/scsi/host0/bus0/target0/lun0: p1
Attached scsi disk sda at scsi0, channel 0, id 0, lun 0
USB Mass Storage device found at 3

Dmesg of an unsuccessful plugging in of the drive:

usb 3-5: new high speed USB device using address 5
scsi2 : SCSI emulation for USB Mass Storage devices

After that, nothing, no replugging or anything else helps. Only reboot.

-- Naked



-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-15 15:37       ` Nuutti Kotivuori
@ 2005-01-15 15:56         ` Jan Kundrát
  2005-01-15 16:00           ` Nuutti Kotivuori
  2005-01-15 16:23         ` Mark Williamson
  1 sibling, 1 reply; 15+ messages in thread
From: Jan Kundrát @ 2005-01-15 15:56 UTC (permalink / raw)
  To: Nuutti Kotivuori; +Cc: xen-devel

Nuutti Kotivuori wrote:
> After that, nothing, no replugging or anything else helps. Only reboot.

Neither removing usb modules?

-jkt

-- 
cd /local/pub && more beer > /dev/mouth



-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-15 15:56         ` Jan Kundrát
@ 2005-01-15 16:00           ` Nuutti Kotivuori
  0 siblings, 0 replies; 15+ messages in thread
From: Nuutti Kotivuori @ 2005-01-15 16:00 UTC (permalink / raw)
  To: Jan Kundrát; +Cc: xen-devel

Jan Kundrát wrote:
> Nuutti Kotivuori wrote:
>> After that, nothing, no replugging or anything else helps. Only
>> reboot.
>
> Neither removing usb modules?

Usb is compiled into the kernel at the moment. I remember that
removing usb-storage didn't help - removing all the usb modules
might've helped, or it might've been that I couldn't remove them
because they were in use.

-- Naked


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Re: [updated] Re: [PATCH] USB virtualisation (experimental)
  2005-01-15 15:37       ` Nuutti Kotivuori
  2005-01-15 15:56         ` Jan Kundrát
@ 2005-01-15 16:23         ` Mark Williamson
  1 sibling, 0 replies; 15+ messages in thread
From: Mark Williamson @ 2005-01-15 16:23 UTC (permalink / raw)
  To: xen-devel; +Cc: Nuutti Kotivuori

> I'd say it is just a trivial bug in the kernel USB stuff - expecting
> some reply to always arrive, when it doesn't if the drive is unplugged
> at the unfortunate moment (or the drive dies, as in this case).

I was just wondering if it was in the storage driver, the low level host 
controller driver or maybe the USB core code.  If all USB devices stop 
working, it suggests one of the latter two, otherwise it suggests the former 
and shouldn't even stop other USB storage devices from working.

> ohci_hcd: 2004 Feb 02 USB 1.1 'Open' Host Controller (OHCI) Driver (PCI)

I didn't think OHCI was very common on PCs but I guess

> Dmesg of an unsuccessful plugging in of the drive:
>
> usb 3-5: new high speed USB device using address 5
> scsi2 : SCSI emulation for USB Mass Storage devices

Yuk!

The USB storage code has a certain amount of statefulness, so if it's hanging 
for that drive once, it may stop it working for the rest of the uptime.  If 
the bug is in the USB storage code, you'll probably find that other storage 
devices still work, otherwise it may be somewhere else.

Cheers,
Mark


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2005-01-15 16:23 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-01-10 17:03 [PATCH] USB virtualisation (experimental) Mark Williamson
2005-01-11  0:24 ` Nivedita Singhvi
2005-01-11  3:21   ` Mark Williamson
2005-01-12 14:58     ` aq
2005-01-12 15:12       ` Mark Williamson
2005-01-14  1:38 ` [updated] " Mark A. Williamson
2005-01-14  2:49 ` Mark Williamson
2005-01-14 14:01   ` Nuutti Kotivuori
2005-01-14 14:07     ` Mark Williamson
2005-01-15 15:37       ` Nuutti Kotivuori
2005-01-15 15:56         ` Jan Kundrát
2005-01-15 16:00           ` Nuutti Kotivuori
2005-01-15 16:23         ` Mark Williamson
2005-01-14 18:59   ` Adam Heath
2005-01-14 19:02     ` Mark Williamson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.