* [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