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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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<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 +#include +#include +#include +#include + +/* + * 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 or + * 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 + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@xxxxxxxxxxx + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@xxxxxxxxx + * (C) Copyright 1999 Deti Fliegl, deti@xxxxxxxxx + * (C) Copyright 1999 Thomas Sailer, sailer@xxxxxxxxxxxxxx + * (C) Copyright 1999 Roman Weissgaerber, weissg@xxxxxxxxx + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@xxxxxxxxxxxxx). + * (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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif +#include + +#include +#include +#include + +#include "xhci.h" + +#include + +#include "../../../../../drivers/usb/hcd.h" + +#include "../usbif.h" +#include +#include + +/* + * 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 +#include +#include "../usbif.h" +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for UTS_SYSNAME */ + + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include "hcd.h" + +#include +#include +#include +#include + + +/*-------------------------------------------------------------------------*/ + +/* + * 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 +"""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 + +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('') + self.print_path(req) + #self.ls() + req.write('

%s

' % 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('

Console %s

' + % (cid, cid)) + req.write('

Connect to console

' + % cinfo.uri()) + if self.dom.config: + req.write("
")
+                PrettyPrint.prettyprint(self.dom.config, out=req)
+                req.write("
") + self.form(req) + req.write('') + return '' + + def form(self, req): + url = req.prePathURL() + req.write('
' % url) + req.write('') + req.write('') + req.write('
') + + req.write('
' % url) + req.write('') + req.write('Halt') + req.write('Reboot') + req.write('
') + + req.write('
' % url) + req.write('') + req.write('Poweroff') + req.write('Halt') + req.write('Reboot') + req.write('
') + + req.write('
' % url) + req.write('
') + req.write(' To file: ') + req.write('
') + + req.write('
' % url) + req.write('
') + req.write(' To host: ') + req.write('Live') + req.write('
') 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 +# Copyright (C) 2004 Intel Research Cambridge +# Copyright (C) 2004 Mark Williamson +"""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 '' % (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;