diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/Kconfig --- a/linux-2.6-xen-sparse/drivers/xen/Kconfig Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig Wed Mar 1 14:34:28 2006 @@ -132,6 +132,28 @@ a failure. The corresponding domain's channel will be closed. Say Y if you want this feature. +config XEN_USBDEV_BACKEND + bool "USB-device backend driver" + select USB + default y + help + The USB-device backend driver allows the kernel to export USB + devices to USB-device frontend drivers running in other domains. + This is not required for USB device access in domain 0 or any domain + given exclusive control over a USB host controller device at the PCI + level. + Say Y if you want to use this kernel to export a USB device to + another domain running a USB-device frontend driver. + +config XEN_USBDEV_BACKEND_TRACE + bool "USB-device backend driver tracing" + depends on XEN_USBDEV_BACKEND + default n + help + This option causes the driver to output a continual trace of its + activity. + Say N here unless you are trying to debug the driver. + config XEN_BLKDEV_FRONTEND tristate "Block-device frontend driver" depends on XEN @@ -168,6 +190,29 @@ select TCG_XEN help The TPM-device frontend driver. + +config XEN_USBDEV_FRONTEND + bool "USB-device frontend driver" + select USB + default y + help + The USB-device frontend driver allows the kernel to access USB + devices exported by a USB-device backend driver running in another + domain. + This is not required for USB device access in domain 0 or any domain + given exclusive control over a USB host controller device at the PCI + level. + Say Y if you want to use a USB-device backend driver to export a USB + device from another domain to the domain which will run this kernel. + +config XEN_USBDEV_FRONTEND_TRACE + bool "USB-device frontend driver tracing" + depends on XEN_USBDEV_FRONTEND + default n + help + This option causes the driver to output a continual trace of its + activity. + Say N here unless you are trying to debug the driver. config XEN_SCRUB_PAGES bool "Scrub memory before freeing it to Xen" diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Wed Mar 1 14:34:28 2006 @@ -13,10 +13,11 @@ obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/ obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/ obj-$(CONFIG_XEN_TPMDEV_BACKEND) += tpmback/ +obj-$(CONFIG_XEN_USBDEV_BACKEND) += usbback/ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/ obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/ obj-$(CONFIG_XEN_TPMDEV_FRONTEND) += tpmfront/ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += usbfront/ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ - diff -r a376bab39768 -r e187d61f5d77 tools/examples/Makefile --- a/tools/examples/Makefile Tue Feb 28 21:57:38 2006 +++ b/tools/examples/Makefile Wed Mar 1 14:34:28 2006 @@ -26,6 +26,7 @@ XEN_SCRIPTS += network-nat vif-nat XEN_SCRIPTS += block XEN_SCRIPTS += block-enbd block-nbd +XEN_SCRIPTS += usb XEN_SCRIPTS += vtpm vtpm-delete XEN_SCRIPTS += xen-hotplug-cleanup XEN_SCRIPT_DATA = xen-script-common.sh locking.sh logging.sh diff -r a376bab39768 -r e187d61f5d77 tools/examples/xen-backend.agent --- a/tools/examples/xen-backend.agent Tue Feb 28 21:57:38 2006 +++ b/tools/examples/xen-backend.agent Wed Mar 1 14:34:28 2006 @@ -3,6 +3,9 @@ PATH=/etc/xen/scripts:$PATH case "$XENBUS_TYPE" in + usb) + /etc/xen/scripts/usb "$ACTION" + ;; vbd) /etc/xen/scripts/block "$ACTION" ;; diff -r a376bab39768 -r e187d61f5d77 tools/examples/xen-backend.rules --- a/tools/examples/xen-backend.rules Tue Feb 28 21:57:38 2006 +++ b/tools/examples/xen-backend.rules Wed Mar 1 14:34:28 2006 @@ -2,4 +2,6 @@ SUBSYSTEM=="xen-backend", KERNEL=="vtpm*", RUN+="/etc/xen/scripts/vtpm $env{ACTION}" SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="online", RUN+="$env{script} online" SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="offline", RUN+="$env{script} offline" +SUBSYSTEM=="xen-backend", KERNEL=="usb*", RUN+="/etc/xen/scripts/usb $env{ACTION}" SUBSYSTEM=="xen-backend", ACTION=="remove", RUN+="/etc/xen/scripts/xen-hotplug-cleanup" + diff -r a376bab39768 -r e187d61f5d77 tools/examples/xmexample1 --- a/tools/examples/xmexample1 Tue Feb 28 21:57:38 2006 +++ b/tools/examples/xmexample1 Wed Mar 1 14:34:28 2006 @@ -56,6 +56,13 @@ # and MODE is r for read-only, w for read-write. disk = [ 'phy:hda1,hda1,w' ] + +#---------------------------------------------------------------------------- +# Define USB devices. +# +# Specify the USB device path as it appears in /sys/bus/usb/devices in the +# backend. This example exports device 3 on bus 1 and device 2 on bus 2. +#usb = ['path=1-3','path=2-2'] #---------------------------------------------------------------------------- # Define to which TPM instance the user domain should communicate. diff -r a376bab39768 -r e187d61f5d77 tools/python/xen/xend/server/usbif.py --- a/tools/python/xen/xend/server/usbif.py Tue Feb 28 21:57:38 2006 +++ b/tools/python/xen/xend/server/usbif.py Wed Mar 1 14:34:28 2006 @@ -22,8 +22,9 @@ """Support for virtual USB hubs. """ +from xen.xend import sxp + from xen.xend.server.DevController import DevController - class UsbifController(DevController): """USB device interface controller. Handles all USB devices @@ -35,8 +36,28 @@ """ DevController.__init__(self, vm) - - def getDeviceDetails(self, _): + def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - return (self.allocateDeviceID(), {}, {}) + path = sxp.child_value(config, 'path') + + devid = self.allocateDeviceID() + + back = { 'path' : path, + 'handle' : "%i" % devid } + + front = { 'handle' : "%i" % devid } + + return (devid, back, front) + + def configuration(self, devid): + """@see DevController.configuration""" + + result = DevController.configuration(self, devid) + + path = self.readBackend(devid,'path') + + if path: + result.append(['path', path]) + + return result diff -r a376bab39768 -r e187d61f5d77 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Tue Feb 28 21:57:38 2006 +++ b/tools/python/xen/xm/create.py Wed Mar 1 14:34:28 2006 @@ -237,6 +237,10 @@ fn=set_bool, default=0, use="Make the domain a network interface backend.") +gopts.var('usbif', val='no|yes', + fn=set_bool, default=0, + use="Make the domain a USB device backend.") + gopts.var('tpmif', val='no|yes', fn=append_value, default=0, use="Make the domain a TPM interface backend.") @@ -261,10 +265,13 @@ For example '-ioports 02f8-02ff'. The option may be repeated to add more than one i/o range.""") -gopts.var('usb', val='PATH', +gopts.var('usb', val="path=PATH,backend=DOM", 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.""") + use="""Map a backend USB port (specified by the backend PATH in the + backend domain DOM) to a single-port virtual host controller device + in the domain. + If backend is not specified the default backend driver domain is + used. This option may be repeated to add more than one port.""") gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME", fn=append_value, default=[], @@ -489,8 +496,13 @@ config_devs.append(['device', config_ioports]) def configure_usb(config_devs, vals): - for path in vals.usb: - config_usb = ['usb', ['path', path]] + for d in vals.usb: + path = d.get('path') + backend = d.get('backend') + config_usb = ['usb'] + config_usb.append(['path', path]) + if backend: + config_usb.append(['backend', backend]) config_devs.append(['device', config_usb]) def configure_vtpm(config_devs, vals): @@ -601,6 +613,8 @@ config.append(['backend', ['blkif']]) if vals.netif: config.append(['backend', ['netif']]) + if vals.usbif: + config.append(['backend', ['usbif']]) if vals.tpmif: config.append(['backend', ['tpmif']]) @@ -670,6 +684,22 @@ ioports.append(hexd) vals.ioports = ioports +def preprocess_usb(vals): + if not vals.usb: return + usb = [] + for port in vals.usb: + d = {} + a = port.split(',') + for b in a: + (k, v) = b.strip().split('=', 1) + k = k.strip() + v = v.strip() + if k not in ['path', 'backend']: + err('Invalid usb port specifier: ' + port) + d[k] = v + usb.append(d) + vals.usb = usb + def preprocess_vtpm(vals): if not vals.vtpm: return vtpms = [] @@ -773,6 +803,7 @@ preprocess_ioports(vals) preprocess_ip(vals) preprocess_nfs(vals) + preprocess_usb(vals) preprocess_vnc(vals) preprocess_vtpm(vals) diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/Makefile --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/Makefile Wed Mar 1 14:34:28 2006 @@ -0,0 +1,11 @@ +obj-y = \ +usbback_buffer_resource_provider.o \ +usbback_callback.o \ +usbback_device.o \ +usbback_gw.o \ +usbback_gw_resource.o \ +usbback_rbr_mapper_pool.o \ +usbback_resource.o \ +usbback_ring_channel.o \ +usbback_work.o \ +usbback_xb_channel.o diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_buffer_resource_provider.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_buffer_resource_provider.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,196 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/blkback/blkback.c */ +/* */ +/* original copyright notice follows... */ +/*****************************************************************************/ +/****************************************************************************** + * arch/xen/drivers/blkif/backend/main.c + * + * Back-end of the driver for virtual block devices. This portion of the + * driver exports a 'unified' block-device interface that can be accessed + * by any operating system that implements a compatible front end. A + * reference front-end implementation can be found in: + * arch/xen/drivers/blkif/frontend + * + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + * Copyright (c) 2005, Christopher Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbback_buffer_resource_provider.h" +#include "usbback_trace.h" + +struct usbback_buffer_rsrc_provider { + spinlock_t lock; + struct usbback_buffer_rsrc_list rsrcs; + struct usbback_buffer_rsrc_list free_rsrcs; + struct page *page; + unsigned long mmap_vstart; + struct list_head page_range_list; + struct list_head *page_range_link; +}; + +static int +alloc_or_free_page_ranges(struct usbback_buffer_rsrc_provider *provider, +int free) +{ + int return_value = 0; + int i; + if (provider->rsrcs.page_ranges == 0) + return 0; + if (free) + goto exit_path; + provider->page = balloon_alloc_empty_page_range( + provider->rsrcs.page_ranges * + provider->rsrcs.page_range_page_count); + if (provider->page == NULL) { + return_value = -ENOMEM; + goto exit_no_page_range; + } + provider->mmap_vstart = (unsigned long)pfn_to_kaddr(page_to_pfn( + provider->page)); + INIT_LIST_HEAD(&provider->page_range_list); + provider->page_range_link = kmalloc(sizeof(struct list_head) * + provider->rsrcs.page_ranges, GFP_KERNEL); + if (provider->page_range_link == NULL) { + return_value = -ENOMEM; + goto exit_no_page_range_link; + } + for (i = 0; i < provider->rsrcs.page_ranges; i++) { + struct list_head *link = &provider->page_range_link[i]; + INIT_LIST_HEAD(link); + list_add(link, &provider->page_range_list); + } + provider->free_rsrcs.page_ranges = provider->rsrcs.page_ranges; + provider->free_rsrcs.page_range_page_count = + provider->rsrcs.page_range_page_count; + return 0; + exit_path: + kfree(provider->page_range_link); + exit_no_page_range_link: + balloon_dealloc_empty_page_range(provider->page, + provider->rsrcs.page_ranges * + provider->rsrcs.page_range_page_count); + exit_no_page_range: + return return_value; +} + +static int +usbback_buffer_rsrc_provider_init_or_exit( +struct usbback_buffer_rsrc_provider *provider, int exit) +{ + int return_value = 0; + trace_info("%p", provider); + if (exit) + goto exit_path; + spin_lock_init(&provider->lock); + provider->free_rsrcs = usbback_buffer_rsrc_list_null(); + if( ( return_value = alloc_or_free_page_ranges(provider, 0) ) != 0 ) + goto exit_no_page_ranges; + return 0; + exit_path: + alloc_or_free_page_ranges(provider, 1); + exit_no_page_ranges: + return return_value; +} + +struct usbback_buffer_rsrc_provider * +usbback_allocate_buffer_rsrc_provider(struct usbback_buffer_rsrc_list rsrcs) +{ + struct usbback_buffer_rsrc_provider *provider; + trace(); + provider = kmalloc(sizeof(struct usbback_buffer_rsrc_provider), + GFP_KERNEL); + if (provider != NULL) { + provider->rsrcs = rsrcs; + if (usbback_buffer_rsrc_provider_init_or_exit(provider, 0) + != 0) { + kfree(provider); + provider = NULL; + } + } + return provider; +} + +void usbback_free_buffer_rsrc_provider( +struct usbback_buffer_rsrc_provider *provider) +{ + trace(); + (void)usbback_buffer_rsrc_provider_init_or_exit(provider, 1); + kfree(provider); +} + +struct usbback_buffer_rsrc_list +usbback_buffer_rsrc_provider_query_free_rsrcs( +struct usbback_buffer_rsrc_provider *provider) +{ + struct usbback_buffer_rsrc_list list; + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + list = provider->free_rsrcs; + spin_unlock_irqrestore(&provider->lock, flags); + return list; +} + +unsigned long +usbback_buffer_rsrc_provider_allocate_page_range( +struct usbback_buffer_rsrc_provider *provider, unsigned long page_count) +{ + unsigned long page_range; + unsigned long flags; + struct list_head *link; + trace(); + spin_lock_irqsave(&provider->lock, flags); + link = provider->page_range_list.next; + list_del_init(link); + provider->free_rsrcs.page_ranges--; + page_range = (provider->mmap_vstart + + (PAGE_SIZE * provider->rsrcs.page_range_page_count * + (((unsigned long)link - + (unsigned long)provider->page_range_link) / + sizeof(struct list_head)))); + spin_unlock_irqrestore(&provider->lock, flags); + return page_range; +} + +void usbback_buffer_rsrc_provider_free_page_range( +struct usbback_buffer_rsrc_provider *provider, unsigned long page_range) +{ + int i; + unsigned long flags; + trace(); + i = ((page_range - provider->mmap_vstart) / (PAGE_SIZE * + provider->rsrcs.page_range_page_count)); + spin_lock_irqsave(&provider->lock, flags); + list_add(&provider->page_range_link[i], &provider->page_range_list); + provider->free_rsrcs.page_ranges++; + spin_unlock_irqrestore(&provider->lock, flags); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_buffer_resource_provider.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_buffer_resource_provider.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,77 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_BUFFER_RESOURCE_PROVIDER_H +#define USBBACK_BUFFER_RESOURCE_PROVIDER_H + +#include +#include +#include + +struct usbback_buffer_rsrc_list { + u32 page_ranges; + u32 page_range_page_count; +}; + +static inline struct usbback_buffer_rsrc_list +usbback_buffer_rsrc_list_null(void) +{ + struct usbback_buffer_rsrc_list list; + memset(&list, 0, sizeof(list)); + return list; +} + +static inline int +usbback_buffer_rsrc_list_subset_of(struct usbback_buffer_rsrc_list *a, +struct usbback_buffer_rsrc_list *b) +{ + return ((a->page_ranges <= b->page_ranges) && + (a->page_range_page_count <= b->page_range_page_count)); +} + +static inline void +usbback_buffer_rsrc_list_plus_equals(struct usbback_buffer_rsrc_list *a, +struct usbback_buffer_rsrc_list *b) +{ + a->page_ranges += b->page_ranges; + if (b->page_range_page_count > a->page_range_page_count) + a->page_range_page_count = b->page_range_page_count; +} + +extern struct usbback_buffer_rsrc_provider * +usbback_allocate_buffer_rsrc_provider( +struct usbback_buffer_rsrc_list rsrc_allocation); + +extern void +usbback_free_buffer_rsrc_provider( +struct usbback_buffer_rsrc_provider *provider); + +extern struct usbback_buffer_rsrc_list +usbback_buffer_rsrc_provider_query_free_rsrcs( +struct usbback_buffer_rsrc_provider *provider); + +extern unsigned long +usbback_buffer_rsrc_provider_allocate_page_range( +struct usbback_buffer_rsrc_provider *provider, unsigned long page_count); + +extern void +usbback_buffer_rsrc_provider_free_page_range( +struct usbback_buffer_rsrc_provider *provider, unsigned long page_range); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_callback.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_callback.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,41 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbback_callback.h" + +void usbback_callback_serialiser_function(void *context) +{ + struct usbback_callback_serialiser *serialiser = + (struct usbback_callback_serialiser *)context; + unsigned long flags; + + spin_lock_irqsave(&serialiser->lock, flags); + while (!list_empty(&serialiser->list) && !serialiser->running) { + struct usbback_callback *callback = + usbback_callback_link_to(serialiser->list.next); + list_del_init(usbback_callback_to_link(callback)); + serialiser->running = 1; + spin_unlock_irqrestore(&serialiser->lock, flags); + usbback_callback_complete_synchronously(callback); + spin_lock_irqsave(&serialiser->lock, flags); + serialiser->running = 0; + } + spin_unlock_irqrestore(&serialiser->lock, flags); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_callback.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_callback.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,146 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_CALLBACK_H +#define USBBACK_CALLBACK_H + +#include +#include +#include "usbback_work.h" + +static inline usbif_error usbif_error_map_local_to(int error) +{ + switch (error) { + case 0: + return USBIF_ERROR_SUCCESS; + case -ENOTCONN: + return USBIF_ERROR_DISCONNECT; + case -EPROTO: + return USBIF_ERROR_INVALID_PROTOCOL; + case -EINVAL: + return USBIF_ERROR_INVALID_PARAMETER; + case -E2BIG: + return USBIF_ERROR_TOO_BIG; + case -ENODEV: + return USBIF_ERROR_NO_DEVICE; + case -ECONNRESET: + return USBIF_ERROR_UNLINKED; + case -EPIPE: + return USBIF_ERROR_PIPE; + default: + return (usbif_error)error; + } +} + +struct usbback_callback { + struct usbback_work work; + usbif_error error; +}; + +typedef void usbback_callback_function(struct usbback_callback *callback); + +static inline void +usbback_callback_init(struct usbback_callback *callback, +usbback_callback_function *function) +{ + usbback_work_init(&callback->work, (void (*)(void *))function, + callback); + callback->error = USBIF_ERROR_SUCCESS; +} + +static inline struct list_head * +usbback_callback_to_link(struct usbback_callback *callback) +{ + return usbback_work_to_link(&callback->work); +} + +static inline struct usbback_callback * +usbback_callback_link_to(struct list_head *link) +{ + return container_of(usbback_work_link_to(link), + struct usbback_callback, work); +} + +static inline void +usbback_callback_complete(struct usbback_callback *callback, usbif_error error) +{ + callback->error = error; + usbback_work_schedule(&callback->work); +} + +static inline void +usbback_callback_success(struct usbback_callback *callback) +{ + usbback_callback_complete(callback, 0); +} + +static inline void +usbback_callback_set_error(struct usbback_callback *callback, +usbif_error error) +{ + callback->error = error; +} + +static inline void +usbback_callback_complete_synchronously(struct usbback_callback *callback) +{ + usbback_work_perform_synchronously(&callback->work); +} + +static inline usbif_error +usbback_callback_query_error(struct usbback_callback *callback) +{ + return callback->error; +} + +struct usbback_callback_serialiser { + spinlock_t lock; + struct list_head list; + struct usbback_work work; + int running; +}; + +void usbback_callback_serialiser_function(void *context); + +#define USBBACK_CALLBACK_SERIALISER_INIT( name ) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .list = LIST_HEAD_INIT( name.list ), \ + .work = USBBACK_WORK_INIT \ + ( name.work, usbback_callback_serialiser_function, &name ),\ + .running = 0 \ +} + +#define USBBACK_CALLBACK_SERIALISER( name ) \ +struct usbback_callback_serialiser name = \ +USBBACK_CALLBACK_SERIALISER_INIT( name ) + +static inline void +usbback_callback_serialiser_complete_callback( +struct usbback_callback_serialiser *serialiser, +struct usbback_callback *callback, usbif_error error) +{ + unsigned long flags; + usbback_callback_set_error(callback, error); + spin_lock_irqsave(&serialiser->lock, flags); + list_add_tail(usbback_callback_to_link(callback), &serialiser->list); + spin_unlock_irqrestore(&serialiser->lock, flags); + usbback_work_schedule(&serialiser->work); +} + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,159 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_CHANNEL_H +#define USBBACK_CHANNEL_H + +#include +#include "usbback_callback.h" + +struct usbback_channel_ibmsg { + struct usbback_callback callback; + struct usbif_request request; +}; + +static inline struct list_head * +usbback_channel_ibmsg_to_link(struct usbback_channel_ibmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct usbback_callback * +usbback_channel_ibmsg_to_callback(struct usbback_channel_ibmsg *message) +{ + return &message->callback; +} + +static inline struct usbback_channel_ibmsg * +usbback_channel_ibmsg_callback_to(struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_channel_ibmsg, callback); +} + +static inline void +usbback_channel_ibmsg_init(struct usbback_channel_ibmsg *message, +usbback_callback_function *callback) +{ + usbback_callback_init(usbback_channel_ibmsg_to_callback(message), + callback); +} + +struct usbback_channel_obmsg { + struct usbback_callback callback; + struct usbif_response response; +}; + +static inline struct list_head * +usbback_channel_obmsg_to_link(struct usbback_channel_obmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct usbback_callback * +usbback_channel_obmsg_to_callback(struct usbback_channel_obmsg *message) +{ + return &message->callback; +} + +static inline struct usbback_channel_obmsg * +usbback_channel_obmsg_callback_to(struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_channel_obmsg, callback); +} + +static inline void +usbback_channel_obmsg_init(struct usbback_channel_obmsg *message, +usbback_callback_function *callback) +{ + usbback_callback_init(usbback_channel_obmsg_to_callback(message), + callback); +} + +struct usbback_channel; + +typedef void +usbback_channel_submit_message_function(struct usbback_channel *channel, +struct usbback_channel_obmsg *message); + +typedef void +usbback_channel_connect_function(void *client_context); + +typedef void +usbback_channel_handle_message_function(void *client_context, +struct usbback_channel_ibmsg *message); + +typedef void +usbback_channel_disconnect_function(void *client_context, +struct usbback_callback *callback); + +struct usbback_channel { + usbback_channel_submit_message_function *submit_message; + void *client_context; + usbback_channel_connect_function *connect; + usbback_channel_handle_message_function *handle_message; + usbback_channel_disconnect_function *disconnect; +}; + +static inline void +usbback_channel_init(struct usbback_channel *channel, +usbback_channel_submit_message_function *submit_message ) +{ + channel->submit_message = submit_message; +} + +static inline void usbback_channel_connect(struct usbback_channel *channel) +{ + channel->connect(channel->client_context); +} + +static inline void +usbback_channel_handle_message(struct usbback_channel *channel, +struct usbback_channel_ibmsg *message) +{ + channel->handle_message(channel->client_context, message); +} + +static inline void +usbback_channel_disconnect(struct usbback_channel *channel, +struct usbback_callback *callback) +{ + channel->disconnect(channel->client_context, callback); +} + +static inline void +usbback_channel_install_client(struct usbback_channel *channel, +void *client_context, usbback_channel_connect_function *connect, +usbback_channel_handle_message_function *handle_message, +usbback_channel_disconnect_function *disconnect) +{ + channel->client_context = client_context; + channel->connect = connect; + channel->handle_message = handle_message; + channel->disconnect = disconnect; +} + +static inline void +usbback_channel_submit_message(struct usbback_channel *channel, +struct usbback_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + channel->submit_message(channel, message); +} + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,839 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/usbif/backend/control.c */ +/* arch/xen/drivers/usbif/backend/interface.c */ +/* arch/xen/drivers/usbif/backend/main.c */ +/* blkback/xenbus.c */ +/* */ +/* original copyright notices follow... */ +/*****************************************************************************/ + +/****************************************************************************** + * arch/xen/drivers/usbif/backend/control.c + * + * Routines for interfacing with the control plane. + * + * Copyright (c) 2004, Keir Fraser + */ + +/****************************************************************************** + * 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 + */ + +/****************************************************************************** + * 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 + * Copyright (c) 2004, 2005 Mark Williamson + * + * Based on arch/xen/drivers/blkif/backend/main.c + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + */ + +/* Xenbus code for blkif backend + Copyright (C) 2005 Rusty Russell + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include "usbback_device.h" +#include "usbback_trace.h" +#include "usbback_xb_channel.h" + +static struct usb_driver usbback_device_usb_driver; + +static DECLARE_RWSEM(usbback_device_list_sem); +static LIST_HEAD(usbback_device_list); + +typedef enum { + usbback_device_state_i, + usbback_device_state_i_up, + usbback_device_state_i_cn, + usbback_device_state_i_up_cn, + usbback_device_state_i_up_cn_ud, + usbback_device_state_i_up_cn_dn, + usbback_device_state_i_up_cn_ud_dn +} usbback_device_state; + +typedef enum { + usbback_device_stimulus_up, /* USB probe */ + usbback_device_stimulus_ud, /* USB disconnect */ + usbback_device_stimulus_cn, /* Gateway connect */ + usbback_device_stimulus_dn, /* Gateway disconnect */ + usbback_device_stimulus_rq, /* Tra queued/rsrc freed */ + usbback_device_stimulus_ri /* test_io rsrcs idle (reent) */ +} usbback_device_stimulus; + +struct usbback_device { + struct xenbus_device *dev; + struct list_head link; + struct usb_device *usbdev; + spinlock_t lock; + usbback_device_state state; + int usbdev_is_connected; + int enabled; + struct list_head tra_list; + struct list_head free_rsrc_list; + unsigned long rsrcs_out; + struct usbback_rsrc rsrcs[USBIF_QUOTA]; + struct usbback_callback *gw_disconnect_callback; + int usb_disconnect; + char *path; + struct usbback_xb_channel channel; + struct usbback_gw gw; +}; + +static void +usbback_device_handle_stimulus(struct usbback_device *device, +usbback_device_stimulus stimulus); + +domid_t usbback_device_query_remote_domain(struct usbback_device *device) +{ + trace(); + return device->dev->otherend_id; +} + +struct usb_device *usbback_device_query_usbdev(struct usbback_device *device) +{ + trace(); + return device->usbdev; +} + +static inline struct usbback_device * +usbback_device_gw_to(struct usbback_gw *gw) +{ + return container_of(gw, struct usbback_device, gw); +} + +static int +usbback_device_probe_usb_device(struct usb_interface *intf, +const struct usb_device_id *id) +{ + int return_value = 0, i; + struct usb_device *usbdev = interface_to_usbdev(intf); + struct usbback_device *device = NULL, *cur = NULL; + trace(); + dev_info(&usbdev->dev, "probe for %s\n", usbdev->dev.bus_id); + down_read(&usbback_device_list_sem); + list_for_each_entry(cur, &usbback_device_list, link) { + dev_info(&usbdev->dev, "testing path %s\n", cur->path ); + if (strcmp(cur->path, usbdev->dev.bus_id) == 0) { + dev_info(&usbdev->dev, "match found\n" ); + device = cur; + break; + } else { + dev_info(&usbdev->dev, "does not match\n" ); + } + } + if (device == NULL) { + dev_info(&usbdev->dev, "no match found\n" ); + return_value = -ENODEV; + goto exit_no_device; + } + for (i = 0; i < usbdev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *other_intf = usb_ifnum_to_if(usbdev, i); + if (other_intf != intf) { + if (usb_interface_claimed(other_intf)) { + dev_info(&usbdev->dev, + "an interface of the matching " + "device is already in use by " + "another driver\n"); + return_value = -EBUSY; + goto exit_interface_already_claimed; + } + } + } + for (i = 0; i < usbdev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *other_intf = usb_ifnum_to_if(usbdev, i); + if (other_intf != intf) { + /* We already checked the interfaces were available. */ + (void)usb_driver_claim_interface( + &usbback_device_usb_driver, + other_intf, device); + } + } + usbdev = usb_get_dev(usbdev); + usb_set_intfdata(intf, device); + spin_lock_irq(&device->lock); + device->usbdev_is_connected = 1; + device->usbdev = usbdev; + usbback_device_handle_stimulus(device, usbback_device_stimulus_up); + spin_unlock_irq(&device->lock); + exit_interface_already_claimed: + exit_no_device: + up_read(&usbback_device_list_sem); + return return_value; +} + +static void usbback_device_disconnect_usb_device(struct usb_interface *intf) +{ + struct usbback_device *device = usb_get_intfdata(intf); + trace(); + /* Protect against reentrant call when we release other interfaces. */ + if (device != NULL) { + struct usb_device *usbdev; + int i; + spin_lock_irq(&device->lock); + device->usbdev_is_connected = 0; + device->enabled = 0; + device->usb_disconnect = 0; + usbback_device_handle_stimulus(device, + usbback_device_stimulus_ud); + spin_unlock_irq(&device->lock); + usbback_work_until(device->usb_disconnect); + usbdev = device->usbdev; + for (i = 0; i < usbdev->actconfig->desc.bNumInterfaces; i++) { + struct usb_interface *other_intf = usb_ifnum_to_if( + usbdev, i); + if (other_intf != intf) { + /* Protect against reentrant call when we */ + /* release other interfaces. */ + usb_set_intfdata(other_intf, NULL); + usb_driver_release_interface( + &usbback_device_usb_driver, + other_intf); + } + } + usb_set_intfdata(intf, NULL); + usb_put_dev(usbdev); + } +} + +static void usbback_device_gw_connect(struct usbback_gw *gw) +{ + struct usbback_device *device = usbback_device_gw_to(gw); + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + usbback_device_handle_stimulus(device, usbback_device_stimulus_cn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +usbback_device_handle_probe(struct usbback_device *device, +struct usbback_gw_tra *tra) +{ + tra->status.probe.result = device->usbdev_is_connected ? + USBIF_PROBE_RESULT_DEVICE_PRESENT : + USBIF_PROBE_RESULT_NO_DEVICE; + usbback_gw_tra_complete(tra, USBIF_ERROR_SUCCESS); +} + +int usbback_device_reset(struct usbback_device *device) +{ + int status = -1; + trace(); + spin_lock_irq(&device->lock); + if (device->usbdev_is_connected) { + if (device->usbdev->speed == USB_SPEED_LOW) { + status = USBIF_RESET_RESULT_LOW_SPEED; + } else if (device->usbdev->speed == USB_SPEED_HIGH) { + status = USBIF_RESET_RESULT_HIGH_SPEED; + } else { + status = USBIF_RESET_RESULT_FULL_SPEED; + } + device->enabled = 1; + } else { + device->enabled = 0; + } + spin_unlock_irq(&device->lock); + return status; +} + +static void +usbback_device_handle_reset(struct usbback_device *device, +struct usbback_gw_tra *tra) +{ + usbif_error error = USBIF_ERROR_SUCCESS; + int result; + trace(); + result = usbback_device_reset(device); + if (result < 0) { + error = USBIF_ERROR_NO_DEVICE; + goto complete; + } + tra->status.reset.result = result; + complete: + usbback_gw_tra_complete(tra, error); +} + +static void +usbback_device_handle_io(struct usbback_device *device, +struct usbback_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + list_add_tail(usbback_gw_tra_to_link(tra), &device->tra_list); + usbback_device_handle_stimulus(device, usbback_device_stimulus_rq); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +usbback_device_handle_unlink(struct usbback_device *device, +struct usbback_gw_tra *unlink_tra) +{ + uint32_t unlink_id = unlink_tra->parameters.unlink.unlink_id; + struct usbback_gw_tra *tra = NULL, *temp; + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + list_for_each_entry(temp, &device->tra_list, callback.work.link) { + if (temp->parameters.io.header.unlink_id == unlink_id) { + tra = temp; + list_del_init(usbback_gw_tra_to_link(tra)); + break; + } + } + if (tra == NULL) { + int i; + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbback_rsrc *rsrc = &device->rsrcs[i]; + if (usbback_rsrc_try_unlink(rsrc, unlink_id)) + break; + } + } + spin_unlock_irqrestore(&device->lock, flags); + if (tra) + usbback_gw_tra_complete(tra, USBIF_ERROR_UNLINKED); + usbback_gw_tra_complete(unlink_tra, USBIF_ERROR_SUCCESS); +} + +static void +usbback_device_gw_tra(struct usbback_gw *gw, struct usbback_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_device *device = usbback_device_gw_to(gw); + switch (tra->parameters.header.tra_type) { + case USBIF_TRA_TYPE_PROBE: + usbback_device_handle_probe(device, tra); + break; + case USBIF_TRA_TYPE_RESET: + usbback_device_handle_reset(device, tra); + break; + case USBIF_TRA_TYPE_IO: + usbback_device_handle_io(device, tra); + break; + case USBIF_TRA_TYPE_UNLINK: + usbback_device_handle_unlink(device, tra); + break; + default: + usbback_gw_tra_complete(tra, USBIF_ERROR_INVALID_PARAMETER); + break; + } +} + +static void +usbback_device_gw_disconnect(struct usbback_gw *gw, +struct usbback_callback *callback) +{ + struct usbback_device *device = usbback_device_gw_to(gw); + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->gw_disconnect_callback = callback; + usbback_device_handle_stimulus(device, usbback_device_stimulus_dn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +usbback_device_invalid_stimulus(struct usbback_device *device, +usbback_device_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbback: device %p in state %d" + "received invalid stimulus %d", device, device->state, stimulus); +} + +static void usbback_device_purge_io(struct usbback_device *device) +{ + int i; + trace(); + while (!list_empty(&device->tra_list)) { + struct usbback_gw_tra *tra = list_entry(device->tra_list.next, + struct usbback_gw_tra, callback.work.link); + list_del_init(usbback_gw_tra_to_link(tra)); + usbback_gw_tra_complete(tra, USBIF_ERROR_UNLINKED); + } + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbback_rsrc *rsrc = &device->rsrcs[i]; + usbback_rsrc_flush_tra(rsrc); + } +} + +static void usbback_device_test_io(struct usbback_device *device) +{ + trace(); + if (device->rsrcs_out == 0) { + usbback_device_handle_stimulus(device, + usbback_device_stimulus_ri); + } +} + +static void usbback_device_kick_io(struct usbback_device *device) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + while (!list_empty(&device->tra_list) && + !list_empty(&device->free_rsrc_list)) { + struct usbback_gw_tra *tra = list_entry(device->tra_list.next, + struct usbback_gw_tra, callback.work.link); + struct usbback_rsrc *rsrc = list_entry( + device->free_rsrc_list.next, + struct usbback_rsrc, link); + list_del_init(usbback_gw_tra_to_link(tra)); + list_del_init(&rsrc->link); + device->rsrcs_out++; + usbback_rsrc_start_io(rsrc, tra); + } +} + +void usbback_device_rsrc_completed_io(struct usbback_device *device, +struct usbback_rsrc *rsrc) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + list_add(&rsrc->link, &device->free_rsrc_list); + device->rsrcs_out--; + usbback_device_handle_stimulus(device, usbback_device_stimulus_rq); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +usbback_device_complete_usb_disconnect(struct usbback_device *device) +{ + trace(); + device->usb_disconnect = 1; + usbback_work_wake_up(); +} + +static void +usbback_device_complete_gw_disconnect(struct usbback_device *device) +{ + trace(); + usbback_callback_success(device->gw_disconnect_callback); +} + +static void +usbback_device_handle_stimulus(struct usbback_device *device, +usbback_device_stimulus stimulus) +{ + trace_info("device %p in state %d received stimulus %d", device, + device->state, stimulus); + switch (device->state) { + case usbback_device_state_i: + /* USB disconnected */ + /* Gateway disconnected */ + /* I/Os idle */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case usbback_device_stimulus_up: + device->state = usbback_device_state_i_up; + break; + case usbback_device_stimulus_cn: + device->state = usbback_device_state_i_cn; + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_up: + /* USB probed */ + /* Gateway disconnected */ + /* I/Os idle */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case usbback_device_stimulus_ud: + device->state = usbback_device_state_i; + usbback_device_complete_usb_disconnect(device); + break; + case usbback_device_stimulus_cn: + device->state = usbback_device_state_i_up_cn; + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_cn: + /* USB disconnected */ + /* Gateway connected */ + /* I/Os idle */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case usbback_device_stimulus_up: + device->state = usbback_device_state_i_up_cn; + break; + case usbback_device_stimulus_dn: + device->state = usbback_device_state_i; + usbback_device_complete_gw_disconnect(device); + break; + case usbback_device_stimulus_rq: + usbback_device_purge_io(device); + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_up_cn: + /* USB probed */ + /* Gateway connected */ + /* Maybe I/Os in progress */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case usbback_device_stimulus_ud: + device->state = usbback_device_state_i_up_cn_ud; + usbback_device_purge_io(device); + usbback_device_test_io(device); + break; + case usbback_device_stimulus_dn: + device->state = usbback_device_state_i_up_cn_dn; + usbback_device_purge_io(device); + usbback_device_test_io(device); + break; + case usbback_device_stimulus_rq: + usbback_device_kick_io(device); + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_up_cn_ud: + /* USB disconnecting */ + /* Gateway connected */ + /* I/Os in progress/testing I/Os */ + /* USB disconnect outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case usbback_device_stimulus_dn: + device->state = usbback_device_state_i_up_cn_ud_dn; + break; + case usbback_device_stimulus_rq: + usbback_device_purge_io(device); + usbback_device_test_io(device); + break; + case usbback_device_stimulus_ri: + device->state = usbback_device_state_i_cn; + usbback_device_complete_usb_disconnect(device); + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_up_cn_dn: + /* USB probed */ + /* Gateway disconnecting */ + /* I/Os in progress/testing I/Os */ + /* USB disconnect not outstanding */ + /* Gateway disconnect outstanding */ + switch (stimulus) { + case usbback_device_stimulus_ud: + device->state = usbback_device_state_i_up_cn_ud_dn; + break; + case usbback_device_stimulus_rq: + usbback_device_purge_io(device); + usbback_device_test_io(device); + break; + case usbback_device_stimulus_ri: + device->state = usbback_device_state_i_up; + usbback_device_complete_gw_disconnect(device); + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbback_device_state_i_up_cn_ud_dn: + /* USB disconnecting */ + /* Gateway disconnecting */ + /* I/Os in progress/testing I/Os */ + /* USB disconnect outstanding */ + /* Gateway disconnect outstanding */ + switch (stimulus) { + case usbback_device_stimulus_rq: + usbback_device_purge_io(device); + usbback_device_test_io(device); + break; + case usbback_device_stimulus_ri: + device->state = usbback_device_state_i; + usbback_device_complete_usb_disconnect(device); + usbback_device_complete_gw_disconnect(device); + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } + break; + default: + usbback_device_invalid_stimulus(device, stimulus); + break; + } +} + +static int +usbback_device_init_or_exit(struct usbback_device *device, +struct xenbus_device *dev, int exit) +{ + xenbus_transaction_t tra; + int return_value = 0, i; + trace(); + if (exit) + goto exit_path; + device->dev = dev; + INIT_LIST_HEAD(&device->link); + spin_lock_init(&device->lock); + device->state = usbback_device_state_i; + INIT_LIST_HEAD(&device->tra_list); + INIT_LIST_HEAD(&device->free_rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbback_rsrc *rsrc = &device->rsrcs[i]; + return_value = usbback_rsrc_init(rsrc, device); + if (return_value != 0) + goto exit_no_rsrc; + list_add(&rsrc->link, &device->free_rsrc_list); + } + return_value = usbback_xb_channel_init(&device->channel); + if (return_value != 0) + goto exit_no_channel; + return_value = usbback_gw_init(&device->gw, + usbback_xb_channel_to_channel(&device->channel), + usbback_device_gw_connect, + usbback_device_gw_tra, + usbback_device_gw_disconnect); + if (return_value != 0) + goto exit_no_gw; + return_value = xenbus_transaction_start(&tra); + if (return_value != 0) { + trace_info("Error starting tra."); + goto exit_no_tra; + } + return_value = xenbus_gather(tra, dev->nodename, "path", NULL, + &device->path, NULL); + (void)xenbus_transaction_end(tra, 1); + if (return_value < 0) { + trace_info("Failed to gather configuration parameters."); + goto exit_no_path; + } + trace_info("path:%s", device->path); + down_write(&usbback_device_list_sem); + list_add_tail(&device->link, &usbback_device_list); + up_write(&usbback_device_list_sem); + bus_rescan_devices(&usb_bus_type); + usbback_xb_channel_connect(&device->channel); + return 0; + exit_path: + usbback_xb_channel_disconnect(&device->channel); + down_write(&usbback_device_list_sem); + list_del_init(&device->link); + up_write(&usbback_device_list_sem); + spin_lock_irq(&device->lock); + if (device->usbdev_is_connected) { + usb_get_dev(device->usbdev); + spin_unlock_irq(&device->lock); + usb_lock_device(device->usbdev); + down_write(&usb_bus_type.subsys.rwsem); + if (device->usbdev_is_connected) + usb_driver_release_interface( + &usbback_device_usb_driver, + usb_ifnum_to_if(device->usbdev, 0)); + up_write(&usb_bus_type.subsys.rwsem); + usb_unlock_device(device->usbdev); + usb_put_dev(device->usbdev); + spin_lock_irq(&device->lock); + } + spin_unlock_irq(&device->lock); + kfree(device->path); + exit_no_path: + exit_no_tra: + usbback_gw_exit(&device->gw); + exit_no_gw: + usbback_xb_channel_exit(&device->channel); + exit_no_channel: + exit_no_rsrc: + while (!list_empty(&device->free_rsrc_list)) { + struct usbback_rsrc *rsrc = list_entry( + device->free_rsrc_list.next, + struct usbback_rsrc, link); + list_del_init(&rsrc->link); + usbback_rsrc_exit(rsrc); + } + return return_value; +} + +static int +usbback_device_init(struct usbback_device *device, struct xenbus_device *dev) +{ + trace(); + return usbback_device_init_or_exit(device, dev, 0); +} + +static void usbback_device_exit(struct usbback_device *device) +{ + trace(); + (void)usbback_device_init_or_exit(device, NULL, 1); +} + +static int +usbback_device_probe_or_remove_xenbus_device(struct xenbus_device *dev, +int remove) +{ + int return_value = 0; + struct usbback_device *device; + trace(); + if (remove) + goto remove_path; + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (device == NULL) { + xenbus_dev_error(dev, -ENOMEM, "allocating device structure"); + return_value = -ENOMEM; + goto exit_no_device; + } + dev->data = device; + return_value = usbback_device_init(device, dev); + if (return_value != 0) + goto exit_no_init; + return 0; + remove_path: + device = dev->data; + usbback_device_exit(device); + exit_no_init: + dev->data = NULL; + kfree(device); + exit_no_device: + return return_value; +} + +static int usbback_device_probe_xenbus_device(struct xenbus_device *dev, +const struct xenbus_device_id *id) +{ + trace(); + return usbback_device_probe_or_remove_xenbus_device(dev, 0); +} + +static int usbback_device_remove_xenbus_device(struct xenbus_device *dev) +{ + trace(); + return usbback_device_probe_or_remove_xenbus_device(dev, 1); +} + +static void +usbback_device_frontend_changed(struct xenbus_device *dev, XenbusState state) +{ + struct usbback_device *device = dev->data; + trace(); + usbback_xb_channel_frontend_changed(&device->channel, dev, state); +} + +static struct usb_device_id usbback_device_usb_id_table[] = { + {.driver_info = 1}, /* Matches all devices. */ + {} +}; + +static struct usb_driver usbback_device_usb_driver = { + .name = "usbback", + .probe = usbback_device_probe_usb_device, + .disconnect = usbback_device_disconnect_usb_device, + .id_table = usbback_device_usb_id_table, +}; + +static struct xenbus_device_id usbback_device_xenbus_ids[] = { + {"usb"}, + {""} +}; + +static struct xenbus_driver usbback_device_xenbus_driver = { + .name = "usb", + .owner = THIS_MODULE, + .ids = usbback_device_xenbus_ids, + .probe = usbback_device_probe_xenbus_device, + .remove = usbback_device_remove_xenbus_device, + .otherend_changed = usbback_device_frontend_changed, +}; + +static int __init usbback_device_class_init(void) +{ + int return_value = 0; + trace(); + return_value = usbback_rsrc_class_init(); + if (return_value != 0) + goto exit_no_rsrc_class; + return_value = usb_register(&usbback_device_usb_driver); + if (return_value != 0) + goto exit_no_usb_register; + return_value = xenbus_register_backend(&usbback_device_xenbus_driver); + if (return_value != 0) + goto exit_no_xenbus_register; + return 0; + exit_no_xenbus_register: + usb_deregister(&usbback_device_usb_driver); + exit_no_usb_register: + usbback_rsrc_class_exit(); + exit_no_rsrc_class: + return return_value; +} + +__initcall(usbback_device_class_init); diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,32 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DEVICE_H +#define USBBACK_DEVICE_H + +#include "usbback_resource.h" + +domid_t usbback_device_query_remote_domain(struct usbback_device *device); + +struct usb_device * usbback_device_query_usbdev(struct usbback_device *device); + +void usbback_device_rsrc_completed_io(struct usbback_device *device, +struct usbback_rsrc *rsrc); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,466 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include "usbback_gw.h" +#include "usbback_trace.h" + +typedef enum { + usbback_gw_stimulus_cc, /* Channel connect. */ + usbback_gw_stimulus_cm, /* Channel message. */ + usbback_gw_stimulus_cd, /* Channel disconnect. */ + usbback_gw_stimulus_kc, /* Kick channel messages completed. */ + usbback_gw_stimulus_tc, /* Target rsrc completed. */ + usbback_gw_stimulus_ti, /* Target rsrcs idle. */ + usbback_gw_stimulus_lc, /* Client connect completed. */ + usbback_gw_stimulus_lg, /* Client disconnect called. */ + usbback_gw_stimulus_ld, /* Client disconnect completed. */ +} usbback_gw_stimulus; + +static void usbback_gw_handle_stimulus(struct usbback_gw *gw, +usbback_gw_stimulus stimulus); + +static void usbback_gw_channel_connect(void *context) +{ + struct usbback_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_cc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbback_gw_handle_channel_message(void *context, +struct usbback_channel_ibmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_gw *gw = context; + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(usbback_channel_ibmsg_to_link(message), + &gw->channel_message_list); + usbback_gw_handle_stimulus(gw,usbback_gw_stimulus_cm); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void +usbback_gw_channel_disconnect(void *context, struct usbback_callback *callback) +{ + struct usbback_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + gw->channel_disconnect_callback = callback; + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_cd); + spin_unlock_irqrestore(&gw->lock, flags); +} + +void usbback_gw_submit_channel_message(struct usbback_gw *gw, +struct usbback_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + usbback_channel_submit_message(gw->channel, message); +} + +void usbback_gw_submit_tra_to_client(struct usbback_gw *gw, +struct usbback_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + gw->handle_tra(gw, tra); +} + +static void usbback_gw_invalid_stimulus(struct usbback_gw *gw, +usbback_gw_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbback: gw %p in state %d" + "received invalid stimulus %d", gw, gw->state, stimulus); +} + +static void +usbback_gw_kick_channel_messages(struct usbback_gw *gw) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!gw->kick_channel_messages_out) { + gw->kick_channel_messages_out = 1; + (void)usbback_work_schedule(&gw->kick_channel_messages_1_work); + } +} + +static void usbback_gw_kick_channel_messages_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_gw *gw = data; + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + while ((!list_empty(&gw->channel_message_list)) && + (!list_empty(&gw->target_rsrc_list))) { + usbif_error error = USBIF_ERROR_SUCCESS; + struct usbback_channel_ibmsg *message; + struct usbback_gw_rsrc *rsrc; + message = list_entry(gw->channel_message_list.next, + struct usbback_channel_ibmsg, + callback.work.link); + rsrc = list_entry(gw->target_rsrc_list.next, + struct usbback_gw_rsrc, + callback.work.link); + list_del_init(usbback_channel_ibmsg_to_link(message)); + list_del_init(usbback_gw_rsrc_to_link(rsrc)); + gw->target_rsrcs_out++; + spin_unlock_irqrestore(&gw->lock, flags); + usbback_gw_rsrc_start_tra(rsrc, &message->request); + spin_lock_irqsave(&gw->lock, flags); + usbback_callback_complete(usbback_channel_ibmsg_to_callback( + message), error); + } + gw->kick_channel_messages_out = 0; + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_kc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void +usbback_gw_kick_channel_messages_2(struct usbback_callback *callback) +{ + struct usbback_gw_rsrc *rsrc = usbback_gw_rsrc_callback_to(callback); + struct usbback_gw *gw = usbback_gw_rsrc_query_gw(rsrc); + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(usbback_gw_rsrc_to_link(rsrc), &gw->target_rsrc_list); + if (--gw->target_rsrcs_out != 0) { + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_tc); + } else { + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_ti); + } + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbback_gw_complete_channel_messages(struct usbback_gw *gw) +{ + trace(); + while (!list_empty(&gw->channel_message_list)) { + struct usbback_channel_ibmsg *message = list_entry( + gw->channel_message_list.next, + struct usbback_channel_ibmsg, + callback.work.link); + list_del_init(usbback_channel_ibmsg_to_link(message)); + usbback_callback_success(usbback_channel_ibmsg_to_callback( + message)); + } +} + +static void usbback_gw_connect_client(struct usbback_gw *gw) +{ + trace(); + (void)usbback_work_schedule(&gw->connect_client_1_work); +} + +static void usbback_gw_connect_client_1(void *data) +{ + struct usbback_gw *gw = data; + unsigned long flags; + trace(); + gw->connect(gw); + spin_lock_irqsave(&gw->lock, flags); + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_lc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbback_gw_disconnect_client(struct usbback_gw *gw) +{ + trace(); + (void)usbback_work_schedule(&gw->disconnect_client_1_work); +} + +static void usbback_gw_disconnect_client_1(void *data) +{ + struct usbback_gw *gw = data; + unsigned long flags; + trace(); + gw->disconnect(gw, &gw->disconnect_client_2_callback); + spin_lock_irqsave(&gw->lock, flags); + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_lg); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbback_gw_disconnect_client_2(struct usbback_callback *callback) +{ + struct usbback_gw *gw = container_of(callback, struct usbback_gw, + disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_ld); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbback_gw_test_target_rsrcs(struct usbback_gw *gw) +{ + trace(); + if (gw->target_rsrcs_out == 0) + usbback_gw_handle_stimulus(gw, usbback_gw_stimulus_ti); +} + +static void usbback_gw_complete_channel_disconnect(struct usbback_gw *gw) +{ + trace(); + usbback_callback_success(gw->channel_disconnect_callback); +} + +static int usbback_gw_init_or_exit(struct usbback_gw *gw, int exit) +{ + int return_value = 0; + u32 i; + trace(); + if (exit) + goto exit_path; + INIT_LIST_HEAD(&gw->target_rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbback_gw_rsrc *rsrc = &gw->target_rsrcs[i]; + usbback_gw_rsrc_init(rsrc, gw, + usbback_gw_kick_channel_messages_2); + list_add_tail(usbback_gw_rsrc_to_link(rsrc), + &gw->target_rsrc_list); + } + spin_lock_init(&gw->lock); + gw->state = usbback_gw_state_i; + INIT_LIST_HEAD(&gw->channel_message_list); + usbback_work_init(&gw->kick_channel_messages_1_work, + usbback_gw_kick_channel_messages_1, gw); + usbback_work_init(&gw->connect_client_1_work, + usbback_gw_connect_client_1, gw); + usbback_work_init(&gw->disconnect_client_1_work, + usbback_gw_disconnect_client_1, gw); + usbback_callback_init(&gw->disconnect_client_2_callback, + usbback_gw_disconnect_client_2); + gw->kick_channel_messages_out = 0; + gw->target_rsrcs_out = 0; + return 0; + exit_path: + return return_value; +} + +int usbback_gw_init(struct usbback_gw *gw, struct usbback_channel *channel, +usbback_gw_connect_function *connect, +usbback_gw_handle_tra_function *handle_tra, +usbback_gw_disconnect_function *disconnect) +{ + trace(); + gw->channel = channel; + gw->connect = connect; + gw->handle_tra = handle_tra; + gw->disconnect = disconnect; + usbback_channel_install_client(channel, gw, + usbback_gw_channel_connect, + usbback_gw_handle_channel_message, + usbback_gw_channel_disconnect); + return usbback_gw_init_or_exit(gw, 0); +} + +void usbback_gw_exit(struct usbback_gw *gw) +{ + trace(); + (void)usbback_gw_init_or_exit(gw, 1); +} + +static void +usbback_gw_handle_stimulus(struct usbback_gw *gw, +usbback_gw_stimulus stimulus) +{ + switch (gw->state) { + case usbback_gw_state_i: + /* Channel disconnected. */ + /* Client disconnected. */ + /* No channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_cc: + gw->state = usbback_gw_state_i_cc; + usbback_gw_connect_client(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc: + /* Channel connected. */ + /* Client connecting. */ + /* Maybe channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_cm: + break; + case usbback_gw_stimulus_cd: + gw->state = usbback_gw_state_i_cc_cd; + usbback_gw_complete_channel_messages(gw); + break; + case usbback_gw_stimulus_lc: + gw->state = usbback_gw_state_i_cc_lc; + usbback_gw_kick_channel_messages(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_cd: + /* Channel disconnecting. */ + /* Client connecting. */ + /* No channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_lc: + gw->state = usbback_gw_state_i_cc_cd_lc; + usbback_gw_disconnect_client(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_lc: + /* Channel connected. */ + /* Client connected. */ + /* Maybe channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Maybe kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_cm: + usbback_gw_kick_channel_messages(gw); + break; + case usbback_gw_stimulus_cd: + gw->state = usbback_gw_state_i_cc_lc_cd; + usbback_gw_complete_channel_messages(gw); + usbback_gw_kick_channel_messages(gw); + break; + case usbback_gw_stimulus_kc: + break; + case usbback_gw_stimulus_tc: + case usbback_gw_stimulus_ti: + usbback_gw_kick_channel_messages(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_lc_cd: + /* Channel disconnecting. */ + /* Client connected. */ + /* No channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_kc: + gw->state = usbback_gw_state_i_cc_cd_lc; + usbback_gw_disconnect_client(gw); + break; + case usbback_gw_stimulus_tc: + case usbback_gw_stimulus_ti: + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_cd_lc: + /* Channel disconnecting. */ + /* Calling client disconnect. */ + /* No channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_tc: + case usbback_gw_stimulus_ti: + break; + case usbback_gw_stimulus_lg: + gw->state = usbback_gw_state_i_cc_cd_lc_lg; + break; + case usbback_gw_stimulus_ld: + gw->state = usbback_gw_state_i_cc_cd_lc_ld; + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_cd_lc_lg: + /* Channel disconnecting. */ + /* Client disconnecting. */ + /* No channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_tc: + case usbback_gw_stimulus_ti: + break; + case usbback_gw_stimulus_ld: + gw->state = usbback_gw_state_i_cc_cd_lc_lg_ld; + usbback_gw_test_target_rsrcs(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_cd_lc_ld: + /* Channel disconnecting. */ + /* Client disconnected but call still in progress. */ + /* No channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_tc: + case usbback_gw_stimulus_ti: + break; + case usbback_gw_stimulus_lg: + gw->state = + usbback_gw_state_i_cc_cd_lc_lg_ld; + usbback_gw_test_target_rsrcs(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbback_gw_state_i_cc_cd_lc_lg_ld: + /* Channel disconnecting. */ + /* Client disconnected. */ + /* No channel messages queued. */ + /* Test target rsrcs or target rsrcs busy. */ + /* Not kicking cm. */ + switch (stimulus) { + case usbback_gw_stimulus_tc: + break; + case usbback_gw_stimulus_ti: + gw->state = usbback_gw_state_i; + usbback_gw_complete_channel_disconnect(gw); + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + default: + usbback_gw_invalid_stimulus(gw, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,163 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_GW_H +#define USBBACK_GW_H + +#include +#include +#include "usbback_callback.h" +#include "usbback_channel.h" + +struct usbback_gw_rsrc; + +struct usbback_gw_tra { + struct usbback_callback callback; + union usbif_parameters parameters; + union usbif_status status; +}; + +static inline struct usbback_callback * +usbback_gw_tra_to_callback(struct usbback_gw_tra *tra) +{ + return &tra->callback; +} + +static inline struct usbback_gw_tra * +usbback_gw_tra_callback_to(struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_gw_tra, callback); +} + +static inline struct list_head * +usbback_gw_tra_to_link(struct usbback_gw_tra *tra) +{ + return usbback_callback_to_link(usbback_gw_tra_to_callback(tra)); +} + +static inline struct usbback_gw_tra * +usbback_gw_tra_link_to(struct list_head *link) +{ + return usbback_gw_tra_callback_to(usbback_callback_link_to(link)); +} + +static inline void +usbback_gw_tra_init(struct usbback_gw_tra *tra, +usbback_callback_function *callback) +{ + usbback_callback_init(usbback_gw_tra_to_callback(tra), callback); +} + +static inline void +usbback_gw_tra_complete(struct usbback_gw_tra *tra, usbif_error error) +{ + usbback_callback_complete(usbback_gw_tra_to_callback(tra), error); +} + +static inline +usbif_error usbback_gw_tra_query_error(struct usbback_gw_tra *tra) +{ + return usbback_callback_query_error(usbback_gw_tra_to_callback(tra)); +} + +struct usbback_gw_rsrc { + struct usbback_callback callback; + struct usbback_gw *gw; + struct usbback_gw_tra tra; + struct usbback_channel_obmsg channel_message; +}; + +static inline struct list_head * +usbback_gw_rsrc_to_link(struct usbback_gw_rsrc *rsrc) +{ + return &rsrc->callback.work.link; +} + +static inline struct usbback_gw_rsrc * +usbback_gw_rsrc_callback_to(struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_gw_rsrc, callback); +} + +static inline struct usbback_gw * +usbback_gw_rsrc_query_gw(struct usbback_gw_rsrc *rsrc) +{ + return rsrc->gw; +} + +void +usbback_gw_rsrc_init(struct usbback_gw_rsrc *rsrc, struct usbback_gw *gw, +usbback_callback_function callback); + +void +usbback_gw_rsrc_start_tra(struct usbback_gw_rsrc *rsrc, +struct usbif_request *request); + +typedef enum { + usbback_gw_state_i, + usbback_gw_state_i_cc, + usbback_gw_state_i_cc_cd, + usbback_gw_state_i_cc_lc, + usbback_gw_state_i_cc_lc_cd, + usbback_gw_state_i_cc_cd_lc, + usbback_gw_state_i_cc_cd_lc_lg, + usbback_gw_state_i_cc_cd_lc_ld, + usbback_gw_state_i_cc_cd_lc_lg_ld +} usbback_gw_state; + +struct usbback_gw; + +typedef void usbback_gw_connect_function(struct usbback_gw *gw); + +typedef void +usbback_gw_handle_tra_function(struct usbback_gw *gw, +struct usbback_gw_tra *tra); + +typedef void +usbback_gw_disconnect_function(struct usbback_gw *gw, +struct usbback_callback *callback); + +struct usbback_gw { + struct usbback_channel *channel; + usbback_gw_connect_function *connect; + usbback_gw_handle_tra_function *handle_tra; + usbback_gw_disconnect_function *disconnect; + struct list_head target_rsrc_list; + struct usbback_gw_rsrc target_rsrcs[USBIF_QUOTA]; + spinlock_t lock; + usbback_gw_state state; + struct list_head channel_message_list; + struct usbback_work kick_channel_messages_1_work; + struct usbback_work connect_client_1_work; + struct usbback_work disconnect_client_1_work; + struct usbback_callback disconnect_client_2_callback; + int kick_channel_messages_out:1; + u32 target_rsrcs_out; + struct usbback_callback *channel_disconnect_callback; +}; + +int +usbback_gw_init(struct usbback_gw *gw, struct usbback_channel *channel, +usbback_gw_connect_function *connect, +usbback_gw_handle_tra_function *handle_tra, +usbback_gw_disconnect_function *disconnect); + +extern void usbback_gw_exit(struct usbback_gw *gw); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw_resource.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_gw_resource.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,75 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include "usbback_gw.h" +#include "usbback_trace.h" + +void +usbback_gw_submit_channel_message(struct usbback_gw *gw, +struct usbback_channel_obmsg *message); + +void +usbback_gw_submit_tra_to_client(struct usbback_gw *gw, +struct usbback_gw_tra *tra); + +void +usbback_gw_rsrc_start_tra(struct usbback_gw_rsrc *rsrc, +struct usbif_request *request) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + rsrc->tra.parameters = request->usbif_parameters; + memset(&rsrc->tra.status,0, sizeof(rsrc->tra.status)); + rsrc->channel_message.response.gw_status.id = + request->gw_parameters.id; + usbback_gw_submit_tra_to_client(rsrc->gw, &rsrc->tra); +} + +static void +usbback_gw_rsrc_tra_callback(struct usbback_callback *callback) +{ + struct usbback_gw_rsrc *rsrc = container_of( + usbback_gw_tra_callback_to(callback), + struct usbback_gw_rsrc, tra); + rsrc->channel_message.response.gw_status.error = + usbback_callback_query_error(callback); + rsrc->channel_message.response.usbif_status = rsrc->tra.status; + usbback_gw_submit_channel_message(rsrc->gw, &rsrc->channel_message); +} + +static void +usbback_gw_rsrc_channel_message_callback(struct usbback_callback *callback) +{ + struct usbback_gw_rsrc *rsrc = container_of( + usbback_channel_obmsg_callback_to(callback), + struct usbback_gw_rsrc, + channel_message); + usbback_callback_success(&rsrc->callback); +} + +void +usbback_gw_rsrc_init(struct usbback_gw_rsrc *rsrc, struct usbback_gw *gw, +usbback_callback_function callback) +{ + trace(); + usbback_callback_init(&rsrc->callback, callback); + rsrc->gw = gw; + usbback_gw_tra_init(&rsrc->tra, usbback_gw_rsrc_tra_callback); + usbback_channel_obmsg_init(&rsrc->channel_message, + usbback_gw_rsrc_channel_message_callback); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_rbr_mapper_pool.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_rbr_mapper_pool.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,347 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbback_rbr_mapper_pool.h" +#include "usbback_trace.h" + +static USBBACK_CALLBACK_SERIALISER(callback_serialiser); + +struct usbback_rbr_mapper_pool { + struct usbback_buffer_rsrc_list dedicated_rsrcs; + struct usbback_buffer_rsrc_provider *provider; + spinlock_t lock; + struct usbback_buffer_rsrc_list head_request_rqm; + struct list_head request_list; + int kicking_requests; + int head_request_rqm_calculated; +}; + +static int usbback_rbr_mapper_pool_calc_rbr_rqm(struct usbif_rbr *rbr, +struct usbback_buffer_rsrc_list *list) +{ + int request_invalid = 0; + trace(); + *list = usbback_buffer_rsrc_list_null(); + if (rbr->byte_count != 0) { + if ((rbr->byte_offset < (USBIF_GRANT_TABLE_REFERENCE_COUNT * + PAGE_SIZE)) && (rbr->byte_count <= + ((USBIF_GRANT_TABLE_REFERENCE_COUNT * + PAGE_SIZE) - rbr->byte_offset))) { + unsigned long first_page, final_page; + list->page_ranges = 1; + first_page = rbr->byte_offset / PAGE_SIZE; + final_page = (rbr->byte_offset + rbr->byte_count - 1) / + PAGE_SIZE; + list->page_range_page_count = final_page - + first_page + 1; + } else { + request_invalid = 1; + } + } + return request_invalid; +} + +static int +usbback_rbr_mapper_pool_calc_rqm(struct usbback_rbr_mapper_request *request, +struct usbback_buffer_rsrc_list *list) +{ + struct usbback_rbr_mapper_element *element; + trace(); + *list = usbback_buffer_rsrc_list_null(); + list_for_each_entry(element, &request->request_elements, link) { + struct usbback_buffer_rsrc_list element_list; + if (usbback_rbr_mapper_pool_calc_rbr_rqm(&element->rbr, + &element_list) != 0) + return 1; + usbback_buffer_rsrc_list_plus_equals(list, &element_list); + } + return 0; +} + +static void +usbback_rbr_mapper_pool_unmap_rbr(struct usbback_rbr_context *context) +{ + struct gnttab_unmap_grant_ref unmap[USBIF_GRANT_TABLE_REFERENCE_COUNT]; + int i; + trace(); + for (i = 0; i < context->page_count; i++) { + unmap[i].host_addr = context->mmap_vaddress + (i * PAGE_SIZE); + unmap[i].dev_bus_addr = 0; + unmap[i].handle = context->handle[i]; + } + if (context->page_count != 0) { + int error = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, + unmap, context->page_count); + BUG_ON(error); + } + for (i = 0; i < context->page_count; i++) { + set_phys_to_machine(__pa(context->mmap_vaddress + (i * + PAGE_SIZE)) >> PAGE_SHIFT, INVALID_P2M_ENTRY); + } + if (context->mmap_vaddress != 0) + usbback_buffer_rsrc_provider_free_page_range( + context->provider, context->mmap_vaddress); +} + +static int +usbback_rbr_mapper_pool_map_rbr(struct usbif_rbr *rbr, domid_t domain, +struct usbback_buffer_rsrc_provider *provider, void **mapping, +int access_flags, struct usbback_rbr_context *context) +{ + struct gnttab_map_grant_ref map[USBIF_GRANT_TABLE_REFERENCE_COUNT]; + unsigned long first_page, final_page, page_count; + int i, j, error; + uint16_t map_flags = GNTMAP_host_map | (((access_flags & + USBBACK_ACCESS_FLAGS_WRITE) == 0) ? GNTMAP_readonly : 0); + trace(); + if (rbr->byte_count == 0) { + context->page_count = 0; + context->mmap_vaddress = 0; + *mapping = NULL; + return 0; + } + first_page = rbr->byte_offset / PAGE_SIZE; + final_page = (rbr->byte_offset + rbr->byte_count - 1) / PAGE_SIZE; + page_count = final_page - first_page + 1; + context->provider = provider; + context->page_count = page_count; + context->mmap_vaddress = + usbback_buffer_rsrc_provider_allocate_page_range( + provider, page_count); + for (i = 0; i < page_count; i++) { + map[i].host_addr = context->mmap_vaddress + (i * PAGE_SIZE); + map[i].dom = domain; + map[i].ref = rbr->reference[first_page + i]; + map[i].flags = map_flags; + } + error = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map, + page_count); + BUG_ON(error); + for (i = 0, j = 0; i < page_count; i++) { + if (likely(map[i].status == 0)) { + context->handle[j] = map[i].handle; + set_phys_to_machine(__pa(context->mmap_vaddress + + (j * PAGE_SIZE)) + >> PAGE_SHIFT, + FOREIGN_FRAME(map[i].dev_bus_addr + >> PAGE_SHIFT)); + j++; + } else { + error = 1; + context->page_count--; + } + } + if (error) { + usbback_rbr_mapper_pool_unmap_rbr(context); + return -EINVAL; + } + *mapping = (void *)(context->mmap_vaddress + (rbr->byte_offset % + PAGE_SIZE)); + return 0; +} + +static void +usbback_rbr_mapper_pool_service_request( +struct usbback_rbr_mapper_pool *pool, +struct usbback_rbr_mapper_request *request) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + int error = 0; + struct usbback_rbr_mapper_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + if (!error) { + error = usbback_rbr_mapper_pool_map_rbr(&element->rbr, + request->domain, pool->provider, + &element->mapping, + element->access_flags, + &element->context); + if (!error) + element->mapped = 1; + } else { + element->mapped = 0; + } + } + if (error) + goto error_path; + usbback_callback_serialiser_complete_callback(&callback_serialiser, + usbback_rbr_mapper_request_to_map_callback(request), + USBIF_ERROR_SUCCESS); + return; + error_path: + list_for_each_entry(element, &request->request_elements, link) { + if (element->mapped) + usbback_rbr_mapper_pool_unmap_rbr(&element->context); + } + usbback_callback_serialiser_complete_callback(&callback_serialiser, + usbback_rbr_mapper_request_to_map_callback(request), + usbif_error_map_local_to(error)); +} + +static void usbback_rbr_mapper_pool_kick_requests( +struct usbback_rbr_mapper_pool *pool) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + spin_lock_irqsave(&pool->lock, flags); + while (!pool->kicking_requests && !list_empty(&pool->request_list)) { + struct usbback_rbr_mapper_request *request = + usbback_rbr_mapper_request_link_to( + pool->request_list.next); + struct usbback_buffer_rsrc_list free_rsrcs; + if (pool->head_request_rqm_calculated) + goto skip_calc; + if ((usbback_rbr_mapper_pool_calc_rqm(request, + &pool->head_request_rqm) != 0) || + !usbback_buffer_rsrc_list_subset_of( + &pool->head_request_rqm, + &pool->dedicated_rsrcs)) { + list_del_init(usbback_rbr_mapper_request_to_link( + request)); + usbback_callback_serialiser_complete_callback( + &callback_serialiser, + usbback_rbr_mapper_request_to_map_callback( + request), + USBIF_ERROR_TOO_BIG); + continue; + } + pool->head_request_rqm_calculated = 1; + skip_calc: + free_rsrcs = usbback_buffer_rsrc_provider_query_free_rsrcs( + pool->provider); + if (!usbback_buffer_rsrc_list_subset_of( + &pool->head_request_rqm, &free_rsrcs)) + break; + list_del_init(usbback_rbr_mapper_request_to_link(request)); + pool->head_request_rqm_calculated = 0; + pool->kicking_requests = 1; + spin_unlock_irqrestore(&pool->lock, flags); + usbback_rbr_mapper_pool_service_request(pool, request); + spin_lock_irqsave(&pool->lock, flags); + pool->kicking_requests = 0; + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void +usbback_rbr_mapper_pool_reserve_and_map_rbrs( +struct usbback_rbr_mapper_pool *pool, +struct usbback_rbr_mapper_request *request, domid_t domain) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + request->pool = pool; + request->domain = domain; + spin_lock_irqsave(&pool->lock, flags); + list_add_tail(usbback_rbr_mapper_request_to_link(request), + &pool->request_list); + spin_unlock_irqrestore(&pool->lock, flags); + usbback_rbr_mapper_pool_kick_requests(pool); +} + +void +usbback_rbr_mapper_pool_abort_reserve_and_map_rbrs( +struct usbback_rbr_mapper_request *request, usbif_error error) +{ + struct usbback_rbr_mapper_pool *pool = request->pool; + struct usbback_rbr_mapper_request *queued_request; + unsigned long flags; + trace(); + spin_lock_irqsave(&pool->lock, flags); + list_for_each_entry(queued_request, &pool->request_list, + map_callback.work.link) { + if (request == queued_request) { + list_del_init(usbback_rbr_mapper_request_to_link( + request)); + pool->head_request_rqm_calculated = 0; + usbback_callback_serialiser_complete_callback( + &callback_serialiser, + usbback_rbr_mapper_request_to_map_callback( + request), + error); + break; + } + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void usbback_rbr_mapper_pool_unmap_and_unreserve_rbrs( +struct usbback_rbr_mapper_request *request) +{ + struct usbback_rbr_mapper_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + usbback_rbr_mapper_pool_unmap_rbr(&element->context); + } + usbback_rbr_mapper_pool_kick_requests(request->pool); + usbback_callback_serialiser_complete_callback(&callback_serialiser, + usbback_rbr_mapper_request_to_unmap_callback(request), + USBIF_ERROR_SUCCESS); +} + +static int +usbback_rbr_mapper_pool_init_or_exit(struct usbback_rbr_mapper_pool *pool, +int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit; + pool->provider = usbback_allocate_buffer_rsrc_provider( + pool->dedicated_rsrcs); + if (pool->provider == NULL) + goto exit_no_provider; + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->request_list); + pool->kicking_requests = 0; + pool->head_request_rqm_calculated = 0; + return 0; + exit: + usbback_free_buffer_rsrc_provider(pool->provider); + exit_no_provider: + return return_value; +} + +struct usbback_rbr_mapper_pool * usbback_rbr_mapper_pool_allocate(void) +{ + struct usbback_rbr_mapper_pool *pool; + trace(); + pool = (struct usbback_rbr_mapper_pool *)kmalloc(sizeof( + struct usbback_rbr_mapper_pool),GFP_KERNEL); + if (pool != NULL) { + pool->dedicated_rsrcs.page_ranges = USBIF_QUOTA; + pool->dedicated_rsrcs.page_range_page_count = + USBIF_GRANT_TABLE_REFERENCE_COUNT; + if (usbback_rbr_mapper_pool_init_or_exit(pool, 0) != 0) { + kfree(pool); + pool = NULL; + } + } + return pool; +} + +void usbback_rbr_mapper_pool_free(struct usbback_rbr_mapper_pool *pool) +{ + trace(); + (void)usbback_rbr_mapper_pool_init_or_exit(pool, 1); + kfree(pool); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_rbr_mapper_pool.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_rbr_mapper_pool.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,155 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_RBR_MAPPER_POOL_H +#define USBBACK_RBR_MAPPER_POOL_H + +#include "usbback_callback.h" +#include "usbback_buffer_resource_provider.h" + +struct usbback_rbr_mapper_pool; + +struct usbback_rbr_mapper_pool *usbback_rbr_mapper_pool_allocate(void); + +void usbback_rbr_mapper_pool_free(struct usbback_rbr_mapper_pool *pool); + +#define USBBACK_ACCESS_FLAGS_READ 1 +#define USBBACK_ACCESS_FLAGS_WRITE 2 + +struct usbback_rbr_context { + struct usbback_buffer_rsrc_provider *provider; + unsigned long page_count; + unsigned long mmap_vaddress; + grant_handle_t handle[USBIF_GRANT_TABLE_REFERENCE_COUNT]; +}; + +struct usbback_rbr_mapper_element { + struct list_head link; + struct usbif_rbr rbr; + int access_flags; + int mapped; + void *mapping; + struct usbback_rbr_context context; +}; + +static inline void +usbback_rbr_mapper_element_init(struct usbback_rbr_mapper_element *element) +{ + memset(element, 0, sizeof(*element)); + INIT_LIST_HEAD(&element->link); +} + +static inline void +usbback_rbr_mapper_element_set_rbr(struct usbback_rbr_mapper_element *element, +struct usbif_rbr rbr, int access_flags) +{ + element->rbr = rbr; + element->access_flags = access_flags; +} + +static inline void usbback_rbr_mapper_element_ensure_removed( +struct usbback_rbr_mapper_element *element) +{ + list_del_init(&element->link); +} + +struct usbback_rbr_mapper_request { + struct usbback_callback map_callback; + struct usbback_callback unmap_callback; + struct usbback_rbr_mapper_pool *pool; + domid_t domain; + struct list_head request_elements; +}; + +static inline struct usbback_callback * +usbback_rbr_mapper_request_to_map_callback( +struct usbback_rbr_mapper_request *request) +{ + return &request->map_callback; +} + +static inline struct usbback_rbr_mapper_request * +usbback_rbr_mapper_request_map_callback_to( +struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_rbr_mapper_request, + map_callback); +} + +static inline struct usbback_callback * +usbback_rbr_mapper_request_to_unmap_callback( +struct usbback_rbr_mapper_request *request) +{ + return &request->unmap_callback; +} + +static inline struct usbback_rbr_mapper_request * +usbback_rbr_mapper_request_unmap_callback_to( +struct usbback_callback *callback) +{ + return container_of(callback, struct usbback_rbr_mapper_request, + unmap_callback); +} + +static inline struct list_head * +usbback_rbr_mapper_request_to_link(struct usbback_rbr_mapper_request *request) +{ + return usbback_callback_to_link( + usbback_rbr_mapper_request_to_map_callback(request)); +} + +static inline struct usbback_rbr_mapper_request * +usbback_rbr_mapper_request_link_to(struct list_head *link) +{ + return usbback_rbr_mapper_request_map_callback_to( + usbback_callback_link_to(link)); +} + +static inline void +usbback_rbr_mapper_request_init(struct usbback_rbr_mapper_request *request, +usbback_callback_function *map_callback, +usbback_callback_function *unmap_callback) +{ + usbback_callback_init(&request->map_callback, map_callback); + usbback_callback_init(&request->unmap_callback, unmap_callback); + INIT_LIST_HEAD(&request->request_elements); +} + +static inline void +usbback_rbr_mapper_request_add_element( +struct usbback_rbr_mapper_request *request, +struct usbback_rbr_mapper_element *element) +{ + list_add(&element->link, &request->request_elements); +} + +void +usbback_rbr_mapper_pool_reserve_and_map_rbrs( +struct usbback_rbr_mapper_pool *pool, +struct usbback_rbr_mapper_request *request, domid_t domain); + +void +usbback_rbr_mapper_pool_abort_reserve_and_map_rbrs( +struct usbback_rbr_mapper_request *request, usbif_error error); + +void +usbback_rbr_mapper_pool_unmap_and_unreserve_rbrs( +struct usbback_rbr_mapper_request *request); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_resource.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_resource.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,655 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on arch/xen/drivers/usbif/backend/main.c, original copyright notice */ +/* follows... */ +/*****************************************************************************/ + +/****************************************************************************** + * 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 + * Copyright (c) 2004, 2005 Mark Williamson + * + * Based on arch/xen/drivers/blkif/backend/main.c + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + */ + +#include +#include +#include "usbback_device.h" +#include "usbback_rbr_mapper_pool.h" +#include "usbback_resource.h" +#include "usbback_trace.h" + +static USBBACK_CALLBACK_SERIALISER(submit_urb_serialiser); + +static struct usbback_rbr_mapper_pool *rbr_mapper_pool; + +int usbback_rsrc_class_init(void) +{ + trace(); + rbr_mapper_pool = usbback_rbr_mapper_pool_allocate(); + return (rbr_mapper_pool != NULL) ? 0 : -ENOMEM; +} + +void usbback_rsrc_class_exit(void) +{ + trace(); + usbback_rbr_mapper_pool_free(rbr_mapper_pool); +} + +typedef enum { + usbback_rsrc_stimulus_st, /* Start IO */ + usbback_rsrc_stimulus_tu, /* Try unlink */ + usbback_rsrc_stimulus_ms, /* Map success */ + usbback_rsrc_stimulus_mf, /* Map failure */ + usbback_rsrc_stimulus_us, /* URB submitted */ + usbback_rsrc_stimulus_un, /* URB not submitted */ + usbback_rsrc_stimulus_uc, /* URB completed */ + usbback_rsrc_stimulus_ul, /* Unlink completed */ +} usbback_rsrc_stimulus; + +static void +usbback_rsrc_handle_stimulus(struct usbback_rsrc *rsrc, +usbback_rsrc_stimulus stimulus); + +void +usbback_rsrc_start_io(struct usbback_rsrc *rsrc, struct usbback_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->tra_error = USBIF_ERROR_SUCCESS; + rsrc->tra = tra; + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_st); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +int usbback_rsrc_try_unlink(struct usbback_rsrc *rsrc, int unlink_id) +{ + int return_value = 0; + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + if ((rsrc->tra != NULL) && (rsrc->tra->parameters.io.header.unlink_id + == unlink_id)) { + usbback_rsrc_handle_stimulus(rsrc, usbback_rsrc_stimulus_tu); + return_value = 1; + } + spin_unlock_irqrestore(&rsrc->lock, flags); + return return_value; +} + +void usbback_rsrc_flush_tra(struct usbback_rsrc *rsrc) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + if (rsrc->tra != NULL) + usbback_rsrc_handle_stimulus(rsrc, usbback_rsrc_stimulus_tu); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + + +static void usbback_rsrc_invalid_stimulus(struct usbback_rsrc *rsrc, +usbback_rsrc_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbback: port rsrc %p in state %d received " + "invalid stimulus %d", rsrc, rsrc->state, stimulus); +} + +static void usbback_rsrc_map_buffer(struct usbback_rsrc *rsrc) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbif_io_tra_parameters_header *header = + &rsrc->tra->parameters.io.header; + trace(); + usbback_rbr_mapper_element_ensure_removed(&rsrc->request_element[0]); + usbback_rbr_mapper_element_ensure_removed(&rsrc->request_element[1]); + usbback_rbr_mapper_request_add_element(&rsrc->rbr_mapper_request, + &rsrc->request_element[0]); + usbback_rbr_mapper_element_set_rbr(&rsrc->request_element[0], + header->rbr, + (header->direction == USBIF_IO_TRA_DIRECTION_OUT) ? + USBBACK_ACCESS_FLAGS_READ : + USBBACK_ACCESS_FLAGS_WRITE); + if (header->io_tra_type == USBIF_IO_TRA_TYPE_ISOCHRONOUS) { + struct usbif_isochronous_io_tra_parameters *parameters= + &rsrc->tra->parameters.io.isochronous; + usbback_rbr_mapper_request_add_element( + &rsrc->rbr_mapper_request, + &rsrc->request_element[1]); + usbback_rbr_mapper_element_set_rbr( + &rsrc->request_element[1], + parameters->schedule_rbr, + USBBACK_ACCESS_FLAGS_READ | + USBBACK_ACCESS_FLAGS_WRITE); + } + usbback_rbr_mapper_pool_reserve_and_map_rbrs(rbr_mapper_pool, + &rsrc->rbr_mapper_request, + usbback_device_query_remote_domain(rsrc->device)); + return; +} + +static void usbback_rsrc_abort_map(struct usbback_rsrc *rsrc) +{ + trace(); + usbback_rbr_mapper_pool_abort_reserve_and_map_rbrs( + &rsrc->rbr_mapper_request, + USBIF_ERROR_UNLINKED); +} + +static void usbback_rsrc_map_callback(struct usbback_callback * callback) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_rbr_mapper_request *request = + usbback_rbr_mapper_request_map_callback_to( + callback); + struct usbback_rsrc *rsrc = container_of(request, + struct usbback_rsrc, + rbr_mapper_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->tra_error = usbback_callback_query_error(callback); + if (rsrc->tra_error == USBIF_ERROR_SUCCESS) { + rsrc->rbrs_mapped = 1; + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_ms); + } else { + trace_info("failed to map FE buffer"); + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_mf); + } + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void usbback_rsrc_submit_urb(struct usbback_rsrc *rsrc) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + usbback_callback_serialiser_complete_callback(&submit_urb_serialiser, + &rsrc->submit_urb_1_callback, USBIF_ERROR_SUCCESS); +} + +static void usbback_rsrc_end_io(struct urb *urb, struct pt_regs *regs); + +static int check_iso_schedule(struct urb *urb) +{ + int i; + unsigned long total_length = 0; + for (i = 0; i < urb->number_of_packets; i++) { + struct usb_iso_packet_descriptor *desc = + &urb->iso_frame_desc[i]; + total_length += desc->length; + if ((desc->offset > urb->transfer_buffer_length) || + (desc->length > (urb->transfer_buffer_length - + desc->offset)) || + (total_length > urb->transfer_buffer_length)) + return -EINVAL; + } + return 0; +} + +static void usbback_rsrc_submit_urb_1(struct usbback_callback * callback) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + static const unsigned int pipe_type[4] = { + [USBIF_IO_TRA_TYPE_CONTROL] = PIPE_CONTROL, + [USBIF_IO_TRA_TYPE_BULK] = PIPE_BULK, + [USBIF_IO_TRA_TYPE_INTERRUPT] = PIPE_INTERRUPT, + [USBIF_IO_TRA_TYPE_ISOCHRONOUS] = PIPE_ISOCHRONOUS + }; + struct usbback_rsrc *rsrc = container_of(callback, + struct usbback_rsrc, submit_urb_1_callback); + union usbif_io_tra_parameters *io_parameters = + &rsrc->tra->parameters.io; + struct urb *urb = rsrc->urb; + struct usb_device * usbdev = + usbback_device_query_usbdev(rsrc->device); + unsigned int pipe; + unsigned long flags; + trace(); + if (io_parameters->header.io_tra_type >= 4) { + rsrc->tra_error = USBIF_ERROR_INVALID_PARAMETER; + goto protocol_error; + } + if (io_parameters->header.io_tra_type == + USBIF_IO_TRA_TYPE_CONTROL) { + struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *) + io_parameters->control.setup; + if (ctrl->bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | + USB_RECIP_DEVICE)) { + if (ctrl->bRequest == USB_REQ_SET_ADDRESS) { + goto handled_special_case; + } else if (ctrl->bRequest == + USB_REQ_SET_CONFIGURATION) { + /* FIXME: what to do for set configuration? */ + goto handled_special_case; + } + } + } + if (io_parameters->header.direction == + USBIF_IO_TRA_DIRECTION_OUT) { + pipe = USB_DIR_OUT; + } else { + pipe = USB_DIR_IN; + } + pipe |= (unsigned int)usbdev->devnum << 8; + pipe |= (unsigned int)io_parameters->header.endpoint << 15; + pipe |= pipe_type[io_parameters->header.io_tra_type] << 30; + if (io_parameters->header.io_tra_type == + USBIF_IO_TRA_TYPE_CONTROL) { + memcpy(rsrc->setup, io_parameters->control.setup, sizeof( + rsrc->setup)); + usb_fill_control_urb(urb, usbdev, pipe, rsrc->setup, + rsrc->request_element[0].mapping, + usbif_rbr_query_byte_count(&rsrc-> + request_element[0].rbr), + usbback_rsrc_end_io, rsrc); + } else if(io_parameters->header.io_tra_type == + USBIF_IO_TRA_TYPE_BULK) { + usb_fill_bulk_urb(urb, usbdev, pipe, + rsrc->request_element[0].mapping, + usbif_rbr_query_byte_count( + &rsrc->request_element[0].rbr), + usbback_rsrc_end_io, + rsrc); + } else if(io_parameters->header.io_tra_type == + USBIF_IO_TRA_TYPE_INTERRUPT) { + /* FIXME: hacking the interval like this is a bit unclean. */ + /* should probably convert back to exponential form for */ + /* high speed transfers and then pass the value into */ + /* fill_int_urb. */ + usb_fill_int_urb(urb, usbdev, pipe, + rsrc->request_element[0].mapping, + usbif_rbr_query_byte_count(&rsrc-> + request_element[0].rbr), + usbback_rsrc_end_io, rsrc, + 1 /* For the time being... */ ); + /* ...now set the real value. */ + urb->interval = io_parameters->interrupt.interval; + } else { + struct usbif_isochronous_io_schedule_element *schedule_element; + struct usbif_isochronous_io_schedule_element aligned_element; + int i; + /* FIXME: where's usb_fill_isoc_urb ?!? */ + spin_lock_init(&urb->lock); + urb->dev = usbdev; + urb->pipe = pipe; + urb->transfer_buffer = rsrc->request_element[0].mapping; + urb->transfer_buffer_length = usbif_rbr_query_byte_count( + &rsrc->request_element[0].rbr); + urb->complete = usbback_rsrc_end_io; + urb->context = rsrc; + urb->number_of_packets = + io_parameters->isochronous.packet_count; + urb->interval = io_parameters->isochronous.interval; + urb->start_frame = 0; + urb->transfer_flags |= URB_ISO_ASAP; + if (io_parameters->isochronous.packet_count > + USBIF_MAX_SCHEDULE_PACKET_COUNT) { + rsrc->tra_error = USBIF_ERROR_TOO_BIG; + goto schedule_error; + } + if (usbif_rbr_query_byte_count(&rsrc->request_element[1]. + rbr) < (io_parameters->isochronous.packet_count + * sizeof(struct + usbif_isochronous_io_schedule_element))) { + rsrc->tra_error = + USBIF_ERROR_INVALID_PARAMETER; + goto schedule_error; + } + schedule_element = rsrc->request_element[1].mapping; + for (i = 0; i < io_parameters->isochronous.packet_count; i++) { + memcpy(&aligned_element, schedule_element++, + sizeof(aligned_element)); + urb->iso_frame_desc[i].offset = aligned_element.offset; + urb->iso_frame_desc[i].length = aligned_element.length; + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = 0; + } + if (!check_iso_schedule(urb)) { + rsrc->tra_error = + USBIF_ERROR_INVALID_PARAMETER; + goto schedule_error; + } + } + if (io_parameters->header.flags & USBIF_IO_FLAGS_SHORT_NOT_OK) + urb->transfer_flags |= URB_SHORT_NOT_OK; + if (io_parameters->header.flags & USBIF_IO_FLAGS_ZERO_PACKET) + urb->transfer_flags |= URB_ZERO_PACKET; + rsrc->tra_error = usbif_error_map_local_to(usb_submit_urb( + urb, GFP_KERNEL)); + if (rsrc->tra_error != USBIF_ERROR_SUCCESS) { + printk(KERN_WARNING "URB %p submission failed.\n", urb); + goto urb_error; + } + spin_lock_irqsave(&rsrc->lock, flags); + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_us); + spin_unlock_irqrestore(&rsrc->lock, flags); + return; + urb_error: + schedule_error: + handled_special_case: + protocol_error: + spin_lock_irqsave(&rsrc->lock, flags); + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_un); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void usbback_rsrc_end_io(struct urb *urb, struct pt_regs *regs) +{ + struct usbback_rsrc *rsrc = urb->context; + unsigned long flags; + trace(); + rsrc->tra_error = usbif_error_map_local_to(urb->status); + if (rsrc->tra_error != USBIF_ERROR_SUCCESS) + printk(KERN_WARNING "URB %p failed. Status %d\n", urb, + urb->status); + rsrc->tra->status.io.length = urb->actual_length; + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + struct usbif_isochronous_io_schedule_element *schedule_element + = rsrc->request_element[1].mapping; + struct usbif_isochronous_io_schedule_element aligned_element; + int i; + for (i = 0; i < urb->number_of_packets; i++) { + aligned_element.length = urb->iso_frame_desc[i]. + actual_length; + aligned_element.error = usbif_error_map_local_to( + urb->iso_frame_desc[i].status); + memcpy(schedule_element++, &aligned_element, + sizeof(*schedule_element)); + } + } + spin_lock_irqsave(&rsrc->lock, flags); + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_uc); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void usbback_rsrc_unlink_urb(struct usbback_rsrc *rsrc) +{ + trace(); + usb_get_urb(rsrc->urb); + (void)usbback_work_schedule(&rsrc->unlink_urb_1_work); +} + +static void usbback_rsrc_unlink_urb_1(void *data) +{ + struct usbback_rsrc *rsrc = data; + unsigned long flags; + trace(); + usb_unlink_urb(rsrc->urb); + usb_put_urb(rsrc->urb); + spin_lock_irqsave(&rsrc->lock, flags); + usbback_rsrc_handle_stimulus(rsrc, + usbback_rsrc_stimulus_ul); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void usbback_rsrc_set_unlinked_error(struct usbback_rsrc *rsrc) +{ + trace(); + rsrc->tra_error = USBIF_ERROR_UNLINKED; +} + +static void usbback_rsrc_complete(struct usbback_rsrc *rsrc) +{ + trace(); + rsrc->completing_tra = rsrc->tra; + rsrc->tra = NULL; + if (rsrc->rbrs_mapped) { + rsrc->rbrs_mapped = 0; + usbback_rbr_mapper_pool_unmap_and_unreserve_rbrs( + &rsrc->rbr_mapper_request); + } else { + usbback_callback_success( + usbback_rbr_mapper_request_to_unmap_callback( + &rsrc->rbr_mapper_request)); + } +} + +static void usbback_rsrc_unmap_callback(struct usbback_callback * callback) +{ + struct usbback_rbr_mapper_request *request = + usbback_rbr_mapper_request_unmap_callback_to(callback); + struct usbback_rsrc *rsrc = container_of(request, struct usbback_rsrc, + rbr_mapper_request); + trace(); + usbback_gw_tra_complete(rsrc->completing_tra, rsrc->tra_error); + usbback_device_rsrc_completed_io(rsrc->device, rsrc); +} + +int usbback_rsrc_init(struct usbback_rsrc *rsrc, struct usbback_device *device) +{ + trace(); + rsrc->device = device; + INIT_LIST_HEAD(&rsrc->link); + spin_lock_init(&rsrc->lock); + rsrc->state = usbback_rsrc_state_i; + rsrc->tra = NULL; + usbback_rbr_mapper_element_init(&rsrc->request_element[0]); + usbback_rbr_mapper_element_init(&rsrc->request_element[1]); + usbback_rbr_mapper_request_init( + &rsrc->rbr_mapper_request, + usbback_rsrc_map_callback, + usbback_rsrc_unmap_callback); + rsrc->rbrs_mapped = 0; + usbback_callback_init(&rsrc->submit_urb_1_callback, + usbback_rsrc_submit_urb_1); + usbback_work_init(&rsrc->unlink_urb_1_work, + usbback_rsrc_unlink_urb_1, rsrc); + rsrc->urb = usb_alloc_urb(USBIF_MAX_SCHEDULE_PACKET_COUNT, GFP_KERNEL); + return (rsrc->urb != NULL) ? 0 : -ENOMEM; +} + +void usbback_rsrc_exit(struct usbback_rsrc *rsrc) +{ + trace(); + usb_free_urb(rsrc->urb); +} + +static void usbback_rsrc_handle_stimulus(struct usbback_rsrc *rsrc, +usbback_rsrc_stimulus stimulus) +{ + trace(); + switch (rsrc->state) { + case usbback_rsrc_state_i: + /* Idle */ + switch (stimulus) { + case usbback_rsrc_stimulus_st: + rsrc->state = usbback_rsrc_state_i_st; + usbback_rsrc_map_buffer(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st: + /* Handling request */ + /* map_buffer outstanding */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + rsrc->state = usbback_rsrc_state_i_st_tu; + usbback_rsrc_abort_map(rsrc); + break; + case usbback_rsrc_stimulus_ms: + rsrc->state = usbback_rsrc_state_i_st_ms; + usbback_rsrc_submit_urb(rsrc); + break; + case usbback_rsrc_stimulus_mf: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_tu: + /* Handling request */ + /* map_buffer outstanding */ + /* unlinking */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + break; + case usbback_rsrc_stimulus_ms: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_set_unlinked_error(rsrc); + usbback_rsrc_complete(rsrc); + break; + case usbback_rsrc_stimulus_mf: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus + (rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms: + /* Handling request */ + /* submit_urb outstanding */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + rsrc->state = usbback_rsrc_state_i_st_ms_tu; + break; + case usbback_rsrc_stimulus_us: + rsrc->state = usbback_rsrc_state_i_st_ms_us; + break; + case usbback_rsrc_stimulus_un: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + case usbback_rsrc_stimulus_uc: + rsrc->state = usbback_rsrc_state_i_st_ms_uc; + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms_tu: + /* Handling request */ + /* submit_urb outstanding */ + /* unlinking */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + break; + case usbback_rsrc_stimulus_us: + rsrc->state = usbback_rsrc_state_i_st_ms_tu_us; + usbback_rsrc_unlink_urb(rsrc); + break; + case usbback_rsrc_stimulus_un: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + case usbback_rsrc_stimulus_uc: + rsrc->state = usbback_rsrc_state_i_st_ms_uc; + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms_us: + /* Handling request */ + /* URB submitted */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + rsrc->state = usbback_rsrc_state_i_st_ms_tu_us; + usbback_rsrc_unlink_urb(rsrc); + break; + case usbback_rsrc_stimulus_uc: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms_uc: + /* Handling request */ + /* submit_urb outstanding */ + /* URB completed */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + break; + case usbback_rsrc_stimulus_us: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms_tu_us: + /* Handling request */ + /* URB submitted */ + /* unlink_urb outstanding */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + break; + case usbback_rsrc_stimulus_uc: + case usbback_rsrc_stimulus_ul: + rsrc->state = + usbback_rsrc_state_i_st_ms_tu_us_uc; + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case usbback_rsrc_state_i_st_ms_tu_us_uc: + /* Handling request */ + /* URB submitted or unlink_urb outstanding */ + switch (stimulus) { + case usbback_rsrc_stimulus_tu: + break; + case usbback_rsrc_stimulus_uc: + case usbback_rsrc_stimulus_ul: + rsrc->state = usbback_rsrc_state_i; + usbback_rsrc_complete(rsrc); + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + default: + usbback_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_resource.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_resource.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,75 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_RESOURCE_H +#define USBBACK_RESOURCE_H + +#include "usbback_gw.h" +#include "usbback_rbr_mapper_pool.h" + +int usbback_rsrc_class_init(void); + +void usbback_rsrc_class_exit(void); + +struct usbback_device; +struct usbback_rsrc; + +int +usbback_rsrc_init(struct usbback_rsrc *rsrc, struct usbback_device *device); + +void usbback_rsrc_exit(struct usbback_rsrc *rsrc); + +void +usbback_rsrc_start_io(struct usbback_rsrc *rsrc, struct usbback_gw_tra *tra); + +int usbback_rsrc_try_unlink(struct usbback_rsrc *rsrc, int unlink_id); + +void usbback_rsrc_flush_tra(struct usbback_rsrc *rsrc); + +typedef enum { + usbback_rsrc_state_i, + usbback_rsrc_state_i_st, + usbback_rsrc_state_i_st_tu, + usbback_rsrc_state_i_st_ms, + usbback_rsrc_state_i_st_ms_tu, + usbback_rsrc_state_i_st_ms_us, + usbback_rsrc_state_i_st_ms_uc, + usbback_rsrc_state_i_st_ms_tu_us, + usbback_rsrc_state_i_st_ms_tu_us_uc +} usbback_rsrc_state; + +struct usbback_rsrc { + struct usbback_device *device; + struct list_head link; + spinlock_t lock; + usbback_rsrc_state state; + usbif_error tra_error; + unsigned long tra_status_length; + struct usbback_gw_tra *tra; + struct usbback_gw_tra *completing_tra; + struct usbback_rbr_mapper_request rbr_mapper_request; + struct usbback_rbr_mapper_element request_element[2]; + int rbrs_mapped; + struct urb *urb; + u8 setup[8]; + struct usbback_callback submit_urb_1_callback; + struct usbback_work unlink_urb_1_work; +}; + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_ring_channel.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_ring_channel.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,760 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redisribute 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 disributed 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#include +#include "usbback_ring_channel.h" +#include "usbback_trace.h" + +static inline void +usbback_ring_channel_rsrc_init(struct usbback_ring_channel_rsrc *rsrc, +struct usbback_ring_channel *channel, usbback_callback_function *callback) +{ + trace(); + usbback_channel_ibmsg_init(&rsrc->message, callback); + rsrc->channel = channel; +} + +static inline struct list_head * +usbback_ring_channel_rsrc_to_link(struct usbback_ring_channel_rsrc *rsrc) +{ + return usbback_channel_ibmsg_to_link(&rsrc->message); +} + +static inline struct usbback_ring_channel_rsrc * +usbback_ring_channel_rsrc_callback_to(struct usbback_callback *callback) +{ + return container_of(usbback_channel_ibmsg_callback_to(callback), + struct usbback_ring_channel_rsrc, message); +} + +typedef enum { + usbback_ring_channel_stimulus_cn,/* connect request */ + usbback_ring_channel_stimulus_mq,/* message queued */ + usbback_ring_channel_stimulus_dn,/* disconnect request */ + usbback_ring_channel_stimulus_ri,/* ring interrupt */ + usbback_ring_channel_stimulus_cs,/* connect successful */ + usbback_ring_channel_stimulus_cf,/* connect failed */ + usbback_ring_channel_stimulus_cc,/* connect client completed */ + usbback_ring_channel_stimulus_dc,/* disconnect client completed */ + usbback_ring_channel_stimulus_ds,/* disconnect successful */ + usbback_ring_channel_stimulus_ks,/* kick send ring completed */ + usbback_ring_channel_stimulus_kr,/* kick recv ring completed */ + usbback_ring_channel_stimulus_rc,/* rsrc completed */ + usbback_ring_channel_stimulus_rf /* rsrc final completion */ +} usbback_ring_channel_stimulus; + +static void +usbback_ring_channel_handle_stimulus(struct usbback_ring_channel *channel, +usbback_ring_channel_stimulus stimulus); + +void +usbback_ring_channel_connect(struct usbback_ring_channel *channel, +struct usbback_ring_channel_connect_request *request) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = &request->callback; + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_submit_message(struct usbback_channel *base_channel, +struct usbback_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_ring_channel *channel = usbback_ring_channel_channel_to( + base_channel); + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(usbback_channel_obmsg_to_link(message), + &channel->message_list); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_mq); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void +usbback_ring_channel_disconnect(struct usbback_ring_channel *channel, +struct usbback_callback *callback) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = callback; + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static irqreturn_t +usbback_ring_channel_interrupt(int irq, void *context, struct pt_regs *ptregs) +{ + struct usbback_ring_channel *channel = context; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_ri); + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +static void +usbback_ring_channel_invalid_stimulus(struct usbback_ring_channel *channel, +usbback_ring_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbback: channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void +usbback_ring_channel_do_connect(struct usbback_ring_channel *channel) +{ + trace(); + (void)usbback_work_schedule(&channel->do_connect_1_work); +} + +static void usbback_ring_channel_do_connect_1(void *data) +{ + struct usbback_ring_channel *channel = data; + struct usbback_ring_channel_connect_request *request = + usbback_ring_channel_connect_request_callback_to( + channel->current_callback); + struct gnttab_map_grant_ref map_op; + struct gnttab_unmap_grant_ref unmap_op; + evtchn_op_t chn_op; + struct usbif_sring *sring; + unsigned long flags; + trace(); + memset(&map_op, 0, sizeof(map_op)); + map_op.host_addr = (unsigned long)channel->ring_area->addr; + map_op.flags = GNTMAP_host_map; + map_op.dom = request->domain_id; + map_op.ref = request->ring_ref; + lock_vm_area(channel->ring_area); + BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &map_op, 1)); + unlock_vm_area(channel->ring_area); + if (map_op.status != 0) { + trace_info("failed to map remote page"); + goto exit_no_mapping; + } + channel->ring_handle = map_op.handle; + /* FIXME: __ia64__ broken here */ + memset(&chn_op, 0, sizeof(chn_op)); + chn_op.cmd = EVTCHNOP_bind_interdomain; + chn_op.u.bind_interdomain.remote_dom = request->domain_id; + chn_op.u.bind_interdomain.remote_port = request->event_channel; + if (HYPERVISOR_event_channel_op(&chn_op) != 0) { + trace_info("failed to bind to remote event channel"); + goto exit_no_bind; + } + channel->event_channel = chn_op.u.bind_interdomain.local_port; + sring = (struct usbif_sring *)channel->ring_area->addr; + BACK_RING_INIT(&channel->back_ring, sring, PAGE_SIZE); + channel->irq = bind_evtchn_to_irqhandler( channel->event_channel, + usbback_ring_channel_interrupt, 0, "usbif-backend", + channel); + if (channel->irq < 0) { + trace_info("failed to bind remote irq"); + goto exit_no_irq; + } + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_cs); + spin_unlock_irqrestore(&channel->lock, flags); + return; + exit_no_irq: + memset(&chn_op, 0, sizeof(chn_op)); + chn_op.cmd = EVTCHNOP_close; + chn_op.u.close.port = channel->event_channel; + BUG_ON(HYPERVISOR_event_channel_op(&chn_op) != 0); + exit_no_bind: + memset(&unmap_op, 0, sizeof(unmap_op)); + unmap_op.host_addr = (unsigned long)channel->ring_area->addr; + unmap_op.handle = channel->ring_handle; + unmap_op.dev_bus_addr = 0; + lock_vm_area(channel->ring_area); + BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &unmap_op, + 1)); + unlock_vm_area(channel->ring_area); + exit_no_mapping: + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_cf); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_connect_client(struct usbback_ring_channel *channel) +{ + trace(); + (void)usbback_work_schedule(&channel->connect_client_1_work); +} + +static void usbback_ring_channel_connect_client_1(void *data) +{ + struct usbback_ring_channel *channel = data; + unsigned long flags; + trace(); + usbback_channel_connect(&channel->channel); + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_cc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_disconnect_client(struct usbback_ring_channel *channel) +{ + trace(); + (void)usbback_work_schedule(&channel->disconnect_client_1_work); +} + +static void usbback_ring_channel_disconnect_client_1(void *data) +{ + struct usbback_ring_channel *channel = data; + trace(); + usbback_channel_disconnect(&channel->channel, + &channel->disconnect_client_2_callback); +} + +static void +usbback_ring_channel_disconnect_client_2(struct usbback_callback *callback) +{ + struct usbback_ring_channel *channel = container_of(callback, + struct usbback_ring_channel, disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_dc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_do_disconnect(struct usbback_ring_channel *channel) +{ + trace(); + (void)usbback_work_schedule(&channel->do_disconnect_1_work); +} + +static void usbback_ring_channel_do_disconnect_1(void *data) +{ + struct usbback_ring_channel *channel = data; + struct gnttab_unmap_grant_ref op; + unsigned long flags; + trace(); + unbind_from_irqhandler(channel->irq, channel); + memset(&op, 0, sizeof(op)); + op.host_addr = (unsigned long)channel->ring_area->addr; + op.handle = channel->ring_handle; + op.dev_bus_addr = 0; + lock_vm_area(channel->ring_area); + BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1)); + unlock_vm_area(channel->ring_area); + spin_lock_irqsave(&channel->lock, flags); + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_ds); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_kick_recv_ring(struct usbback_ring_channel *channel) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!channel->recv_ring_kick_out) { + channel->recv_ring_kick_out = 1; + (void)usbback_work_schedule(&channel->kick_recv_ring_1_work); + } +} + +static void usbback_ring_channel_kick_recv_ring_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbback_ring_channel *channel = data; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + for (;;) { + struct usbif_back_ring *back_ring = &channel->back_ring; + int progress = 0; + RING_IDX rp, rc; + rc = back_ring->req_cons; + /* To avoid going idle when there is a waiting request we */ + /* need a mb() here to make sure we see any requests which */ + /* are new this time around the outer 'for' loop. */ + /* FIXME: spin_lock below makes this unnecessary? */ + mb(); + rp = back_ring->sring->req_prod; + /* To make sure we see the correct data for the requests */ + /* found above we need a rmb() here to force reads to be */ + /* after the read of back_ring->sring->req_prod. */ + rmb(); + while (!list_empty(&channel->rsrc_list) && (rc != rp) && + !RING_REQUEST_CONS_OVERFLOW(back_ring, rc)) { + struct usbback_ring_channel_rsrc *rsrc; + struct usbif_request *request; + rsrc = list_entry(channel->rsrc_list.next, + struct usbback_ring_channel_rsrc, + message.callback.work.link); + list_del_init(usbback_ring_channel_rsrc_to_link(rsrc)); + request = RING_GET_REQUEST(back_ring, rc); + rsrc->message.request = *request; + back_ring->req_cons = ++rc; + channel->rsrcs_out++; + spin_unlock_irqrestore(&channel->lock, flags); + usbback_channel_handle_message(&channel->channel, + &rsrc->message); + progress = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (!progress) + { + channel->recv_ring_kick_out = 0; + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_kr); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_kick_recv_ring_2(struct usbback_callback *callback) +{ + struct usbback_ring_channel_rsrc *rsrc = + usbback_ring_channel_rsrc_callback_to(callback); + struct usbback_ring_channel *channel = rsrc->channel; + unsigned long flags; + if ((usbback_callback_query_error(callback) != USBIF_ERROR_SUCCESS) && + (channel->protocol_error != NULL)) + channel->protocol_error(channel); + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(usbback_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + if (--channel->rsrcs_out == 0) { + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_rf); + } else { + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_rc); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_kick_send_ring(struct usbback_ring_channel *channel) +{ + if (!channel->send_ring_kick_out) { + channel->send_ring_kick_out = 1; + (void)usbback_work_schedule(&channel->kick_send_ring_1_work); + } +} + +static void usbback_ring_channel_kick_send_ring_1(void *data) +{ + struct usbback_ring_channel *channel = data; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + for (;;) { + struct usbif_back_ring *back_ring = &channel->back_ring; + int notify = 0; + while (!list_empty(&channel->message_list)) { + struct usbback_channel_obmsg *message; + struct usbif_response *response; + message = list_entry(channel->message_list.next, + struct usbback_channel_obmsg, + callback.work.link); + list_del_init(usbback_channel_obmsg_to_link(message)); + spin_unlock_irqrestore(&channel->lock, flags); + response = RING_GET_RESPONSE(back_ring, + back_ring->rsp_prod_pvt); + *response = message->response; + usbback_callback_success( + usbback_channel_obmsg_to_callback(message)); + back_ring->rsp_prod_pvt++; + notify = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (notify) { + spin_unlock_irqrestore(&channel->lock, flags); + RING_PUSH_RESPONSES(back_ring); + notify_remote_via_irq(channel->irq); + spin_lock_irqsave(&channel->lock, flags); + } else { + channel->send_ring_kick_out = 0; + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_ks); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_ring_channel_complete_current_callback( +struct usbback_ring_channel *channel) +{ + trace(); + usbback_callback_success(channel->current_callback); +} + +static void +usbback_ring_channel_fail_current_callback( +struct usbback_ring_channel *channel) +{ + trace(); + usbback_callback_complete(channel->current_callback, + USBIF_ERROR_FAILURE); +} + +static void +usbback_ring_channel_fail_out_messages(struct usbback_ring_channel *channel) +{ + trace(); + while (!list_empty(&channel->message_list)) { + struct usbback_channel_obmsg *message = list_entry( + channel->message_list.next, + struct usbback_channel_obmsg, + callback.work.link); + list_del_init(usbback_channel_obmsg_to_link(message)); + /* Message completion doesn't guarantee message delivery so */ + /* there is no need for an error code here. */ + usbback_callback_success(usbback_channel_obmsg_to_callback( + message)); + } +} + +static void +usbback_ring_channel_test_rsrcs(struct usbback_ring_channel *channel) +{ + trace(); + if (channel->rsrcs_out == 0) { + usbback_ring_channel_handle_stimulus(channel, + usbback_ring_channel_stimulus_rf); + } +} + +static int +usbback_ring_channel_init_or_exit(struct usbback_ring_channel *channel, +int exit) +{ + int return_value = 0, i; + trace(); + if (exit) + goto exit_path; + if ((channel->ring_area = alloc_vm_area(PAGE_SIZE)) == NULL) { + trace_info("failed to allocate ring area"); + return_value = -ENOMEM; + goto exit_no_ring_area; + } + spin_lock_init(&channel->lock); + channel->state = usbback_ring_channel_state_i; + INIT_LIST_HEAD(&channel->message_list); + usbback_work_init(&channel->do_connect_1_work, + usbback_ring_channel_do_connect_1, channel); + usbback_work_init(&channel->connect_client_1_work, + usbback_ring_channel_connect_client_1, channel); + usbback_work_init(&channel->disconnect_client_1_work, + usbback_ring_channel_disconnect_client_1, channel); + usbback_callback_init(&channel->disconnect_client_2_callback, + usbback_ring_channel_disconnect_client_2); + usbback_work_init(&channel->do_disconnect_1_work, + usbback_ring_channel_do_disconnect_1, channel); + usbback_work_init(&channel->kick_recv_ring_1_work, + usbback_ring_channel_kick_recv_ring_1, channel); + usbback_work_init(&channel->kick_send_ring_1_work, + usbback_ring_channel_kick_send_ring_1, channel); + INIT_LIST_HEAD(&channel->rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbback_ring_channel_rsrc *rsrc = &channel->rsrcs[i]; + usbback_ring_channel_rsrc_init(rsrc, channel, + usbback_ring_channel_kick_recv_ring_2); + list_add_tail(usbback_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + } + channel->recv_ring_kick_out = 0; + channel->send_ring_kick_out = 0; + channel->rsrcs_out = 0; + return 0; + exit_path: + free_vm_area(channel->ring_area); + exit_no_ring_area: + return return_value; +} + +int +usbback_ring_channel_init(struct usbback_ring_channel *channel, +void (*protocol_error) (struct usbback_ring_channel *channel)) +{ + trace(); + usbback_channel_init(&channel->channel, + usbback_ring_channel_submit_message); + channel->protocol_error = protocol_error; + return usbback_ring_channel_init_or_exit(channel, 0); +} + +void usbback_ring_channel_exit(struct usbback_ring_channel *channel) +{ + trace(); + (void)usbback_ring_channel_init_or_exit(channel, 1); +} + +static void usbback_ring_channel_handle_stimulus( +struct usbback_ring_channel *channel, usbback_ring_channel_stimulus stimulus) +{ + switch (channel->state) { + case usbback_ring_channel_state_i: + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* Ring disconnected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_cn: + channel->state = usbback_ring_channel_state_i_cn; + usbback_ring_channel_do_connect(channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn: + /* Interface connecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* do connect in progress. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_cs: + channel->state = usbback_ring_channel_state_i_cn_cs; + usbback_ring_channel_connect_client(channel); + break; + case usbback_ring_channel_stimulus_cf: + channel->state = usbback_ring_channel_state_i; + usbback_ring_channel_fail_current_callback(channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs: + /* Interface connecting. */ + /* Client connecting. */ + /* Maybe messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* Ring connected. */ + /* Connect client in progress */ + switch (stimulus) { + case usbback_ring_channel_stimulus_mq: + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_cc: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc; + usbback_ring_channel_complete_current_callback( + channel); + usbback_ring_channel_kick_send_ring(channel); + usbback_ring_channel_kick_recv_ring(channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc: + /* Interface connected. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Maybe kick send in progress. */ + /* Maybe kick recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Ring connected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_mq: + usbback_ring_channel_kick_send_ring(channel); + break; + case usbback_ring_channel_stimulus_dn: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc_dn; + usbback_ring_channel_kick_send_ring(channel); + usbback_ring_channel_kick_recv_ring(channel); + break; + case usbback_ring_channel_stimulus_ri: + usbback_ring_channel_kick_recv_ring(channel); + break; + case usbback_ring_channel_stimulus_ks: + case usbback_ring_channel_stimulus_kr: + break; + case usbback_ring_channel_stimulus_rc: + case usbback_ring_channel_stimulus_rf: + usbback_ring_channel_kick_recv_ring(channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc_dn: + /* Interface disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Kick send in progress. */ + /* Kick recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Ring connected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_mq: + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_ks: + case usbback_ring_channel_stimulus_kr: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc_dn_ks; + break; + case usbback_ring_channel_stimulus_rc: + case usbback_ring_channel_stimulus_rf: + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc_dn_ks: + /* Interface disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* One of kick send/recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Ring connected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_mq: + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_ks: + case usbback_ring_channel_stimulus_kr: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr; + usbback_ring_channel_disconnect_client(channel); + usbback_ring_channel_fail_out_messages(channel); + break; + case usbback_ring_channel_stimulus_rc: + case usbback_ring_channel_stimulus_rf: + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr: + /* Interface disconnecting. */ + /* Client disconnecting. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Maybe target rsrcs busy. */ + /* Ring connected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_mq: + usbback_ring_channel_fail_out_messages(channel); + break; + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_dc: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc; + usbback_ring_channel_test_rsrcs(channel); + break; + case usbback_ring_channel_stimulus_rc: + case usbback_ring_channel_stimulus_rf: + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc: + /* Interface disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Testing target rsrcs/ target rsrcs busy */ + /* Ring connected. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_ri: + case usbback_ring_channel_stimulus_rc: + break; + case usbback_ring_channel_stimulus_rf: + channel->state = + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf; + usbback_ring_channel_do_disconnect(channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf: + /* Interface disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Target rsrcs idle. */ + /* Disconnecting. */ + switch (stimulus) { + case usbback_ring_channel_stimulus_ri: + break; + case usbback_ring_channel_stimulus_ds: + channel->state = usbback_ring_channel_state_i; + usbback_ring_channel_complete_current_callback( + channel); + break; + default: + usbback_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + default: + usbback_ring_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_ring_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_ring_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,111 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_RING_CHANNEL_H +#define USBBACK_RING_CHANNEL_H + +#include +#include +#include +#include "usbback_channel.h" + +struct usbback_ring_channel; + +struct usbback_ring_channel_rsrc { + struct usbback_channel_ibmsg message; + struct usbback_ring_channel *channel; +}; + +typedef enum { + usbback_ring_channel_state_i, + usbback_ring_channel_state_i_cn, + usbback_ring_channel_state_i_cn_cs, + usbback_ring_channel_state_i_cn_cs_cc, + usbback_ring_channel_state_i_cn_cs_cc_dn, + usbback_ring_channel_state_i_cn_cs_cc_dn_ks, + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr, + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc, + usbback_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf +} usbback_ring_channel_state; + +struct usbback_ring_channel { + struct usbback_channel channel; + void (*protocol_error) (struct usbback_ring_channel *channel); + struct vm_struct *ring_area; + spinlock_t lock; + usbback_ring_channel_state state; + struct list_head message_list; + struct usbback_work do_connect_1_work; + struct usbback_work connect_client_1_work; + struct usbback_work disconnect_client_1_work; + struct usbback_callback disconnect_client_2_callback; + struct usbback_work do_disconnect_1_work; + struct usbback_work kick_recv_ring_1_work; + struct usbback_work kick_send_ring_1_work; + struct list_head rsrc_list; + struct usbback_ring_channel_rsrc rsrcs[USBIF_QUOTA]; + int recv_ring_kick_out; + int send_ring_kick_out; + int rsrcs_out; + struct usbback_callback *current_callback; + grant_handle_t ring_handle; + unsigned int event_channel; + int irq; + struct usbif_back_ring back_ring; +}; + +static inline struct usbback_channel * +usbback_ring_channel_to_channel(struct usbback_ring_channel *channel) +{ + return &channel->channel; +} + +static inline struct usbback_ring_channel * +usbback_ring_channel_channel_to(struct usbback_channel *channel) +{ + return container_of(channel, struct usbback_ring_channel, channel); +} + +int usbback_ring_channel_init(struct usbback_ring_channel *channel, +void (*protocol_error) (struct usbback_ring_channel *channel)); + +struct usbback_ring_channel_connect_request { + struct usbback_callback callback; + domid_t domain_id; + grant_ref_t ring_ref; + evtchn_port_t event_channel; +}; + +static inline struct usbback_ring_channel_connect_request * +usbback_ring_channel_connect_request_callback_to( +struct usbback_callback *callback) +{ + return container_of(callback, + struct usbback_ring_channel_connect_request,callback); +} + +void usbback_ring_channel_connect(struct usbback_ring_channel *channel, +struct usbback_ring_channel_connect_request *request); + +void usbback_ring_channel_disconnect(struct usbback_ring_channel *channel, +struct usbback_callback *callback); + +void usbback_ring_channel_exit(struct usbback_ring_channel *channel); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,36 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_TRACE_H +#define USBBACK_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_BACKEND_TRACE + +#define trace_info(format, ...) \ +printk(KERN_INFO "usbback %s: " format "\n", __FUNCTION__, ## __VA_ARGS__) +#define trace() trace_info("") +#else +#define trace_info(format, ...) +#define trace() +#endif + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_work.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_work.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbback_work.h" + +static void usbback_work_function(void *ignored); + +DEFINE_SPINLOCK(usbback_work_list_lock); +LIST_HEAD(usbback_work_list); +DECLARE_WORK(usbback_work_work, usbback_work_function, NULL); +DECLARE_WAIT_QUEUE_HEAD(usbback_work_waitqueue); +LIST_HEAD(usbback_work_condition); + +void usbback_work_wake_up(void) +{ + unsigned long flags; + spin_lock_irqsave(&usbback_work_list_lock, flags); + while (!list_empty(&usbback_work_condition)) + list_del_init(usbback_work_condition.next); + spin_unlock_irqrestore(&usbback_work_list_lock, flags); + wake_up(&usbback_work_waitqueue); +} + +int usbback_work_schedule(struct usbback_work *work) +{ + int scheduled = 0; + unsigned long flags; + spin_lock_irqsave(&usbback_work_list_lock, flags); + if (list_empty(&work->link)) { + list_add_tail(&work->link, &usbback_work_list); + scheduled = 1; + } + spin_unlock_irqrestore(&usbback_work_list_lock, flags); + if (scheduled) { + usbback_work_wake_up(); + schedule_work(&usbback_work_work); + } + return scheduled; +} + +static void usbback_work_function(void *ignored) +{ + unsigned long flags; + spin_lock_irqsave(&usbback_work_list_lock, flags); + while (!list_empty(&usbback_work_list)) { + struct usbback_work *work = list_entry(usbback_work_list.next, + struct usbback_work, + link); + list_del_init(&work->link); + spin_unlock_irqrestore(&usbback_work_list_lock, flags); + usbback_work_perform_synchronously(work); + spin_lock_irqsave(&usbback_work_list_lock, flags); + } + spin_unlock_irqrestore(&usbback_work_list_lock, flags); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_work.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_work.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,104 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_WORK_H +#define USBBACK_WORK_H + +#include +#include +#include +#include +#include + +struct usbback_work { + struct list_head link; + void (*function)(void *context); + void *context; +}; + +#define USBBACK_WORK_INIT( name, fn, ctx ) \ +{ .link = LIST_HEAD_INIT( name.link ), .function = fn, .context = ctx } + +#define USBBACK_WORK( name, fn, ctx ) \ +struct usbback_work name = USBBACK_WORK_INIT( name, fn, ctx ) + +static inline void +usbback_work_init(struct usbback_work *work, void (*function) (void *context), +void *context) +{ + INIT_LIST_HEAD(&work->link); + work->function = function; + work->context = context; +} + +static inline struct list_head *usbback_work_to_link(struct usbback_work *work) +{ + return &work->link; +} + +static inline struct usbback_work *usbback_work_link_to(struct list_head *link) +{ + return container_of(link, struct usbback_work, link); +} + +int usbback_work_schedule(struct usbback_work *work); + +static inline void +usbback_work_perform_synchronously(struct usbback_work *work) +{ + work->function(work->context); +} + +extern spinlock_t usbback_work_list_lock; +extern struct list_head usbback_work_list; +extern wait_queue_head_t usbback_work_waitqueue; +extern struct list_head usbback_work_condition; + +#define usbback_work_until( condition ) \ +do { \ + unsigned long flags; \ + spin_lock_irqsave(&usbback_work_list_lock, flags); \ + for (;;) { \ + struct list_head link; \ + \ + while (!list_empty(&usbback_work_list) && !(condition)) { \ + struct usbback_work *work; \ + work = list_entry(usbback_work_list.next, \ + struct usbback_work, link); \ + list_del_init(&work->link); \ + spin_unlock_irqrestore(&usbback_work_list_lock, \ + flags); \ + usbback_work_perform_synchronously(work); \ + spin_lock_irqsave(&usbback_work_list_lock, flags); \ + } \ + if (condition) \ + break; \ + INIT_LIST_HEAD(&link); \ + list_add_tail(&link, &usbback_work_condition); \ + spin_unlock_irqrestore(&usbback_work_list_lock, flags); \ + wait_event(usbback_work_waitqueue, list_empty(&link)); \ + spin_lock_irqsave(&usbback_work_list_lock, flags); \ + } \ + spin_unlock_irqrestore(&usbback_work_list_lock, flags); \ +} \ +while (0) + +void usbback_work_wake_up(void); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_xb_channel.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_xb_channel.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,333 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbback_xb_channel.h" +#include "usbback_trace.h" + +typedef enum { + usbback_xb_channel_stimulus_cn, + usbback_xb_channel_stimulus_dn, + usbback_xb_channel_stimulus_pe, /* Protocol error. */ + usbback_xb_channel_stimulus_fi, /* Frontend initialised. */ + usbback_xb_channel_stimulus_rs, + usbback_xb_channel_stimulus_rf +} usbback_xb_channel_stimulus; + +static void +usbback_xb_channel_handle_stimulus(struct usbback_xb_channel *channel, +usbback_xb_channel_stimulus stimulus); + +void usbback_xb_channel_connect(struct usbback_xb_channel *channel) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void +usbback_xb_channel_frontend_initialised(struct usbback_xb_channel *channel, +struct xenbus_device *dev) +{ + unsigned long ring_ref; + unsigned int event_channel; + unsigned long flags; + int error; + trace(); + error = xenbus_gather(XBT_NULL, dev->otherend, + "ring-ref", "%lu", &ring_ref, + "event-channel", "%u", &event_channel, NULL); + spin_lock_irqsave(&channel->lock, flags); + if(!error) { + channel->domain_id = dev->otherend_id; + channel->ring_ref = ring_ref; + channel->event_channel = event_channel; + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_fi); + } else { + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_pe); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +void usbback_xb_channel_frontend_changed(struct usbback_xb_channel *channel, +struct xenbus_device *dev, XenbusState state) +{ + trace(); + + switch(state) { + case XenbusStateUnknown: + trace_info( "XenbusStateUnknown" ); + break; + case XenbusStateInitialising: + trace_info( "XenbusStateInitialising" ); + break; + case XenbusStateInitWait: + trace_info( "XenbusStateInitWait" ); + break; + case XenbusStateInitialised: + trace_info( "XenbusStateInitialised" ); + break; + case XenbusStateConnected: + trace_info( "XenbusStateConnected" ); + break; + case XenbusStateClosing: + trace_info( "XenbusStateClosing" ); + break; + case XenbusStateClosed: + trace_info( "XenbusStateClosed" ); + break; + } + + switch(state) { + case XenbusStateInitialised: + usbback_xb_channel_frontend_initialised(channel, dev); + break; + case XenbusStateClosed: + device_unregister(&dev->dev); /*This is how blkback does it.*/ + break; + default: + break; + } +} + +static void +usbback_xb_channel_protocol_error(struct usbback_ring_channel *ring_channel) +{ + struct usbback_xb_channel *channel = + usbback_xb_channel_ring_channel_to(ring_channel); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_pe); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void usbback_xb_channel_disconnect(struct usbback_xb_channel *channel) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->disconnected = 0; + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); + usbback_work_until(channel->disconnected); +} + +static void +usbback_xb_channel_invalid_stimulus(struct usbback_xb_channel *channel, +usbback_xb_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbback: xb channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void usbback_xb_channel_connect_ring(struct usbback_xb_channel *channel) +{ + trace(); + channel->ring_connect.domain_id = channel->domain_id; + channel->ring_connect.ring_ref = channel->ring_ref; + channel->ring_connect.event_channel = channel->event_channel; + usbback_ring_channel_connect(&channel->channel, + &channel->ring_connect); +} + +static void +usbback_xb_channel_connect_ring_1(struct usbback_callback *callback) +{ + struct usbback_xb_channel *channel = container_of( + usbback_ring_channel_connect_request_callback_to(callback), + struct usbback_xb_channel, ring_connect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + if (usbback_callback_query_error(callback) == USBIF_ERROR_SUCCESS) { + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_rs); + } else { + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_rf); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_xb_channel_disconnect_ring(struct usbback_xb_channel *channel) +{ + trace(); + usbback_ring_channel_disconnect(&channel->channel, + &channel->ring_disconnect); +} + +static void +usbback_xb_channel_disconnect_ring_1(struct usbback_callback *callback) +{ + struct usbback_xb_channel *channel = container_of(callback, + struct usbback_xb_channel, ring_disconnect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbback_xb_channel_handle_stimulus(channel, + usbback_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbback_xb_channel_complete_disconnect(struct usbback_xb_channel *channel) +{ + trace(); + channel->disconnected = 1; + usbback_work_wake_up(); +} + +static int +usbback_xb_channel_init_or_exit(struct usbback_xb_channel *channel, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + if ((return_value = usbback_ring_channel_init(&channel->channel, + usbback_xb_channel_protocol_error)) != 0) { + goto exit_no_ring_channel; + } + spin_lock_init(&channel->lock); + channel->state = usbback_xb_channel_state_i; + usbback_callback_init(&channel->ring_connect.callback, + usbback_xb_channel_connect_ring_1); + usbback_callback_init(&channel->ring_disconnect, + usbback_xb_channel_disconnect_ring_1); + return 0; + exit_path: + usbback_ring_channel_exit(&channel->channel); + exit_no_ring_channel: + return return_value; +} + +int usbback_xb_channel_init(struct usbback_xb_channel *channel) +{ + trace(); + return usbback_xb_channel_init_or_exit(channel, 0); +} + +void usbback_xb_channel_exit(struct usbback_xb_channel *channel) +{ + trace(); + (void)usbback_xb_channel_init_or_exit(channel, 1); +} + +static void +usbback_xb_channel_handle_stimulus(struct usbback_xb_channel *channel, +usbback_xb_channel_stimulus stimulus) { + trace_info("xb channel %p in state %d received stimulus %d", channel, + channel->state, stimulus); + switch (channel->state) { + case usbback_xb_channel_state_i: + switch (stimulus) { + case usbback_xb_channel_stimulus_cn: + channel->state = usbback_xb_channel_state_i_cn; + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case usbback_xb_channel_state_i_cn: + switch (stimulus) { + case usbback_xb_channel_stimulus_dn: + channel->state = usbback_xb_channel_state_i; + usbback_xb_channel_complete_disconnect(channel); + break; + case usbback_xb_channel_stimulus_fi: + channel->state = usbback_xb_channel_state_i_cn_fi; + usbback_xb_channel_connect_ring(channel); + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case usbback_xb_channel_state_i_cn_fi: + switch (stimulus) { + case usbback_xb_channel_stimulus_dn: + channel->state = usbback_xb_channel_state_i_cn_fi_dn; + break; + case usbback_xb_channel_stimulus_rs: + channel->state = usbback_xb_channel_state_i_cn_fi_rs; + break; + case usbback_xb_channel_stimulus_rf: + channel->state = usbback_xb_channel_state_i_cn; + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case usbback_xb_channel_state_i_cn_fi_dn: + switch (stimulus) { + case usbback_xb_channel_stimulus_rs: + channel->state = + usbback_xb_channel_state_i_cn_fi_dn_rs; + usbback_xb_channel_disconnect_ring(channel); + break; + case usbback_xb_channel_stimulus_rf: + channel->state = usbback_xb_channel_state_i; + usbback_xb_channel_complete_disconnect(channel); + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case usbback_xb_channel_state_i_cn_fi_rs: + switch (stimulus) { + case usbback_xb_channel_stimulus_dn: + channel->state = + usbback_xb_channel_state_i_cn_fi_dn_rs; + usbback_xb_channel_disconnect_ring(channel); + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case usbback_xb_channel_state_i_cn_fi_dn_rs: + switch (stimulus) { + case usbback_xb_channel_stimulus_rs: + channel->state = usbback_xb_channel_state_i; + usbback_xb_channel_complete_disconnect(channel); + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + default: + usbback_xb_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_xb_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_xb_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,73 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_XB_CHANNEL_H +#define USBBACK_XB_CHANNEL_H + +#include +#include "usbback_ring_channel.h" + +typedef enum { + usbback_xb_channel_state_i, + usbback_xb_channel_state_i_cn, + usbback_xb_channel_state_i_cn_fi, + usbback_xb_channel_state_i_cn_fi_dn, + usbback_xb_channel_state_i_cn_fi_rs, + usbback_xb_channel_state_i_cn_fi_dn_rs, +} usbback_xb_channel_state; + +struct usbback_xb_channel { + struct usbback_ring_channel channel; + spinlock_t lock; + usbback_xb_channel_state state; + domid_t domain_id; + grant_ref_t ring_ref; + evtchn_port_t event_channel; + struct usbback_ring_channel_connect_request ring_connect; + struct usbback_callback ring_disconnect; + int disconnected; +}; + +static inline struct usbback_channel * +usbback_xb_channel_to_channel(struct usbback_xb_channel *channel) +{ + return usbback_ring_channel_to_channel(&channel->channel); +} + +static inline struct usbback_xb_channel * +usbback_xb_channel_ring_channel_to(struct usbback_ring_channel *ring_channel) +{ + return container_of(ring_channel, struct usbback_xb_channel, channel); +} + +static inline struct usbback_xb_channel * +usbback_xb_channel_channel_to(struct usbback_channel *channel) +{ + return usbback_xb_channel_ring_channel_to( + usbback_ring_channel_channel_to(channel)); +} + +void usbback_xb_channel_connect(struct usbback_xb_channel *channel); +void usbback_xb_channel_frontend_changed(struct usbback_xb_channel *channel, +struct xenbus_device *dev, XenbusState state); +void usbback_xb_channel_disconnect(struct usbback_xb_channel *channel); +int usbback_xb_channel_init(struct usbback_xb_channel *channel); +void usbback_xb_channel_exit(struct usbback_xb_channel *channel); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile Wed Mar 1 14:34:28 2006 @@ -0,0 +1,15 @@ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += usbfront.o + +usbfront-objs := \ +usbfront_buffer_resource_provider.o \ +usbfront_callback.o \ +usbfront_device.o \ +usbfront_driver.o \ +usbfront_gw.o \ +usbfront_gw_resource.o \ +usbfront_hcd_resource.o \ +usbfront_module.o \ +usbfront_rbr_provider_pool.o \ +usbfront_ring_channel.o \ +usbfront_work.o \ +usbfront_xb_channel.o diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_buffer_resource_provider.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_buffer_resource_provider.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,173 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/blkback/blkback.c */ +/* */ +/* original copyright notice follows... */ +/*****************************************************************************/ +/****************************************************************************** + * arch/xen/drivers/blkif/backend/main.c + * + * Back-end of the driver for virtual block devices. This portion of the + * driver exports a 'unified' block-device interface that can be accessed + * by any operating system that implements a compatible front end. A + * reference front-end implementation can be found in: + * arch/xen/drivers/blkif/frontend + * + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + * Copyright (c) 2005, Christopher Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbfront_buffer_resource_provider.h" +#include "usbfront_trace.h" + +struct usbfront_buffer_resource_provider { + spinlock_t lock; + struct usbfront_buffer_resource_list resources; + struct usbfront_buffer_resource_list free_resources; + grant_ref_t grant_ref_pool; +}; + +static int +alloc_or_free_grant_refs(struct usbfront_buffer_resource_provider *provider, +int free) +{ + int return_value = 0; + if (provider->resources.grant_references == 0) + return 0; + if (free) + goto exit_path; + return_value = gnttab_alloc_grant_references( + provider->resources.grant_references, + &provider->grant_ref_pool); + if (return_value != 0) + goto exit_no_grant_references; + provider->free_resources.grant_references = + provider->resources.grant_references; + return 0; + + exit_path: + gnttab_free_grant_references(provider->grant_ref_pool); + + exit_no_grant_references: + return return_value; +} + +static int +usbfront_buffer_resource_provider_init_or_exit( +struct usbfront_buffer_resource_provider *provider, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + spin_lock_init(&provider->lock); + provider->free_resources = usbfront_buffer_resource_list_null(); + if( ( return_value = alloc_or_free_grant_refs(provider, 0) ) != 0 ) + goto exit_no_grant_refs; + return 0; + exit_path: + alloc_or_free_grant_refs(provider, 1); + exit_no_grant_refs: + return return_value; +} + +struct usbfront_buffer_resource_provider * +usbfront_allocate_buffer_resource_provider( +struct usbfront_buffer_resource_list resources) +{ + struct usbfront_buffer_resource_provider *provider; + trace(); + provider = kmalloc(sizeof(struct usbfront_buffer_resource_provider), + GFP_KERNEL); + if (provider != NULL) { + provider->resources = resources; + if (usbfront_buffer_resource_provider_init_or_exit(provider, 0) + != 0) { + kfree(provider); + provider = NULL; + } + } + return provider; +} + +void usbfront_free_buffer_resource_provider( +struct usbfront_buffer_resource_provider *provider) +{ + trace(); + (void)usbfront_buffer_resource_provider_init_or_exit(provider, 1); + kfree(provider); +} + +struct usbfront_buffer_resource_list +usbfront_buffer_resource_provider_query_free_resources( +struct usbfront_buffer_resource_provider *provider) +{ + struct usbfront_buffer_resource_list list; + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + list = provider->free_resources; + spin_unlock_irqrestore(&provider->lock, flags); + return list; +} + +grant_ref_t +usbfront_buffer_resource_provider_allocate_grant_reference( +struct usbfront_buffer_resource_provider *provider) +{ + grant_ref_t reference; + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + provider->free_resources.grant_references--; + reference = gnttab_claim_grant_reference(&provider->grant_ref_pool); + spin_unlock_irqrestore(&provider->lock, flags); + return reference; +} + +void +usbfront_buffer_resource_provider_free_grant_reference( +struct usbfront_buffer_resource_provider *provider, grant_ref_t reference) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + gnttab_release_grant_reference(&provider->grant_ref_pool, reference); + provider->free_resources.grant_references++; + spin_unlock_irqrestore(&provider->lock, flags); +} + +void +usbfront_buffer_resource_list_trace(struct usbfront_buffer_resource_list list) +{ + trace(); + printk(KERN_INFO "usbfront %s: grant_references:%d\n", + __PRETTY_FUNCTION__, list.grant_references); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_buffer_resource_provider.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_buffer_resource_provider.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,78 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_BUFFER_RESOURCE_PROVIDER_H +#define USBFRONT_BUFFER_RESOURCE_PROVIDER_H + +#include +#include +#include + +struct usbfront_buffer_resource_list { + u32 grant_references; +}; + +static inline struct usbfront_buffer_resource_list +usbfront_buffer_resource_list_null(void) +{ + struct usbfront_buffer_resource_list list; + memset(&list, 0, sizeof(list)); + return list; +} + +static inline int +usbfront_buffer_resource_list_subset_of( +struct usbfront_buffer_resource_list *a, +struct usbfront_buffer_resource_list *b) +{ + return a->grant_references <= b->grant_references; +} + +static inline void +usbfront_buffer_resource_list_plus_equals( +struct usbfront_buffer_resource_list *a, +struct usbfront_buffer_resource_list *b) +{ + a->grant_references += b->grant_references; +} + +struct usbfront_buffer_resource_provider * +usbfront_allocate_buffer_resource_provider( +struct usbfront_buffer_resource_list resource_allocation); + +void +usbfront_free_buffer_resource_provider( +struct usbfront_buffer_resource_provider *provider); + +struct usbfront_buffer_resource_list +usbfront_buffer_resource_provider_query_free_resources( +struct usbfront_buffer_resource_provider *provider); + +grant_ref_t +usbfront_buffer_resource_provider_allocate_grant_reference( +struct usbfront_buffer_resource_provider *provider); + +void +usbfront_buffer_resource_provider_free_grant_reference( +struct usbfront_buffer_resource_provider *provider, grant_ref_t reference); + +void +usbfront_buffer_resource_list_trace(struct usbfront_buffer_resource_list list); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_callback.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_callback.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,39 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbfront_callback.h" + +void usbfront_callback_serialiser_function(void *context) +{ + struct usbfront_callback_serialiser *serialiser = context; + unsigned long flags; + spin_lock_irqsave(&serialiser->lock, flags); + while (!list_empty(&serialiser->list) && !serialiser->running) { + struct usbfront_callback *callback = + usbfront_callback_link_to(serialiser->list.next); + list_del_init(usbfront_callback_to_link(callback)); + serialiser->running = 1; + spin_unlock_irqrestore(&serialiser->lock, flags); + usbfront_callback_complete_synchronously(callback); + spin_lock_irqsave(&serialiser->lock, flags); + serialiser->running = 0; + } + spin_unlock_irqrestore(&serialiser->lock, flags); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_callback.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_callback.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,150 @@ +/*****************************************************************************/ +/* A callback object for use in scheduling completion of asynchronous */ +/* requests. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_CALLBACK_H +#define USBFRONT_CALLBACK_H + +#include +#include +#include "usbfront_work.h" + +static inline int usbif_error_map_to_local(usbif_error error) +{ + switch (error) { + case USBIF_ERROR_SUCCESS: + return 0; + case USBIF_ERROR_DISCONNECT: + return -ENOTCONN; + case USBIF_ERROR_INVALID_PROTOCOL: + return -EPROTO; + case USBIF_ERROR_INVALID_PARAMETER: + return -EINVAL; + case USBIF_ERROR_TOO_BIG: + return -E2BIG; + case USBIF_ERROR_NO_DEVICE: + return -ENODEV; + case USBIF_ERROR_UNLINKED: + return -ECONNRESET; + case USBIF_ERROR_PIPE: + return -EPIPE; + default: + return (int)error; + } +} + +struct usbfront_callback { + struct usbfront_work work; + usbif_error error; +}; + +typedef void usbfront_callback_function(struct usbfront_callback *callback); + +static inline void +usbfront_callback_init(struct usbfront_callback *callback, +usbfront_callback_function *function) +{ + usbfront_work_init(&callback->work, (void (*)(void *))function, + callback); + callback->error = USBIF_ERROR_SUCCESS; +} + +static inline struct list_head * +usbfront_callback_to_link(struct usbfront_callback *callback) +{ + return usbfront_work_to_link(&callback->work); +} + +static inline struct usbfront_callback * +usbfront_callback_link_to(struct list_head *link) +{ + return container_of(usbfront_work_link_to(link), + struct usbfront_callback, work); +} + +static inline void +usbfront_callback_complete(struct usbfront_callback *callback, +usbif_error error) +{ + callback->error = error; + usbfront_work_schedule(&callback->work); +} + +static inline void +usbfront_callback_success(struct usbfront_callback *callback) +{ + usbfront_callback_complete(callback, 0); +} + +static inline void +usbfront_callback_set_error(struct usbfront_callback *callback, +usbif_error error) +{ + callback->error = error; +} + +static inline void +usbfront_callback_complete_synchronously(struct usbfront_callback *callback) +{ + usbfront_work_perform_synchronously(&callback->work); +} + +static inline usbif_error +usbfront_callback_query_error(struct usbfront_callback *callback) +{ + return callback->error; +} + +struct usbfront_callback_serialiser { + spinlock_t lock; + struct list_head list; + struct usbfront_work work; + int running; +}; + +void usbfront_callback_serialiser_function(void *context); + +#define USBFRONT_CALLBACK_SERIALISER_INIT( name ) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .list = LIST_HEAD_INIT( name.list ), \ + .work = USBFRONT_WORK_INIT \ + ( name.work, usbfront_callback_serialiser_function, &name ),\ + .running = 0 \ +} + +#define USBFRONT_CALLBACK_SERIALISER( name ) \ +struct usbfront_callback_serialiser name = \ +USBFRONT_CALLBACK_SERIALISER_INIT( name ) + +static inline void +usbfront_callback_serialiser_complete_callback( +struct usbfront_callback_serialiser *serialiser, +struct usbfront_callback *callback, usbif_error error) +{ + unsigned long flags; + usbfront_callback_set_error(callback, error); + spin_lock_irqsave(&serialiser->lock, flags); + list_add_tail(usbfront_callback_to_link(callback), &serialiser->list); + spin_unlock_irqrestore(&serialiser->lock, flags); + usbfront_work_schedule(&serialiser->work); +} + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,159 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_CHANNEL_H +#define USBFRONT_CHANNEL_H + +#include +#include "usbfront_callback.h" + +struct usbfront_channel_ibmsg { + struct usbfront_callback callback; + struct usbif_response response; +}; + +static inline struct list_head * +usbfront_channel_ibmsg_to_link(struct usbfront_channel_ibmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct usbfront_callback * +usbfront_channel_ibmsg_to_callback(struct usbfront_channel_ibmsg *message) +{ + return &message->callback; +} + +static inline struct usbfront_channel_ibmsg * +usbfront_channel_ibmsg_callback_to(struct usbfront_callback *callback) +{ + return container_of(callback, struct usbfront_channel_ibmsg, callback); +} + +static inline void +usbfront_channel_ibmsg_init(struct usbfront_channel_ibmsg *message, +usbfront_callback_function *callback) +{ + usbfront_callback_init(usbfront_channel_ibmsg_to_callback(message), + callback); +} + +struct usbfront_channel_obmsg { + struct usbfront_callback callback; + struct usbif_request request; +}; + +static inline struct list_head * +usbfront_channel_obmsg_to_link(struct usbfront_channel_obmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct usbfront_callback * +usbfront_channel_obmsg_to_callback(struct usbfront_channel_obmsg *message) +{ + return &message->callback; +} + +static inline struct usbfront_channel_obmsg * +usbfront_channel_obmsg_callback_to(struct usbfront_callback *callback) +{ + return container_of(callback, struct usbfront_channel_obmsg, callback); +} + +static inline void +usbfront_channel_obmsg_init(struct usbfront_channel_obmsg *message, +usbfront_callback_function *callback) +{ + usbfront_callback_init(usbfront_channel_obmsg_to_callback(message), + callback); +} + +struct usbfront_channel; + +typedef void +usbfront_channel_submit_message_function(struct usbfront_channel *channel, +struct usbfront_channel_obmsg *message); + +typedef void +usbfront_channel_connect_function(void *client_context); + +typedef void +usbfront_channel_handle_message_function(void *client_context, +struct usbfront_channel_ibmsg *message); + +typedef void +usbfront_channel_disconnect_function(void *client_context, +struct usbfront_callback *callback); + +struct usbfront_channel { + usbfront_channel_submit_message_function *submit_message; + void *client_context; + usbfront_channel_connect_function *connect; + usbfront_channel_handle_message_function *handle_message; + usbfront_channel_disconnect_function *disconnect; +}; + +static inline void +usbfront_channel_init(struct usbfront_channel *channel, +usbfront_channel_submit_message_function *submit_message ) +{ + channel->submit_message = submit_message; +} + +static inline void usbfront_channel_connect(struct usbfront_channel *channel) +{ + channel->connect(channel->client_context); +} + +static inline void +usbfront_channel_handle_message(struct usbfront_channel *channel, +struct usbfront_channel_ibmsg *message) +{ + channel->handle_message(channel->client_context, message); +} + +static inline void +usbfront_channel_disconnect(struct usbfront_channel *channel, +struct usbfront_callback *callback) +{ + channel->disconnect(channel->client_context, callback); +} + +static inline void +usbfront_channel_install_client(struct usbfront_channel *channel, +void *client_context, usbfront_channel_connect_function *connect, +usbfront_channel_handle_message_function *handle_message, +usbfront_channel_disconnect_function *disconnect) +{ + channel->client_context = client_context; + channel->connect = connect; + channel->handle_message = handle_message; + channel->disconnect = disconnect; +} + +static inline void +usbfront_channel_submit_message(struct usbfront_channel *channel, +struct usbfront_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + channel->submit_message(channel, message); +} + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,768 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on arch/xen/drivers/usbif/frontend/main.c, original copyright */ +/* notice follows... */ +/*****************************************************************************/ + +/* + * 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, 2005 Mark Williamson + * + * Contact or + * regarding this code. + * + * Still to be (maybe) implemented: + * - migration / backend restart support? + * - support for building / using as a module + */ + +/* + * 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 + */ + +/* Xenbus code for blkif backend + Copyright (C) 2005 Rusty Russell + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include "usbfront_device.h" +#include "usbfront_driver.h" +#include "usbfront_xb_channel.h" +#include "usbfront_trace.h" + +typedef enum { + usbfront_device_state_i, + usbfront_device_state_i_cn, + usbfront_device_state_i_cn_dn, + usbfront_device_state_i_cn_ps, + usbfront_device_state_i_cn_pf, + usbfront_device_state_i_cn_dn_ps, + usbfront_device_state_i_cn_ps_dn, + usbfront_device_state_i_cn_ps_pc, + usbfront_device_state_i_cn_ps_dn_rc +} usbfront_device_state; + +typedef enum { + usbfront_device_stimulus_cn, /* Gateway connect. */ + usbfront_device_stimulus_dn, /* Gateway disconnect. */ + usbfront_device_stimulus_ps, /* Probe driver success. */ + usbfront_device_stimulus_pf, /* Probe driver failure. */ + usbfront_device_stimulus_rc, /* Remove driver complete. */ + usbfront_device_stimulus_pt, /* Polling tick. */ + usbfront_device_stimulus_pc /* Probe complete. */ +} usbfront_device_stimulus; + +struct usbfront_device { + struct xenbus_device *dev; + void *drvdata; + spinlock_t lock; + usbfront_device_state state; + struct usbfront_xb_channel channel; + struct usbfront_gw gw; + struct usbfront_callback *gw_disconnect_callback; + int port_probe_count; + struct usbfront_gw_tra probe; + struct usb_port_status port_status; + struct usbfront_work probe_driver_1_work; + struct usbfront_work remove_driver_1_work; +}; + +static void +usbfront_device_handle_stimulus(struct usbfront_device *device, +usbfront_device_stimulus stimulus); + +static inline struct usbfront_device * +usbfront_device_gw_to(struct usbfront_gw *gw) +{ + return container_of(gw, struct usbfront_device, gw); +} + +void usbfront_device_set_drvdata(struct usbfront_device *device, void *data) +{ + device->drvdata = data; +} + +void *usbfront_device_get_drvdata(struct usbfront_device *device) +{ + return device->drvdata; +} + +struct device *usbfront_device_to_dev(struct usbfront_device *device) +{ + return &device->dev->dev; +} + +struct usbfront_device *usbfront_device_dev_to(struct device *dev) +{ + return to_xenbus_device(dev)->data; +} + +int usbfront_device_query_port_status_changed(struct usbfront_device *device) +{ + int changed; + unsigned long flags; + spin_lock_irqsave(&device->lock, flags); + usbfront_device_handle_stimulus(device, usbfront_device_stimulus_pt); + changed = (device->port_status.wPortChange != 0); + spin_unlock_irqrestore(&device->lock, flags); + return changed; +} + +struct usb_port_status +usbfront_device_query_port_status(struct usbfront_device *device) +{ + struct usb_port_status port_status; + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + port_status = device->port_status; + spin_unlock_irqrestore(&device->lock, flags); + return port_status; +} + +void usbfront_device_set_port_power(struct usbfront_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->port_status.wPortStatus |= USB_PORT_STAT_POWER; + spin_unlock_irqrestore(&device->lock, flags); +} + +void usbfront_device_set_port_reset(struct usbfront_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->port_status.wPortStatus |= USB_PORT_STAT_RESET; + device->port_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + usbfront_device_handle_stimulus(device, usbfront_device_stimulus_pt); + spin_unlock_irqrestore(&device->lock, flags); +} + +void usbfront_device_clear_port_enable(struct usbfront_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->port_status.wPortStatus &= ~USB_PORT_STAT_ENABLE; + spin_unlock_irqrestore(&device->lock, flags); +} + +void +usbfront_device_clear_port_connection_change(struct usbfront_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->port_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION; + spin_unlock_irqrestore(&device->lock, flags); +} + +void usbfront_device_clear_port_reset_change(struct usbfront_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->port_status.wPortChange &= ~USB_PORT_STAT_C_RESET; + spin_unlock_irqrestore(&device->lock, flags); +} + +domid_t usbfront_device_query_domain(struct usbfront_device *device) +{ + trace(); + return device->dev->otherend_id; +} + +void usbfront_device_submit_tra(struct usbfront_device *device, +struct usbfront_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + usbfront_gw_submit_tra(&device->gw, tra); +} + +static void usbfront_device_gw_connect(struct usbfront_gw *gw) +{ + struct usbfront_device *device = usbfront_device_gw_to(gw); + unsigned long flags; + trace(); + /* Between connect and completion of the disconnect callback we are */ + /* allowed to issue tras. */ + spin_lock_irqsave(&device->lock, flags); + usbfront_device_handle_stimulus(device, usbfront_device_stimulus_cn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void usbfront_device_gw_disconnect(struct usbfront_gw *gw, +struct usbfront_callback *callback) +{ + struct usbfront_device *device = usbfront_device_gw_to(gw); + unsigned long flags; + trace(); + /* We must stop issuing tras and complete the callback once */ + /* all of the tras we are issuing have completed or failed */ + /* back to us. */ + spin_lock_irqsave(&device->lock, flags); + device->gw_disconnect_callback = callback; + usbfront_device_handle_stimulus(device, usbfront_device_stimulus_dn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void usbfront_device_invalid_stimulus(struct usbfront_device *device, +usbfront_device_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: device %p in state %d received invalid " + "stimulus %d\n", device, device->state, stimulus); +} + +static void usbfront_device_probe_driver(struct usbfront_device *device) +{ + trace(); + (void)usbfront_work_schedule(&device->probe_driver_1_work); +} + +static void usbfront_device_probe_driver_1(void *data) +{ + struct usbfront_device *device = data; + usbfront_device_stimulus stimulus; + unsigned long flags; + trace(); + if (usbfront_driver_probe(device) == 0) { + stimulus = usbfront_device_stimulus_ps; + } else { + stimulus = usbfront_device_stimulus_pf; + } + spin_lock_irqsave(&device->lock, flags); + usbfront_device_handle_stimulus(device, stimulus); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void usbfront_device_remove_driver(struct usbfront_device *device) +{ + trace(); + (void)usbfront_work_schedule(&device->remove_driver_1_work); +} + +static void usbfront_device_remove_driver_1(void *data) +{ + struct usbfront_device *device = data; + unsigned long flags; + trace(); + usbfront_driver_remove(device); + spin_lock_irqsave(&device->lock, flags); + usbfront_device_handle_stimulus(device, usbfront_device_stimulus_rc); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void usbfront_device_probe_all_ports(struct usbfront_device *device) +{ + device->port_probe_count = 1; + if ((device->port_status.wPortStatus & USB_PORT_STAT_RESET) != + USB_PORT_STAT_RESET) { + device->probe.parameters.header.tra_type = + USBIF_TRA_TYPE_PROBE; + } else { + device->probe.parameters.header.tra_type = + USBIF_TRA_TYPE_RESET; + } + usbfront_gw_submit_tra(&device->gw, &device->probe); +} + +static void +usbfront_device_probe_all_ports_1(struct usbfront_callback *callback) +{ + struct usbfront_device *device = container_of( + usbfront_gw_tra_callback_to(callback), + struct usbfront_device, probe); + unsigned long flags; + spin_lock_irqsave(&device->lock, flags); + if (device->probe.parameters.header.tra_type == + USBIF_TRA_TYPE_PROBE) { + if (usbfront_callback_query_error(callback) == + USBIF_ERROR_SUCCESS) { + struct usb_port_status *port_status = + &device->port_status; + if (device->probe.status.probe.result == + USBIF_PROBE_RESULT_DEVICE_PRESENT) { + /* There is a device attached to the port. */ + if ((port_status->wPortStatus & + USB_PORT_STAT_CONNECTION) != + USB_PORT_STAT_CONNECTION) { + /* There wasn't a device attached to */ + /* the port before. */ + port_status->wPortStatus |= + USB_PORT_STAT_CONNECTION; + port_status->wPortChange |= + USB_PORT_STAT_C_CONNECTION; + } + } else if (device->probe.status.probe.result == + USBIF_PROBE_RESULT_NO_DEVICE) { + /* There isn't a device attached to the port.*/ + if ((port_status->wPortStatus & + USB_PORT_STAT_CONNECTION) == + USB_PORT_STAT_CONNECTION) { + /* There was a device attached to */ + /* the port before. */ + port_status->wPortStatus &= + ~USB_PORT_STAT_CONNECTION; + port_status->wPortChange |= + USB_PORT_STAT_C_CONNECTION; + } + } else { + trace_info("device %p: unexpected result %d " + "probing port", device, + (int)device->probe.status.probe. + result); + } + } else { + trace_info("device %p: error %d probing port", + device, usbif_error_map_to_local( + usbfront_callback_query_error(callback))); + } + } else { + struct usb_port_status *port_status = &device->port_status; + if ((usbfront_callback_query_error(callback) == + USBIF_ERROR_SUCCESS) && + ((device->probe.status.reset.result == + USBIF_RESET_RESULT_FULL_SPEED) || + (device->probe.status.reset.result == + USBIF_RESET_RESULT_LOW_SPEED) || + (device->probe.status.reset.result == + USBIF_RESET_RESULT_HIGH_SPEED))) { + if (device->probe.status.reset.result == + USBIF_RESET_RESULT_LOW_SPEED) { + port_status->wPortStatus |= + USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus &= + ~USB_PORT_STAT_HIGH_SPEED; + } else if (device->probe.status.reset.result == + USBIF_RESET_RESULT_HIGH_SPEED) { + port_status->wPortStatus &= + ~USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus |= + USB_PORT_STAT_HIGH_SPEED; + } else { + port_status->wPortStatus &= + ~USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus &= + ~USB_PORT_STAT_HIGH_SPEED; + } + port_status->wPortStatus &= ~USB_PORT_STAT_RESET; + port_status->wPortStatus |= USB_PORT_STAT_ENABLE; + port_status->wPortChange |= USB_PORT_STAT_C_RESET; + } else { + if (usbfront_callback_query_error(callback) != + USBIF_ERROR_SUCCESS) { + printk(KERN_ERR "usbfront: device %p: error %d" + " resetting port", device, + usbif_error_map_to_local( + usbfront_callback_query_error( + callback))); + } else { + trace_info("device %p: unexpected result %d" + " resetting port", device, + (int)device->probe.status.probe.result); + } + port_status->wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus &= ~USB_PORT_STAT_HIGH_SPEED; + port_status->wPortStatus &= ~USB_PORT_STAT_RESET; + port_status->wPortChange |= USB_PORT_STAT_C_RESET; + } + } + + if (--device->port_probe_count == 0) + usbfront_device_handle_stimulus(device, + usbfront_device_stimulus_pc); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +usbfront_device_disconnect_all_ports(struct usbfront_device *device) +{ + struct usb_port_status *port_status = &device->port_status; + trace(); + if ((port_status->wPortStatus & USB_PORT_STAT_CONNECTION) == + USB_PORT_STAT_CONNECTION) { + /* There was a device attached to the port before. */ + port_status->wPortStatus &= ~USB_PORT_STAT_CONNECTION; + port_status->wPortChange |= USB_PORT_STAT_C_CONNECTION; + } +} + +static void +usbfront_device_complete_gw_disconnect(struct usbfront_device *device) +{ + trace(); + usbfront_callback_success(device->gw_disconnect_callback); +} + +static int +usbfront_device_resume_or_suspend(struct usbfront_device *device, int suspend) +{ + trace(); + if (suspend) + goto suspend_path; + usbfront_xb_channel_connect(&device->channel, device->dev); + return 0; + suspend_path: + usbfront_xb_channel_disconnect(&device->channel); + return 0; +} + +static int usbfront_device_init_or_exit(struct usbfront_device *device, +struct xenbus_device *dev, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + device->dev = dev; + spin_lock_init(&device->lock); + device->state = usbfront_device_state_i; + usbfront_gw_tra_init(&device->probe, + usbfront_device_probe_all_ports_1); + usbfront_work_init(&device->probe_driver_1_work, + usbfront_device_probe_driver_1, device); + usbfront_work_init(&device->remove_driver_1_work, + usbfront_device_remove_driver_1, device); + return_value = usbfront_xb_channel_init(&device->channel); + if (return_value != 0) + goto exit_no_channel; + return_value = usbfront_gw_init(&device->gw, + usbfront_xb_channel_to_channel(&device->channel), + usbfront_device_gw_connect, + usbfront_device_gw_disconnect, + USBIF_QUOTA); + if (return_value != 0) + goto exit_no_gw; + return_value = usbfront_device_resume_or_suspend(device, 0); + if (return_value != 0) + goto exit_no_resume; + return 0; + exit_path: + (void)usbfront_device_resume_or_suspend(device, 1); + exit_no_resume: + usbfront_gw_exit(&device->gw); + exit_no_gw: + usbfront_xb_channel_exit(&device->channel); + exit_no_channel: + return return_value; +} + +static int +usbfront_device_init(struct usbfront_device *device, struct xenbus_device *dev) +{ + trace(); + return usbfront_device_init_or_exit(device, dev, 0); +} + +static void usbfront_device_exit(struct usbfront_device *device) +{ + trace(); + (void)usbfront_device_init_or_exit(device, NULL, 1); +} + +static int +usbfront_device_probe_or_remove(struct xenbus_device *dev, int remove) +{ + int return_value = 0; + struct usbfront_device *device; + trace(); + if (remove) + goto remove_path; + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (device == NULL) { + xenbus_dev_error(dev, -ENOMEM,"allocating FE structure"); + return_value = -ENOMEM; + goto exit_no_device; + } + dev->data = device; + return_value = usbfront_device_init(device, dev); + if (return_value != 0) + goto exit_init_failed; + return 0; + remove_path: + device = dev->data; + usbfront_device_exit(device); + exit_init_failed: + dev->data = NULL; + kfree(device); + exit_no_device: + return return_value; +} + +static int usbfront_device_probe(struct xenbus_device *dev, +const struct xenbus_device_id *id) +{ + trace(); + return usbfront_device_probe_or_remove(dev, 0); +} + +static int usbfront_device_remove(struct xenbus_device *dev) +{ + trace(); + return usbfront_device_probe_or_remove(dev, 1); +} + +static int usbfront_device_resume(struct xenbus_device *dev) +{ + struct usbfront_device *device = dev->data; + trace(); + (void)usbfront_device_resume_or_suspend(device, 1); + return usbfront_device_resume_or_suspend(device, 0); +} + +static void +usbfront_device_backend_changed(struct xenbus_device *dev, XenbusState state) +{ + struct usbfront_device *device = dev->data; + trace(); + usbfront_xb_channel_backend_changed(&device->channel, dev, state); +} + +static struct xenbus_device_id usbfront_device_ids[] = { + {"usb"}, + {""} +}; + +static struct xenbus_driver usbfront_device_driver = { + .name = "usb", + .owner = THIS_MODULE, + .ids = usbfront_device_ids, + .probe = usbfront_device_probe, + .remove = usbfront_device_remove, + .resume = usbfront_device_resume, + .otherend_changed = usbfront_device_backend_changed, +}; + +int usbfront_device_class_init(void) +{ + trace(); + return xenbus_register_frontend(&usbfront_device_driver); +} + +void usbfront_device_class_exit(void) +{ + trace(); + xenbus_unregister_driver(&usbfront_device_driver); +} + +static void usbfront_device_handle_stimulus(struct usbfront_device *device, +usbfront_device_stimulus stimulus) +{ + switch (device->state) { + case usbfront_device_state_i: + switch (stimulus) { + case usbfront_device_stimulus_cn: + device->state = usbfront_device_state_i_cn; + usbfront_device_probe_driver(device); + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn: + /* Probing driver. */ + switch (stimulus) { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_dn; + break; + case usbfront_device_stimulus_ps: + device->state = usbfront_device_state_i_cn_ps; + usbfront_device_probe_all_ports(device); + break; + case usbfront_device_stimulus_pf: + device->state = usbfront_device_state_i_cn_pf; + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_dn: + /* Probing driver. */ + /* Gateway disconnecting. */ + switch (stimulus) { + case usbfront_device_stimulus_ps: + device->state = usbfront_device_state_i_cn_dn_ps; + usbfront_device_remove_driver(device); + break; + case usbfront_device_stimulus_pf: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports(device); + usbfront_device_complete_gw_disconnect(device); + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_ps: + /* Driver Probed. */ + /* Polling ports. */ + switch (stimulus) { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_ps_dn; + usbfront_device_remove_driver(device); + break; + case usbfront_device_stimulus_pt: + break; + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i_cn_ps_pc; + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_pf: + /* Driver probe failed. */ + switch (stimulus) { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports(device); + usbfront_device_complete_gw_disconnect(device); + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_dn_ps: + /* Gateway disconnecting. */ + /* Removing driver. */ + switch (stimulus) { + case usbfront_device_stimulus_rc: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports(device); + usbfront_device_complete_gw_disconnect(device); + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_ps_dn: + /* Gateway disconnecting. */ + /* Removing driver. */ + /* Polling ports. */ + switch (stimulus) { + case usbfront_device_stimulus_rc: + device->state = usbfront_device_state_i_cn_ps_dn_rc; + break; + case usbfront_device_stimulus_pt: + break; + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i_cn_dn_ps; + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_ps_pc: + /* Driver Probed. */ + switch (stimulus) { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_dn_ps; + usbfront_device_remove_driver(device); + break; + case usbfront_device_stimulus_pt: + device->state = usbfront_device_state_i_cn_ps; + usbfront_device_probe_all_ports(device); + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + case usbfront_device_state_i_cn_ps_dn_rc: + /* Gateway disconnecting. */ + /* Polling ports. */ + switch (stimulus) { + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports(device); + usbfront_device_complete_gw_disconnect(device); + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } + break; + default: + usbfront_device_invalid_stimulus(device, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_DEVICE_H +#define USBFRONT_DEVICE_H + +#include +#include +#include +#include "../../usb/core/hcd.h" +#include "usbfront_gw.h" + +int usbfront_device_class_init(void); +void usbfront_device_class_exit(void); + +struct usbfront_device; + +void usbfront_device_set_drvdata(struct usbfront_device *device, void *data); +void *usbfront_device_get_drvdata(struct usbfront_device *device); +struct device *usbfront_device_to_dev(struct usbfront_device *device); +struct usbfront_device *usbfront_device_dev_to(struct device *dev); +int usbfront_device_query_port_status_changed(struct usbfront_device *device); +struct usb_port_status +usbfront_device_query_port_status(struct usbfront_device *device); +void usbfront_device_set_port_power(struct usbfront_device *device); +void usbfront_device_set_port_reset(struct usbfront_device *device); +void usbfront_device_clear_port_enable(struct usbfront_device *device); +void +usbfront_device_clear_port_connection_change(struct usbfront_device *device); +void usbfront_device_clear_port_reset_change(struct usbfront_device *device); +domid_t usbfront_device_query_domain(struct usbfront_device *device); +void usbfront_device_submit_tra(struct usbfront_device *device, +struct usbfront_gw_tra *tra); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,569 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/usbif/frontend/main.c */ +/* pristine-linux-2.6.11/drivers/usb/host/uhci-hcd.c */ +/* pristine-linux-2.6.11/drivers/usb/core/hcd-pci.c */ +/* */ +/* original copyright notices follow... */ +/*****************************************************************************/ + +/* + * 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, 2005 Mark Williamson + * + * Contact or + * regarding this code. + * + * Still to be (maybe) implemented: + * - migration / backend restart support? + * - support for building / using as a module + */ + +/* + * 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 + */ + +/* + * Universal Host Controller Interface driver for USB. + * + * Maintainer: Alan Stern + * + * (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) + * (C) Copyright 2004 Alan Stern, stern@xxxxxxxxxxxxxxxxxxx + * + * 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 + */ + +/* + * (C) Copyright David Brownell 2000-2002 + * + * 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 "usbfront_driver.h" +#include "usbfront_hcd_resource.h" +#include "usbfront_sll.h" +#include "usbfront_trace.h" + +typedef enum { + usbfront_hcd_state_i +} usbfront_hcd_state; + +typedef enum { + usbfront_hcd_stimulus_uq /* urb enqueue */ +} usbfront_hcd_stimulus; + +struct usbfront_hcd { + u64 dma_mask; + spinlock_t lock; + usbfront_hcd_state state; + struct usbfront_sll urb_sll; + struct list_head free_resource_list; + struct usbfront_hcd_resource resources[USBIF_QUOTA]; +}; + +static void +usbfront_hcd_handle_stimulus(struct usbfront_hcd *uhcd, +usbfront_hcd_stimulus stimulus); + +static struct usbfront_hcd *usbfront_hcd_hcd_to_uhcd(struct usb_hcd *hcd) +{ + return (struct usbfront_hcd *)(hcd->hcd_priv); +} + +static struct usbfront_device * +usbfront_hcd_hcd_to_usbfront_device(struct usb_hcd *hcd) +{ + return usbfront_device_dev_to(hcd_to_bus(hcd)->controller); +} + +void usbfront_hcd_resource_completed(struct usbfront_callback *callback); + +static int usbfront_hcd_start(struct usb_hcd *hcd) +{ + int return_value = 0; + struct usbfront_hcd *uhcd = usbfront_hcd_hcd_to_uhcd(hcd); + int i; + trace(); + memset(uhcd, 0, sizeof(*uhcd)); +#if 0 + /* FIXME: This bit of dma_mask manipulation is to trick the USB hcd */ + /* code into giving us buffers allocated using dma_alloc_coherent. */ + /* Which is necessary to allow the BE to map them using */ + /* dma_map_single. This is unclean in so many different ways. */ + + uhcd->dma_mask = 0xffffffffffffffffULL; + { + struct device *dev = usbfront_device_to_dev( + usbfront_hcd_hcd_to_usbfront_device(hcd)); + dev->dma_mask = &uhcd->dma_mask; + dev->coherent_dma_mask = uhcd->dma_mask; + } +#endif + spin_lock_init(&uhcd->lock); + uhcd->state = usbfront_hcd_state_i; + usbfront_sll_init(&uhcd->urb_sll); + INIT_LIST_HEAD(&uhcd->free_resource_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbfront_hcd_resource *resource = &uhcd->resources[i]; + return_value = usbfront_hcd_resource_init(resource, + usbfront_hcd_hcd_to_usbfront_device(hcd), + i, usbfront_hcd_resource_completed); + if (return_value != 0) { + trace_info("usbfront_hcd_resource_init failed"); + goto exit_no_resource; + } + list_add(usbfront_hcd_resource_to_link(resource), + &uhcd->free_resource_list); + } + hcd->state = HC_STATE_RUNNING; /* FIXME: breaks encapsulation */ + return 0; + exit_no_resource: + while (!list_empty(&uhcd->free_resource_list)) { + struct usbfront_hcd_resource *resource = list_entry( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + callback.work.link); + list_del_init(usbfront_hcd_resource_to_link(resource)); + usbfront_hcd_resource_exit(resource); + } + return return_value; +} + +static void usbfront_hcd_stop(struct usb_hcd *hcd) +{ + struct usbfront_hcd *uhcd = usbfront_hcd_hcd_to_uhcd(hcd); + trace(); + while (!list_empty(&uhcd->free_resource_list)) { + struct usbfront_hcd_resource *resource = list_entry( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + callback.work.link); + list_del_init(usbfront_hcd_resource_to_link(resource)); + usbfront_hcd_resource_exit(resource); + } +} + +static int usbfront_hcd_get_frame_number(struct usb_hcd *hcd) +{ + trace(); + return 0; +} + +static int +usbfront_hcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, +struct urb *urb, gfp_t mem_flags) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_hcd *uhcd = usbfront_hcd_hcd_to_uhcd(hcd); + unsigned long flags; + trace(); + spin_lock_irqsave(&uhcd->lock, flags); + usbfront_slk_init((struct usbfront_slk *)&urb->hcpriv); + usbfront_sll_add_last(&uhcd->urb_sll, (struct usbfront_slk *)&urb-> + hcpriv); + usbfront_hcd_handle_stimulus(uhcd, usbfront_hcd_stimulus_uq); + spin_unlock_irqrestore(&uhcd->lock, flags); + return 0; +} + +static int usbfront_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + int dequeued; + struct usbfront_hcd *uhcd = usbfront_hcd_hcd_to_uhcd(hcd); + unsigned long flags; + trace(); + spin_lock_irqsave(&uhcd->lock, flags); + dequeued = usbfront_sll_remove_slk(&uhcd->urb_sll, + (struct usbfront_slk *)&urb->hcpriv); + spin_unlock_irqrestore(&uhcd->lock, flags); + if (dequeued) { + urb->hcpriv = 0; + urb->actual_length = 0; + local_irq_save(flags); + usb_hcd_giveback_urb(hcd, urb, 0); + local_irq_restore(flags); + } else { + usbfront_hcd_resource_dequeue_urb(urb); + } + return 0; +} + +static void +usbfront_hcd_endpoint_disable(struct usb_hcd *hcd, +struct usb_host_endpoint *ep) +{ + trace(); +} + +static int usbfront_hcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + int changed = 0; + struct usbfront_device *device = usbfront_hcd_hcd_to_usbfront_device( + hcd); + *buf = 0; + if (usbfront_device_query_port_status_changed(device)) { + *buf |= 2; + changed = 1; + } + return changed ? 1 : 0; +} + +static int +usbfront_hcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, +u16 wIndex, char *buf, u16 wLength) +{ + struct usbfront_device *device = usbfront_hcd_hcd_to_usbfront_device( + hcd); + int return_value = 0; + trace(); + switch (typeReq) { + case GetHubStatus: + trace_info("GetHubStatus"); + if ((wValue != 0) || (wIndex != 0) || (wLength != 4)) + goto error; + /* 2 bytes wHubStatus and 2 bytes wHubChange: */ + /* Local power supply good, no overcurrect condition */ + /* No changes. */ + memset(buf, 0, wLength); + break; + case GetPortStatus: + trace_info("GetPortStatus"); + if ((wValue != 0) || (wIndex > 1) || (wLength != 4)) + goto error; + { + struct usb_port_status port_status = + usbfront_device_query_port_status(device); + memcpy(buf, &port_status, wLength); + } + break; + case GetHubDescriptor: + trace_info("GetHubDescriptor"); + { + /* bDescLength */ + /* bDescriptorType */ + /* bNbrPorts */ + /* wHubCharacteristics LSB */ + /* wHubCharacteristics MSB */ + /* bPwrOn2PwrGood */ + /* bHubContrCurrent */ + /* DeviceRemovable */ + /* PortPwrCtrlMask */ + /* See table 11.23.2.1 of the USB 2.0 specification. */ + + char descriptor[] = { 0x09, 0x29, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0xFF }; + descriptor[2] = 1; + memcpy(buf, &descriptor, min_t(size_t, sizeof( + descriptor), wLength)); + } + break; + case SetPortFeature: + trace_info("SetPortFeature"); + if (((wIndex & 0x00FF) > 1) || (wLength != 0)) + goto error; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + trace_info("USB_PORT_FEAT_SUSPEND"); + break; + case USB_PORT_FEAT_RESET: + trace_info("USB_PORT_FEAT_RESET"); + usbfront_device_set_port_reset(device); + break; + case USB_PORT_FEAT_POWER: + trace_info("USB_PORT_FEAT_POWER"); + usbfront_device_set_port_power(device); + break; + default: + trace_info("Unknown:%x", wValue); + goto error; + } + break; + case ClearPortFeature: + trace_info("ClearPortFeature"); + if (((wIndex & 0x00FF) > 1) || (wLength != 0)) + goto error; + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + trace_info("USB_PORT_FEAT_ENABLE"); + usbfront_device_clear_port_enable(device); + break; + case USB_PORT_FEAT_SUSPEND: + trace_info("USB_PORT_FEAT_SUSPEND"); + break; + case USB_PORT_FEAT_POWER: + trace_info("USB_PORT_FEAT_POWER"); + break; + case USB_PORT_FEAT_INDICATOR: + trace_info("USB_PORT_FEAT_INDICATOR"); + break; + case USB_PORT_FEAT_C_CONNECTION: + trace_info("USB_PORT_C_CONNECTION"); + usbfront_device_clear_port_connection_change(device); + break; + case USB_PORT_FEAT_C_RESET: + trace_info("USB_PORT_C_RESET"); + usbfront_device_clear_port_reset_change(device); + break; + case USB_PORT_FEAT_C_ENABLE: + trace_info("USB_PORT_C_ENABLE"); + break; + case USB_PORT_FEAT_C_SUSPEND: + trace_info("USB_PORT_C_SUSPEND"); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + trace_info("USB_PORT_C_OVER_CURRENT"); + break; + default: + trace_info("Unknown:%x", wValue); + goto error; + } + break; + default: + trace_info("Unknown:%x", typeReq); + error: + return_value = -EPIPE; + } + return return_value; +} + +static int +usbfront_hcd_start_port_reset(struct usb_hcd *hcd, unsigned port_num) +{ + trace(); + return 0; +} + +static struct hc_driver usbfront_hc_driver = { + .description = "usbfront_hc_driver", + .product_desc = "Xen USB Front-End Driver", + .hcd_priv_size = sizeof(struct usbfront_hcd), + /* .irq */ + .flags = HCD_USB2, + /* reset optional. */ + .start = usbfront_hcd_start, + /* suspend optional. */ + /* resume optional. */ + .stop = usbfront_hcd_stop, + .get_frame_number = usbfront_hcd_get_frame_number, + .urb_enqueue = usbfront_hcd_urb_enqueue, + .urb_dequeue = usbfront_hcd_urb_dequeue, + .endpoint_disable = usbfront_hcd_endpoint_disable, + .hub_status_data = usbfront_hcd_hub_status_data, + .hub_control = usbfront_hcd_hub_control, + /* hub_suspend optional. */ + /* hub_resume optional. */ + .start_port_reset = usbfront_hcd_start_port_reset, +}; + +static void +usbfront_hcd_invalid_stimulus(struct usbfront_hcd *uhcd, +usbfront_hcd_stimulus stimulus); + +static void usbfront_hcd_kick_urbs(struct usbfront_hcd *uhcd); + +static void +usbfront_hcd_handle_stimulus(struct usbfront_hcd *uhcd, +usbfront_hcd_stimulus stimulus) +{ + trace_info("uhcd %p in state %d received stimulus %d", uhcd, + uhcd->state, stimulus); + switch (uhcd->state) { + case usbfront_hcd_state_i: + switch (stimulus) { + case usbfront_hcd_stimulus_uq: + usbfront_hcd_kick_urbs(uhcd); + break; + } + break; + default: + usbfront_hcd_invalid_stimulus(uhcd, stimulus); + break; + } +} + +static void +usbfront_hcd_invalid_stimulus(struct usbfront_hcd *uhcd, +usbfront_hcd_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: hc_driver_uhcd %p in state %d" + "received invalid stimulus %d", uhcd, uhcd->state, stimulus); +} + +static void usbfront_hcd_kick_urbs(struct usbfront_hcd *uhcd) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + while (!usbfront_sll_is_empty(&uhcd->urb_sll) && + !list_empty(&uhcd->free_resource_list)) { + struct urb *urb = container_of( + (void **)usbfront_sll_remove_first( + &uhcd->urb_sll), + struct urb, hcpriv); + struct usbfront_hcd_resource *resource = list_entry( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + callback.work.link); + list_del_init(usbfront_hcd_resource_to_link(resource)); + trace_info("Starting URB"); + usbfront_hcd_resource_start_urb(resource, urb); + } +} + +void usbfront_hcd_resource_completed(struct usbfront_callback * callback) +{ + struct usbfront_hcd_resource *resource = + usbfront_hcd_resource_callback_to(callback); + struct usbfront_hcd *uhcd = usbfront_hcd_hcd_to_uhcd( + usbfront_device_get_drvdata( + usbfront_hcd_resource_query_device(resource))); + unsigned long flags; + trace(); + spin_lock_irqsave(&uhcd->lock, flags); + list_add(usbfront_hcd_resource_to_link(resource), + &uhcd->free_resource_list); + usbfront_hcd_kick_urbs(uhcd); + spin_unlock_irqrestore(&uhcd->lock, flags); +} + +static int +usbfront_driver_probe_or_remove(struct usbfront_device *device, int remove) +{ + int return_value = 0; + struct usb_hcd *hcd; + trace(); + if (remove) + goto remove_path; + if (usb_disabled()) { + return_value = -ENODEV; + goto exit_no_usb; + } + if ((hcd = usb_create_hcd(&usbfront_hc_driver, + usbfront_device_to_dev(device), + usbfront_device_to_dev(device)->bus_id))== NULL) { + trace_info("usb_create_hcd failed"); + return_value = -ENOMEM; + goto exit_no_hcd; + } + usbfront_device_set_drvdata(device, hcd); + if ((return_value = usb_add_hcd(hcd, 0, 0)) != 0) { + trace_info("usb_add_hcd failed"); + goto exit_no_add; + } + return 0; + remove_path: + hcd = usbfront_device_get_drvdata(device); + usb_remove_hcd(hcd); + exit_no_add: + usb_put_hcd(hcd); + usbfront_device_set_drvdata(device, NULL); + exit_no_hcd: + exit_no_usb: + return return_value; +} + +int usbfront_driver_probe(struct usbfront_device *device) +{ + trace(); + return usbfront_driver_probe_or_remove(device, 0); +} + +void usbfront_driver_remove(struct usbfront_device *device) +{ + trace(); + usbfront_driver_probe_or_remove(device, 1); +} + +int usbfront_driver_class_init(void) +{ + trace(); + return usbfront_hcd_resource_class_init(); +} + +void usbfront_driver_class_exit(void) +{ + trace(); + usbfront_hcd_resource_class_exit(); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,30 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_DRIVER_H +#define USBFRONT_DRIVER_H + +#include "usbfront_device.h" + +int usbfront_driver_class_init(void); +void usbfront_driver_class_exit(void); +int usbfront_driver_probe(struct usbfront_device *device); +void usbfront_driver_remove(struct usbfront_device *device); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,503 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include "usbfront_gw.h" +#include "usbfront_gw_resource.h" +#include "usbfront_trace.h" + +typedef enum { + usbfront_gw_stimulus_tq, /* Transaction queued. */ + usbfront_gw_stimulus_cc, /* Channel connect. */ + usbfront_gw_stimulus_cd, /* Channel disconnect. */ + usbfront_gw_stimulus_kt, /* Kick tras completed. */ + usbfront_gw_stimulus_ic, /* Initiator resource completed. */ + usbfront_gw_stimulus_ii, /* Initiator resources idle. */ + usbfront_gw_stimulus_lc, /* Client connect completed. */ + usbfront_gw_stimulus_lg, /* Client disconnect called. */ + usbfront_gw_stimulus_ld, /* Client disconnect completed. */ +} usbfront_gw_stimulus; + +static void +usbfront_gw_handle_stimulus(struct usbfront_gw *gw, +usbfront_gw_stimulus stimulus); + +void +usbfront_gw_submit_tra(struct usbfront_gw *gw, struct usbfront_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(usbfront_gw_tra_to_link(tra), &gw->tra_list); + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_tq); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_channel_connect(void *context) +{ + struct usbfront_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_cc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_handle_channel_message(void *context, +struct usbfront_channel_ibmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_gw *gw = context; + struct usbfront_gw_resource *resource; + usbif_error error = USBIF_ERROR_INVALID_PROTOCOL; + if (message->response.gw_status.id >= gw->initiator_quota) + goto complete_channel_message; + resource = &gw->initiator_resources[message->response.gw_status.id]; + if (usbfront_gw_resource_handle_response(resource, &message->response) + != 0) + goto complete_channel_message; + error = USBIF_ERROR_SUCCESS; + complete_channel_message: + usbfront_callback_complete(usbfront_channel_ibmsg_to_callback(message), + error); +} + +static void usbfront_gw_channel_disconnect(void *context, +struct usbfront_callback *callback) +{ + struct usbfront_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + gw->channel_disconnect_callback = callback; + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_cd); + spin_unlock_irqrestore(&gw->lock, flags); +} + +void usbfront_gw_submit_channel_message(struct usbfront_gw *gw, +struct usbfront_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + usbfront_channel_submit_message(gw->channel, message); +} + +static void usbfront_gw_invalid_stimulus(struct usbfront_gw *gw, +usbfront_gw_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: gw %p in state %d" + "received invalid stimulus %d", gw, gw->state, stimulus); +} + +static void usbfront_gw_kick_tras(struct usbfront_gw *gw) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!gw->kick_tras_out) { + gw->kick_tras_out = 1; + (void)usbfront_work_schedule(&gw->kick_tras_1_work); + } +} + +static void usbfront_gw_kick_tras_1(void *data) +{ + struct usbfront_gw *gw = data; + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + while ((!list_empty(&gw->tra_list)) && + (!list_empty(&gw->initiator_resource_list))) { + struct usbfront_gw_tra *tra; + struct usbfront_gw_resource *resource; + tra = list_entry(gw->tra_list.next, + struct usbfront_gw_tra, + callback.work.link); + resource = list_entry(gw->initiator_resource_list.next, + struct usbfront_gw_resource, + callback.work.link); + list_del_init(usbfront_gw_tra_to_link(tra)); + list_del_init(usbfront_gw_resource_to_link(resource)); + gw->initiator_resources_out++; + spin_unlock_irqrestore(&gw->lock, flags); + usbfront_gw_resource_start(resource, tra); + spin_lock_irqsave(&gw->lock, flags); + } + gw->kick_tras_out = 0; + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_kt); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_kick_tras_2(struct usbfront_callback *callback) +{ + struct usbfront_gw_resource *resource = + usbfront_gw_resource_callback_to(callback); + struct usbfront_gw *gw = usbfront_gw_resource_query_gw(resource); + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(usbfront_gw_resource_to_link(resource), + &gw->initiator_resource_list); + if (--gw->initiator_resources_out != 0) { + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_ic); + } else { + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_ii); + } + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_fail_out_tras(struct usbfront_gw *gw) +{ + trace(); + while (!list_empty(&gw->tra_list)) { + struct usbfront_gw_tra *tra = list_entry(gw->tra_list.next, + struct usbfront_gw_tra, callback.work.link); + list_del_init(usbfront_gw_tra_to_link(tra)); + usbfront_callback_complete(usbfront_gw_tra_to_callback(tra), + USBIF_ERROR_DISCONNECT); + } +} + +static void usbfront_gw_connect_client(struct usbfront_gw *gw) +{ + trace(); + (void)usbfront_work_schedule(&gw->connect_client_1_work); +} + +static void usbfront_gw_connect_client_1(void *data) +{ + struct usbfront_gw *gw = data; + unsigned long flags; + trace(); + gw->connect(gw); + spin_lock_irqsave(&gw->lock, flags); + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_lc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_disconnect_client(struct usbfront_gw *gw) +{ + trace(); + (void)usbfront_work_schedule(&gw->disconnect_client_1_work); +} + +static void usbfront_gw_disconnect_client_1(void *data) +{ + struct usbfront_gw *gw = data; + unsigned long flags; + trace(); + gw->disconnect(gw, &gw->disconnect_client_2_callback); + spin_lock_irqsave(&gw->lock, flags); + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_lg); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_disconnect_client_2(struct usbfront_callback *callback) +{ + struct usbfront_gw *gw = container_of(callback, struct usbfront_gw, + disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_ld); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void usbfront_gw_abort_initiator_resources(struct usbfront_gw *gw) +{ + u32 i; + trace(); + for (i = 0; i < gw->initiator_quota; i++) + usbfront_gw_resource_abort(&gw->initiator_resources[i], + USBIF_ERROR_DISCONNECT); +} + +static void usbfront_gw_test_initiator_resources(struct usbfront_gw *gw) +{ + trace(); + if (gw->initiator_resources_out == 0) + usbfront_gw_handle_stimulus(gw, usbfront_gw_stimulus_ii); +} + +static void usbfront_gw_complete_channel_disconnect(struct usbfront_gw *gw) +{ + trace(); + usbfront_callback_success(gw->channel_disconnect_callback); +} + +static int usbfront_gw_init_or_exit(struct usbfront_gw *gw, int exit) +{ + int return_value = 0; + u32 i; + trace(); + if (exit) + goto exit_path; + INIT_LIST_HEAD(&gw->initiator_resource_list); + if (gw->initiator_quota != 0) { + /*FIXME: kmalloc > page size*/ + gw->initiator_resources = kmalloc(sizeof( + struct usbfront_gw_resource) * + gw->initiator_quota, GFP_KERNEL); + if (gw->initiator_resources == NULL) { + return_value = -ENOMEM; + goto exit_no_initiator_resources; + } + for (i = 0; i < gw->initiator_quota; i++) { + struct usbfront_gw_resource *resource = + &gw->initiator_resources[i]; + usbfront_gw_resource_init(resource, + gw, usbfront_gw_kick_tras_2, i); + list_add_tail(usbfront_gw_resource_to_link(resource), + &gw->initiator_resource_list); + } + } + spin_lock_init(&gw->lock); + gw->state = usbfront_gw_state_i; + INIT_LIST_HEAD(&gw->tra_list); + usbfront_work_init(&gw->kick_tras_1_work, usbfront_gw_kick_tras_1, gw); + usbfront_work_init(&gw->connect_client_1_work, + usbfront_gw_connect_client_1, gw); + usbfront_work_init(&gw->disconnect_client_1_work, + usbfront_gw_disconnect_client_1, gw); + usbfront_callback_init(&gw->disconnect_client_2_callback, + usbfront_gw_disconnect_client_2); + gw->kick_tras_out = 0; + gw->initiator_resources_out = 0; + return 0; + exit_path: + if (gw->initiator_quota != 0) + kfree(gw->initiator_resources); + exit_no_initiator_resources: + return return_value; +} + +int +usbfront_gw_init(struct usbfront_gw *gw, +struct usbfront_channel *channel, usbfront_gw_connect_function *connect, +usbfront_gw_disconnect_function *disconnect, u32 initiator_quota) +{ + trace(); + gw->channel = channel; + gw->connect = connect; + gw->disconnect = disconnect; + gw->initiator_quota = initiator_quota; + usbfront_channel_install_client(channel, gw, + usbfront_gw_channel_connect, + usbfront_gw_handle_channel_message, + usbfront_gw_channel_disconnect); + return usbfront_gw_init_or_exit(gw, 0); +} + +void usbfront_gw_exit(struct usbfront_gw *gw) +{ + trace(); + (void)usbfront_gw_init_or_exit(gw, 1); +} + +static void +usbfront_gw_handle_stimulus(struct usbfront_gw *gw, +usbfront_gw_stimulus stimulus) +{ + switch (gw->state) { + case usbfront_gw_state_i: + /* Channel disconnected. */ + /* Client disconnected. */ + /* No tras queued. */ + /* Initiator resources idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_cc: + gw->state = usbfront_gw_state_i_cc; + usbfront_gw_connect_client(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc: + /* Channel connected. */ + /* Client connecting. */ + /* Maybe tras queued. */ + /* Initiator resources idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + break; + case usbfront_gw_stimulus_cd: + gw->state = usbfront_gw_state_i_cc_cd; + break; + case usbfront_gw_stimulus_lc: + gw->state = usbfront_gw_state_i_cc_lc; + usbfront_gw_kick_tras(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_cd: + /* Channel disconnecting. */ + /* Client connecting. */ + /* Maybe tras queued. */ + /* Initiator resources idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + break; + case usbfront_gw_stimulus_lc: + gw->state = usbfront_gw_state_i_cc_cd_lc; + usbfront_gw_disconnect_client(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_lc: + /* Channel connected. */ + /* Client connected. */ + /* Maybe tras queued. */ + /* Maybe initiator resources busy. */ + /* Maybe kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + usbfront_gw_kick_tras(gw); + break; + case usbfront_gw_stimulus_cd: + gw->state = usbfront_gw_state_i_cc_lc_cd; + usbfront_gw_kick_tras(gw); + break; + case usbfront_gw_stimulus_kt: + break; + case usbfront_gw_stimulus_ic: + case usbfront_gw_stimulus_ii: + usbfront_gw_kick_tras(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_lc_cd: + /* Channel disconnecting. */ + /* Client connected. */ + /* Maybe tras queued. */ + /* Maybe initiator resources busy. */ + /* Kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + break; + case usbfront_gw_stimulus_kt: + gw->state = usbfront_gw_state_i_cc_cd_lc; + usbfront_gw_disconnect_client(gw); + break; + case usbfront_gw_stimulus_ic: + case usbfront_gw_stimulus_ii: + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_cd_lc: + /* Channel disconnecting. */ + /* Calling client disconnect. */ + /* Maybe tras queued. */ + /* Maybe initiator resources busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + break; + case usbfront_gw_stimulus_ic: + case usbfront_gw_stimulus_ii: + break; + case usbfront_gw_stimulus_lg: + gw->state = usbfront_gw_state_i_cc_cd_lc_lg; + usbfront_gw_fail_out_tras(gw); + usbfront_gw_abort_initiator_resources(gw); + break; + case usbfront_gw_stimulus_ld: + gw->state = usbfront_gw_state_i_cc_cd_lc_ld; + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_cd_lc_lg: + /* Channel disconnecting. */ + /* Client disconnecting. */ + /* No tras queued. */ + /* Maybe initiator resources busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_tq: + break; + case usbfront_gw_stimulus_ic: + case usbfront_gw_stimulus_ii: + break; + case usbfront_gw_stimulus_ld: + gw->state = + usbfront_gw_state_i_cc_cd_lc_lg_ld; + usbfront_gw_test_initiator_resources(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_cd_lc_ld: + /* Channel disconnecting. */ + /* Client disconnected but call still in progress. */ + /* No tras queued. */ + /* Maybe initiator resources busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_ic: + case usbfront_gw_stimulus_ii: + break; + case usbfront_gw_stimulus_lg: + gw->state = + usbfront_gw_state_i_cc_cd_lc_lg_ld; + usbfront_gw_test_initiator_resources(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case usbfront_gw_state_i_cc_cd_lc_lg_ld: + /* Channel disconnecting. */ + /* Client disconnected. */ + /* No tras queued. */ + /* Test initiator resources or initiator resources busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case usbfront_gw_stimulus_ic: + break; + case usbfront_gw_stimulus_ii: + gw->state = usbfront_gw_state_i; + usbfront_gw_complete_channel_disconnect(gw); + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + default: + usbfront_gw_invalid_stimulus(gw, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,123 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_GW_H +#define USBFRONT_GW_H + +#include "usbfront_callback.h" +#include "usbfront_channel.h" + +struct usbfront_gw_resource; + +struct usbfront_gw_tra { + struct usbfront_callback callback; + union usbif_parameters parameters; + union usbif_status status; +}; + +static inline struct usbfront_callback * +usbfront_gw_tra_to_callback(struct usbfront_gw_tra *tra) +{ + return &tra->callback; +} + +static inline struct usbfront_gw_tra * +usbfront_gw_tra_callback_to(struct usbfront_callback *callback) +{ + return container_of(callback, struct usbfront_gw_tra, callback); +} + +static inline struct list_head * +usbfront_gw_tra_to_link(struct usbfront_gw_tra *tra) +{ + return usbfront_callback_to_link(usbfront_gw_tra_to_callback(tra)); +} + +static inline struct usbfront_gw_tra * +usbfront_gw_tra_link_to(struct list_head *link) +{ + return usbfront_gw_tra_callback_to(usbfront_callback_link_to(link)); +} + +static inline void usbfront_gw_tra_init(struct usbfront_gw_tra *tra, +usbfront_callback_function *callback) +{ + usbfront_callback_init(usbfront_gw_tra_to_callback(tra), callback); +} + +static inline void +usbfront_gw_tra_complete(struct usbfront_gw_tra *tra, usbif_error error) +{ + usbfront_callback_complete(usbfront_gw_tra_to_callback(tra), error); +} + +static inline +usbif_error usbfront_gw_tra_query_error(struct usbfront_gw_tra *tra) +{ + return usbfront_callback_query_error(usbfront_gw_tra_to_callback(tra)); +} + +typedef enum { + usbfront_gw_state_i, + usbfront_gw_state_i_cc, + usbfront_gw_state_i_cc_cd, + usbfront_gw_state_i_cc_lc, + usbfront_gw_state_i_cc_lc_cd, + usbfront_gw_state_i_cc_cd_lc, + usbfront_gw_state_i_cc_cd_lc_lg, + usbfront_gw_state_i_cc_cd_lc_ld, + usbfront_gw_state_i_cc_cd_lc_lg_ld +} usbfront_gw_state; + +struct usbfront_gw; + +typedef void usbfront_gw_connect_function(struct usbfront_gw *gw); + +typedef void usbfront_gw_disconnect_function(struct usbfront_gw *gw, +struct usbfront_callback *callback); + +struct usbfront_gw { + struct usbfront_channel *channel; + usbfront_gw_connect_function *connect; + usbfront_gw_disconnect_function *disconnect; + u32 initiator_quota; + struct list_head initiator_resource_list; + struct usbfront_gw_resource *initiator_resources; + spinlock_t lock; + usbfront_gw_state state; + struct list_head tra_list; + struct usbfront_work kick_tras_1_work; + struct usbfront_work connect_client_1_work; + struct usbfront_work disconnect_client_1_work; + struct usbfront_callback disconnect_client_2_callback; + int kick_tras_out; + u32 initiator_resources_out; + struct usbfront_callback *channel_disconnect_callback; +}; + +int usbfront_gw_init(struct usbfront_gw *gw, +struct usbfront_channel *channel, usbfront_gw_connect_function *connect, +usbfront_gw_disconnect_function *disconnect, u32 initiator_quota); + +extern void +usbfront_gw_submit_tra(struct usbfront_gw *gw, struct usbfront_gw_tra *tra); + +extern void usbfront_gw_exit(struct usbfront_gw *gw); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw_resource.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw_resource.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,211 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include "usbfront_gw_resource.h" +#include "usbfront_trace.h" + +void usbfront_gw_submit_channel_message(struct usbfront_gw *gw, +struct usbfront_channel_obmsg *message); + +static void +usbfront_gw_resource_handle_stimulus(struct usbfront_gw_resource *resource, +usbfront_gw_resource_stimulus stimulus); + +void usbfront_gw_resource_start(struct usbfront_gw_resource *resource, +struct usbfront_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + resource->tra = tra; + resource->error = USBIF_ERROR_SUCCESS; + resource->channel_message.request.usbif_parameters = + tra->parameters; + spin_lock_irqsave(&resource->lock, flags); + usbfront_gw_resource_handle_stimulus(resource, + usbfront_gw_resource_stimulus_st); + spin_unlock_irqrestore(&resource->lock, flags); + usbfront_gw_submit_channel_message(resource->gw, + &resource->channel_message); +} + +void usbfront_gw_resource_abort(struct usbfront_gw_resource *resource, +usbif_error error) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&resource->lock, flags); + resource->aborted_error = error; + usbfront_gw_resource_handle_stimulus(resource, + usbfront_gw_resource_stimulus_ab); + spin_unlock_irqrestore(&resource->lock, flags); +} + +static void usbfront_gw_resource_channel_message_callback( +struct usbfront_callback *callback) +{ + struct usbfront_gw_resource *resource = container_of(callback, + struct usbfront_gw_resource, channel_message.callback); + unsigned long flags; + spin_lock_irqsave(&resource->lock, flags); + usbfront_gw_resource_handle_stimulus(resource, + usbfront_gw_resource_stimulus_sc); + spin_unlock_irqrestore(&resource->lock, flags); +} + +int usbfront_gw_resource_handle_response(struct usbfront_gw_resource *resource, +struct usbif_response *response) +{ + int return_value = -1; + unsigned long flags; + spin_lock_irqsave(&resource->lock, flags); + if ((resource->state == usbfront_gw_resource_state_i_st) + ||(resource->state == usbfront_gw_resource_state_i_st_sc)) { + resource->tra->status = response->usbif_status; + resource->error = response->gw_status.error; + usbfront_gw_resource_handle_stimulus(resource, + usbfront_gw_resource_stimulus_ts); + return_value = 0; + } + spin_unlock_irqrestore(&resource->lock, flags); + return return_value; +} + + +static void +usbfront_gw_resource_invalid_stimulus(struct usbfront_gw_resource *resource, +usbfront_gw_resource_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: gw initiator resource %p in state %d" + "received invalid stimulus %d", resource, resource->state, + stimulus); +} + +static void +usbfront_gw_resource_set_aborted(struct usbfront_gw_resource *resource) +{ + trace(); + resource->error = resource->aborted_error; +} + +static void +usbfront_gw_resource_complete(struct usbfront_gw_resource *resource) +{ + usbfront_callback_complete(&resource->tra->callback, resource->error); + usbfront_callback_success(&resource->callback); +} + +void usbfront_gw_resource_init(struct usbfront_gw_resource *resource, +struct usbfront_gw *gw, usbfront_callback_function callback, int id) +{ + trace(); + usbfront_callback_init(&resource->callback, callback); + resource->gw = gw; + resource->channel_message.request.gw_parameters.id = id; + spin_lock_init(&resource->lock); + resource->state = usbfront_gw_resource_state_i; + usbfront_callback_init(usbfront_channel_obmsg_to_callback( + &resource->channel_message), + usbfront_gw_resource_channel_message_callback); +} + +static void +usbfront_gw_resource_handle_stimulus(struct usbfront_gw_resource *resource, +usbfront_gw_resource_stimulus stimulus) +{ + switch (resource->state) { + case usbfront_gw_resource_state_i: + switch (stimulus) { + case usbfront_gw_resource_stimulus_st: + resource->state = usbfront_gw_resource_state_i_st; + break; + case usbfront_gw_resource_stimulus_ab: + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_gw_resource_state_i_st: + switch (stimulus) { + case usbfront_gw_resource_stimulus_ab: + resource->state = usbfront_gw_resource_state_i_st_ab; + break; + case usbfront_gw_resource_stimulus_sc: + resource->state = usbfront_gw_resource_state_i_st_sc; + break; + case usbfront_gw_resource_stimulus_ts: + resource->state = usbfront_gw_resource_state_i_st_ts; + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_gw_resource_state_i_st_ab: + switch (stimulus) { + case usbfront_gw_resource_stimulus_sc: + resource->state = usbfront_gw_resource_state_i; + usbfront_gw_resource_set_aborted(resource); + usbfront_gw_resource_complete(resource); + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_gw_resource_state_i_st_sc: + switch (stimulus) { + case usbfront_gw_resource_stimulus_ab: + resource->state = usbfront_gw_resource_state_i; + usbfront_gw_resource_set_aborted(resource); + usbfront_gw_resource_complete(resource); + break; + case usbfront_gw_resource_stimulus_ts: + resource->state = usbfront_gw_resource_state_i; + usbfront_gw_resource_complete(resource); + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_gw_resource_state_i_st_ts: + switch (stimulus) { + case usbfront_gw_resource_stimulus_ab: + break; + case usbfront_gw_resource_stimulus_sc: + resource->state = usbfront_gw_resource_state_i; + usbfront_gw_resource_complete(resource); + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + default: + usbfront_gw_resource_invalid_stimulus(resource, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw_resource.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_gw_resource.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,84 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_GW_RESOURCE_H +#define USBFRONT_GW_RESOURCE_H + +#include +#include +#include "usbfront_channel.h" +#include "usbfront_gw.h" + +typedef enum { + usbfront_gw_resource_state_i, + usbfront_gw_resource_state_i_st, + usbfront_gw_resource_state_i_st_ab, + usbfront_gw_resource_state_i_st_sc, + usbfront_gw_resource_state_i_st_ts +} usbfront_gw_resource_state; + +typedef enum { + usbfront_gw_resource_stimulus_st, + usbfront_gw_resource_stimulus_ab, + usbfront_gw_resource_stimulus_sc, + usbfront_gw_resource_stimulus_ts, +} usbfront_gw_resource_stimulus; + +struct usbfront_gw_resource { + struct usbfront_callback callback; + struct usbfront_gw *gw; + spinlock_t lock; + usbfront_gw_resource_state state; + struct usbfront_gw_tra *tra; + usbif_error aborted_error; + usbif_error error; + struct usbfront_channel_obmsg channel_message; +}; + +static inline struct list_head * +usbfront_gw_resource_to_link(struct usbfront_gw_resource *resource) +{ + return &resource->callback.work.link; +} + +static inline struct usbfront_gw_resource * +usbfront_gw_resource_callback_to(struct usbfront_callback *callback) +{ + return container_of(callback, struct usbfront_gw_resource, callback); +} + +static inline struct usbfront_gw * +usbfront_gw_resource_query_gw(struct usbfront_gw_resource *resource) +{ + return resource->gw; +} + +void usbfront_gw_resource_init(struct usbfront_gw_resource *resource, +struct usbfront_gw *gw, usbfront_callback_function callback, int id); + +void usbfront_gw_resource_start(struct usbfront_gw_resource *resource, +struct usbfront_gw_tra *tra); + +void usbfront_gw_resource_abort(struct usbfront_gw_resource *resource, +usbif_error error); + +int usbfront_gw_resource_handle_response(struct usbfront_gw_resource *resource, +struct usbif_response *response); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,605 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on arch/xen/drivers/usbif/frontend/main.c, original copyright */ +/* notice follows... */ +/*****************************************************************************/ + +/* + * 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, 2005 Mark Williamson + * + * Contact or + * regarding this code. + * + * Still to be (maybe) implemented: + * - migration / backend restart support? + * - support for building / using as a module + */ + +/* + * 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 "usbfront_driver.h" +#include "usbfront_hcd_resource.h" +#include "usbfront_rbr_provider_pool.h" +#include "usbfront_trace.h" + +struct usbfront_rbr_provider_pool *rbr_provider_pool; + +int usbfront_hcd_resource_class_init(void) +{ + trace(); + rbr_provider_pool = usbfront_rbr_provider_pool_allocate(); + return (rbr_provider_pool != NULL) ? 0 : -ENOMEM; +} + +void usbfront_hcd_resource_class_exit(void) +{ + trace(); + usbfront_rbr_provider_pool_free(rbr_provider_pool); +} + +typedef enum { + usbfront_hcd_resource_stimulus_st, /* Start URB */ + usbfront_hcd_resource_stimulus_dq, /* Dequeue URB */ + usbfront_hcd_resource_stimulus_cs, /* Create RBRs success */ + usbfront_hcd_resource_stimulus_cf, /* Create RBRs failure */ + usbfront_hcd_resource_stimulus_tc, /* Transaction complete */ + usbfront_hcd_resource_stimulus_rc, /* Revoke RBRs complete */ + usbfront_hcd_resource_stimulus_uc, /* Unlink complete */ +} usbfront_hcd_resource_stimulus; + +static void +usbfront_hcd_resource_handle_stimulus(struct usbfront_hcd_resource *resource, +usbfront_hcd_resource_stimulus stimulus); + +void usbfront_hcd_resource_start_urb(struct usbfront_hcd_resource *resource, +struct urb *urb) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + urb->hcpriv = resource; + spin_lock_irqsave(&resource->lock, flags); + resource->urb = urb; + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_st); + spin_unlock_irqrestore(&resource->lock, flags); +} + +static DEFINE_RWLOCK(dequeue_lock); + +void usbfront_hcd_resource_dequeue_urb(struct urb *urb) +{ + unsigned long flags; + trace(); + write_lock_irqsave(&dequeue_lock, flags); + if (urb->hcpriv != NULL) { + struct usbfront_hcd_resource *resource = + (struct usbfront_hcd_resource *)urb->hcpriv; + unsigned long flags2; + spin_lock_irqsave(&resource->lock, flags2); + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_dq); + spin_unlock_irqrestore(&resource->lock, flags2); + } + write_unlock_irqrestore(&dequeue_lock, flags); +} + + +static void +usbfront_hcd_resource_invalid_stimulus(struct usbfront_hcd_resource *resource, +usbfront_hcd_resource_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: hcd resource %p in state %d" + "received invalid stimulus %d", + resource, resource->state, stimulus); +} + +static void +usbfront_hcd_resource_create_rbrs(struct usbfront_hcd_resource *resource) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + usbfront_create_rbr_request_element_ensure_removed( + &resource->rbr_request_element[0]); + usbfront_create_rbr_request_element_ensure_removed( + &resource->rbr_request_element[1]); + usbfront_create_rbr_request_element_set_buffer( + &resource->rbr_request_element[0], + resource->urb->transfer_buffer, + resource->urb->transfer_buffer_length, + usb_pipein(resource->urb->pipe) ? + USBFRONT_ACCESS_FLAGS_WRITE : + USBFRONT_ACCESS_FLAGS_READ); + usbfront_rbr_provider_request_add_element(&resource->rbr_request, + &resource->rbr_request_element[0]); + if (usb_pipetype(resource->urb->pipe) == PIPE_ISOCHRONOUS) { + struct usbif_isochronous_io_schedule_element *element; + int i; + if (resource->urb->number_of_packets > (PAGE_SIZE / + sizeof(struct + usbif_isochronous_io_schedule_element))) + goto schedule_too_big; + element = resource->schedule; + for (i = 0; i < resource->urb->number_of_packets; i++) { + memset(&element[i], 0, sizeof(element[i])); + element[i].offset = resource->urb->iso_frame_desc[i]. + offset; + element[i].length = resource->urb->iso_frame_desc[i]. + length; + } + usbfront_create_rbr_request_element_set_buffer( + &resource->rbr_request_element[1], + resource->schedule, + sizeof(struct usbif_isochronous_io_schedule_element) * + resource->urb->number_of_packets, + USBFRONT_ACCESS_FLAGS_WRITE | + USBFRONT_ACCESS_FLAGS_READ); + usbfront_rbr_provider_request_add_element( + &resource->rbr_request, + &resource->rbr_request_element[1]); + } + usbfront_rbr_provider_pool_reserve_and_create_rbrs(rbr_provider_pool, + &resource->rbr_request, + usbfront_device_query_domain(resource->device)); + return; + schedule_too_big: + usbfront_callback_complete( + usbfront_rbr_provider_request_to_create_callback( + &resource->rbr_request), USBIF_ERROR_TOO_BIG); +} + +static void +usbfront_hcd_resource_create_rbrs_1(struct usbfront_callback * callback) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_hcd_resource *resource = container_of( + usbfront_rbr_provider_request_create_callback_to(callback), + struct usbfront_hcd_resource, rbr_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&resource->lock, flags); + if (usbfront_callback_query_error(callback) == USBIF_ERROR_SUCCESS) { + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_cs); + } else { + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_cf); + } + spin_unlock_irqrestore(&resource->lock, flags); +} + +static void +usbfront_hcd_resource_abort_create_rbrs(struct usbfront_hcd_resource *resource) +{ + trace(); + usbfront_rbr_provider_pool_abort_reserve_and_create_rbrs( + &resource->rbr_request, USBIF_ERROR_UNLINKED); +} + +static void +usbfront_hcd_resource_set_unlinked_error( +struct usbfront_hcd_resource *resource) +{ + trace(); + usbfront_callback_set_error( + usbfront_rbr_provider_request_to_create_callback( + &resource->rbr_request), USBIF_ERROR_UNLINKED); +} + +static void +usbfront_hcd_resource_submit_tra( +struct usbfront_hcd_resource *resource) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct urb *urb = resource->urb; + union usbif_io_tra_parameters *parameters = + &resource->io_tra.parameters.io; + trace(); + memset(parameters, 0, sizeof(*parameters)); + parameters->header.header.tra_type = USBIF_TRA_TYPE_IO; + parameters->header.device_number = usb_pipedevice(urb->pipe); + parameters->header.endpoint = usb_pipeendpoint(urb->pipe); + parameters->header.direction = usb_pipein(urb->pipe) ? + USBIF_IO_TRA_DIRECTION_IN : + USBIF_IO_TRA_DIRECTION_OUT; + parameters->header.unlink_id = resource->index; + if (urb->transfer_flags & URB_SHORT_NOT_OK) + parameters->header.flags |= USBIF_IO_FLAGS_SHORT_NOT_OK; + if (urb->transfer_flags & URB_ZERO_PACKET) + parameters->header.flags |= USBIF_IO_FLAGS_ZERO_PACKET; + parameters->header.rbr = usbfront_create_rbr_request_element_query_rbr( + &resource->rbr_request_element[0]); + if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + parameters->header.io_tra_type = + USBIF_IO_TRA_TYPE_CONTROL; + memcpy(parameters->control.setup,urb->setup_packet, 8); + } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { + parameters->header.io_tra_type = + USBIF_IO_TRA_TYPE_BULK; + } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + parameters->header.io_tra_type = + USBIF_IO_TRA_TYPE_INTERRUPT; + parameters->interrupt.interval = urb->interval; + } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + parameters->header.io_tra_type = + USBIF_IO_TRA_TYPE_ISOCHRONOUS; + parameters->isochronous.interval = urb->interval; + parameters->isochronous.schedule_rbr = + usbfront_create_rbr_request_element_query_rbr( + &resource->rbr_request_element[1]); + parameters->isochronous.packet_count = urb->number_of_packets; + } + + usbfront_device_submit_tra(resource->device, + &resource->io_tra); +} + +static void +usbfront_hcd_resource_submit_tra_1(struct usbfront_callback *callback) +{ + struct usbfront_hcd_resource *resource = container_of( + usbfront_gw_tra_callback_to(callback), + struct usbfront_hcd_resource, io_tra); + unsigned long flags; + trace(); + spin_lock_irqsave(&resource->lock, flags); + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_tc); + spin_unlock_irqrestore(&resource->lock, flags); +} + +static void +usbfront_hcd_resource_revoke_rbrs(struct usbfront_hcd_resource *resource) +{ + trace(); + usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs(&resource-> + rbr_request); +} + +static void +usbfront_hcd_resource_revoke_rbrs_1(struct usbfront_callback *callback) +{ + struct usbfront_hcd_resource *resource = container_of( + usbfront_rbr_provider_request_revoke_callback_to(callback), + struct usbfront_hcd_resource, rbr_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&resource->lock, flags); + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_rc); + spin_unlock_irqrestore(&resource->lock, flags); +} + +static void usbfront_hcd_resource_submit_unlink( +struct usbfront_hcd_resource *resource) +{ + struct usbif_unlink_tra_parameters *parameters = + &resource->unlink_tra.parameters.unlink; + trace(); + memset(parameters, 0, sizeof(*parameters)); + parameters->header.tra_type = USBIF_TRA_TYPE_UNLINK; + parameters->unlink_id = resource->index; + usbfront_device_submit_tra(resource->device, + &resource->unlink_tra); +} + +static void +usbfront_hcd_resource_submit_unlink_1(struct usbfront_callback *callback) +{ + struct usbfront_hcd_resource *resource = container_of( + usbfront_gw_tra_callback_to(callback), + struct usbfront_hcd_resource, unlink_tra); + unsigned long flags; + trace(); + spin_lock_irqsave(&resource->lock, flags); + usbfront_hcd_resource_handle_stimulus(resource, + usbfront_hcd_resource_stimulus_uc); + spin_unlock_irqrestore(&resource->lock, flags); +} + +static void +usbfront_hcd_resource_complete(struct usbfront_hcd_resource *resource) +{ + int scheduled; + trace(); + scheduled = usbfront_work_schedule(&resource->complete_1_work); +} + +static void usbfront_hcd_resource_complete_1(void *context) +{ + struct usbfront_hcd_resource *resource = context; + struct urb *urb = resource->urb; + usbif_error error; + unsigned long flags; + trace(); + if (((error = usbfront_callback_query_error( + usbfront_rbr_provider_request_to_create_callback( + &resource->rbr_request))) == USBIF_ERROR_SUCCESS) && ((error = + usbfront_callback_query_error( + usbfront_rbr_provider_request_to_revoke_callback( + &resource->rbr_request))) == USBIF_ERROR_SUCCESS)) { + urb->status = usbif_error_map_to_local( + usbfront_gw_tra_query_error(&resource-> + io_tra)); + urb->actual_length = resource->io_tra.status.io.length; + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + int i; + urb->error_count = 0; + for (i = 0; i < urb->number_of_packets; i++) { + struct usbif_isochronous_io_schedule_element + *schedule = &resource->schedule[i]; + urb->iso_frame_desc[i].actual_length = + schedule->length; + urb->iso_frame_desc[i].status = + usbif_error_map_to_local(schedule-> + error); + if (urb->iso_frame_desc[i].status != 0) + urb->error_count++; + } + } + } else { + urb->status = usbif_error_map_to_local(error); + urb->actual_length = 0; + } + read_lock_irqsave(&dequeue_lock, flags); + urb->hcpriv = 0; + read_unlock_irqrestore(&dequeue_lock, flags); + local_irq_save(flags); + usb_hcd_giveback_urb(usbfront_device_get_drvdata(resource->device), + urb, NULL); + local_irq_restore(flags); + usbfront_callback_success(&resource->callback); +} + +static int +usbfront_hcd_resource_init_or_exit(struct usbfront_hcd_resource *resource, +struct usbfront_device *device, int index, +usbfront_callback_function *callback, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + usbfront_callback_init(&resource->callback, callback); + resource->device = device; + resource->index = index; + resource->schedule = (struct usbif_isochronous_io_schedule_element *) + __get_free_page(GFP_KERNEL); + if (resource->schedule == NULL) { + return_value = -ENOMEM; + goto exit_no_schedule; + } + spin_lock_init(&resource->lock); + resource->state = usbfront_hcd_resource_state_i; + usbfront_create_rbr_request_element_init( + &resource->rbr_request_element[0]); + usbfront_create_rbr_request_element_init( + &resource->rbr_request_element[1]); + usbfront_rbr_provider_request_init(&resource->rbr_request, + usbfront_hcd_resource_create_rbrs_1, + usbfront_hcd_resource_revoke_rbrs_1); + usbfront_gw_tra_init(&resource->io_tra, + usbfront_hcd_resource_submit_tra_1); + usbfront_gw_tra_init(&resource->unlink_tra, + usbfront_hcd_resource_submit_unlink_1); + usbfront_work_init(&resource->complete_1_work, + usbfront_hcd_resource_complete_1, resource); + return 0; + exit_path: + free_page((unsigned long)resource->schedule); + exit_no_schedule: + return return_value; +} + +int +usbfront_hcd_resource_init(struct usbfront_hcd_resource *resource, +struct usbfront_device *device, int index, +usbfront_callback_function *callback) +{ + trace(); + return usbfront_hcd_resource_init_or_exit(resource, device, index, + callback, 0); +} + +void usbfront_hcd_resource_exit(struct usbfront_hcd_resource *resource) +{ + trace(); + usbfront_hcd_resource_init_or_exit(resource, NULL, 0, NULL, 1); +} + +static void +usbfront_hcd_resource_handle_stimulus(struct usbfront_hcd_resource *resource, +usbfront_hcd_resource_stimulus stimulus) +{ + trace(); + switch (resource->state) { + case usbfront_hcd_resource_state_i: + /* Initialised. */ + /* Maybe completing previous URB. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_st: + resource->state = usbfront_hcd_resource_state_i_st; + usbfront_hcd_resource_create_rbrs(resource); + break; + /* A dequeue is possible whilst we are making the */ + /* complete response after we have transitioned back */ + /* to the i state. */ + case usbfront_hcd_resource_stimulus_dq: + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st: + /* Creating RBRs. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + resource->state = usbfront_hcd_resource_state_i_st_dq; + usbfront_hcd_resource_abort_create_rbrs(resource); + break; + case usbfront_hcd_resource_stimulus_cs: + resource->state = usbfront_hcd_resource_state_i_st_cs; + usbfront_hcd_resource_submit_tra(resource); + break; + case usbfront_hcd_resource_stimulus_cf: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete(resource); + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st_dq: + /* Creating RBRs. */ + /* Dequeue URB. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_cs: + resource->state = + usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_set_unlinked_error(resource); + usbfront_hcd_resource_revoke_rbrs(resource); + break; + case usbfront_hcd_resource_stimulus_cf: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete(resource); + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs: + /* Transaction submitted. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + resource->state = + usbfront_hcd_resource_state_i_st_cs_dq; + usbfront_hcd_resource_submit_unlink(resource); + break; + case usbfront_hcd_resource_stimulus_tc: + resource->state = + usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_revoke_rbrs(resource); + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st_dq_cs: + /* Revoking RBRs. */ + /* Maybe Dequeue URB. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_rc: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete(resource); + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs_dq: + /* Transaction submitted. */ + /* Unlink submitted. */ + /* Dequeue URB. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_tc: + case usbfront_hcd_resource_stimulus_uc: + resource->state = + usbfront_hcd_resource_state_i_st_cs_dq_tc; + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs_dq_tc: + /* Transaction submitted or Unlink submitted. */ + /* Dequeue URB. */ + switch (stimulus) { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_tc: + case usbfront_hcd_resource_stimulus_uc: + resource->state = + usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_revoke_rbrs(resource); + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, + stimulus); + break; + } + break; + default: + usbfront_hcd_resource_invalid_stimulus(resource, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_HCD_RESOURCE_H +#define USBFRONT_HCD_RESOURCE_H + +#include +#include +#include +#include "usbfront_device.h" +#include "usbfront_rbr_provider_pool.h" + +int usbfront_hcd_resource_class_init(void); + +void usbfront_hcd_resource_class_exit(void); + +typedef enum { + usbfront_hcd_resource_state_i, + usbfront_hcd_resource_state_i_st, + usbfront_hcd_resource_state_i_st_dq, + usbfront_hcd_resource_state_i_st_cs, + usbfront_hcd_resource_state_i_st_dq_cs, + usbfront_hcd_resource_state_i_st_cs_dq, + usbfront_hcd_resource_state_i_st_cs_dq_tc +} usbfront_hcd_resource_state; + +struct usbfront_hcd_resource { + struct usbfront_callback callback; + struct usbfront_device *device; + int index; + struct usbif_isochronous_io_schedule_element *schedule; + spinlock_t lock; + usbfront_hcd_resource_state state; + struct urb *urb; + struct usbfront_create_rbr_request_element rbr_request_element[2]; + struct usbfront_rbr_provider_request rbr_request; + struct usbfront_gw_tra io_tra; + struct usbfront_gw_tra unlink_tra; + struct usbfront_work complete_1_work; +}; + +static inline struct list_head * +usbfront_hcd_resource_to_link(struct usbfront_hcd_resource *resource) +{ + return usbfront_callback_to_link(&resource->callback); +} + +static inline struct usbfront_hcd_resource* +usbfront_hcd_resource_callback_to(struct usbfront_callback *callback) +{ + return container_of(callback, struct usbfront_hcd_resource, callback); +} + +static inline struct usbfront_device * +usbfront_hcd_resource_query_device(struct usbfront_hcd_resource *resource) +{ + return resource->device; +} + +int +usbfront_hcd_resource_init(struct usbfront_hcd_resource *resource, +struct usbfront_device *device, int index, +usbfront_callback_function *callback); + +void usbfront_hcd_resource_exit(struct usbfront_hcd_resource *resource); + +void usbfront_hcd_resource_start_urb(struct usbfront_hcd_resource *resource, +struct urb *urb); + +void usbfront_hcd_resource_dequeue_urb(struct urb *urb); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_module.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_module.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,65 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include +#include "usbfront_device.h" +#include "usbfront_driver.h" +#include "usbfront_trace.h" + +static int usbfront_module_init_or_exit(int exit) +{ + int return_value = 0; + trace(); + if (usb_disabled()) { + return_value = -ENODEV; + goto exit_no_usb; + } + if (exit) + goto exit_path; + if ((return_value = usbfront_driver_class_init()) != 0) + goto exit_no_driver_class; + if ((return_value = usbfront_device_class_init()) != 0) + goto exit_no_device_class; + return 0; + exit_path: + usbfront_device_class_exit(); + exit_no_device_class: + usbfront_driver_class_exit(); + exit_no_driver_class: + exit_no_usb: + return return_value; +} + +static int __init usbfront_module_init(void) +{ + trace(); + return usbfront_module_init_or_exit(0); +} + +static void __exit usbfront_module_exit(void) +{ + trace(); + (void)usbfront_module_init_or_exit(1); +} + +module_init(usbfront_module_init); +module_exit(usbfront_module_exit); + +MODULE_LICENSE("GPL"); diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_rbr_provider_pool.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_rbr_provider_pool.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,339 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbfront_rbr_provider_pool.h" +#include "usbfront_trace.h" + +static USBFRONT_CALLBACK_SERIALISER(callback_serialiser); + +struct usbfront_rbr_provider_pool { + struct usbfront_buffer_resource_list dedicated_resources; + struct usbfront_buffer_resource_provider *provider; + spinlock_t lock; + struct usbfront_buffer_resource_list head_request_req; + struct list_head request_list; + int kicking_requests; + int head_request_req_calculated; +}; + +static int +usbfront_rbr_provider_pool_init_or_exit( +struct usbfront_rbr_provider_pool *pool, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + pool->provider = usbfront_allocate_buffer_resource_provider( + pool->dedicated_resources); + if (pool->provider == NULL) + goto exit_no_provider; + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->request_list); + pool->kicking_requests = 0; + pool->head_request_req_calculated = 0; + return 0; + exit_path: + usbfront_free_buffer_resource_provider(pool->provider); + exit_no_provider: + return return_value; +} + +struct usbfront_rbr_provider_pool *usbfront_rbr_provider_pool_allocate(void) +{ + struct usbfront_rbr_provider_pool *pool; + trace(); + pool = (struct usbfront_rbr_provider_pool *)kmalloc(sizeof( + struct usbfront_rbr_provider_pool), GFP_KERNEL); + if (pool != NULL) { + pool->dedicated_resources.grant_references = USBIF_QUOTA * + USBIF_GRANT_TABLE_REFERENCE_COUNT; + if (usbfront_rbr_provider_pool_init_or_exit(pool, 0) != 0) { + kfree(pool); + pool = NULL; + } + } + return pool; +} + +void +usbfront_rbr_provider_pool_free(struct usbfront_rbr_provider_pool *pool) +{ + trace(); + (void)usbfront_rbr_provider_pool_init_or_exit(pool, 1); + kfree(pool); +} + +static void +usbfront_rbr_provider_pool_kick_requests( +struct usbfront_rbr_provider_pool *pool); + +void +usbfront_rbr_provider_pool_reserve_and_create_rbrs( +struct usbfront_rbr_provider_pool *pool, +struct usbfront_rbr_provider_request *request, domid_t domain) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + request->pool = pool; + request->domain = domain; + spin_lock_irqsave(&pool->lock, flags); + list_add_tail(usbfront_rbr_provider_request_to_link(request), + &pool->request_list); + spin_unlock_irqrestore(&pool->lock, flags); + usbfront_rbr_provider_pool_kick_requests(pool); +} + +static int +usbfront_rbr_provider_pool_calculate_rbr_resources(void *buffer, +usbif_buffer_byte_count length, struct usbfront_buffer_resource_list *list) +{ + unsigned long first_page, final_page; + trace(); + *list = usbfront_buffer_resource_list_null(); + if (length == 0) + return 0; + first_page = (((unsigned long)buffer) / PAGE_SIZE); + final_page = (((unsigned long)((char *)buffer+length-1))/PAGE_SIZE); + list->grant_references = final_page - first_page + 1; + return list->grant_references > USBIF_GRANT_TABLE_REFERENCE_COUNT; +} + +static int usbfront_rbr_provider_pool_calc_req( +struct usbfront_rbr_provider_request *request, +struct usbfront_buffer_resource_list *list) +{ + struct usbfront_create_rbr_request_element *element; + trace(); + *list = usbfront_buffer_resource_list_null(); + list_for_each_entry(element, &request->request_elements, link) { + struct usbfront_buffer_resource_list element_list; + if (usbfront_rbr_provider_pool_calculate_rbr_resources( + element->buffer, element->length, &element_list) != 0) + return 1; + usbfront_buffer_resource_list_plus_equals(list, &element_list); + } + return 0; +} + +static void +usbfront_rbr_provider_pool_create_rbr(void *buffer, +usbif_buffer_byte_count length, domid_t domain, +struct usbfront_buffer_resource_provider *provider, struct usbif_rbr *rbr, +int access_flags, struct usbfront_rbr_context *context) +{ + int i = 0; + unsigned long page, final_page; + trace(); + memset(rbr, 0, sizeof(*rbr)); + if (length == 0) { + context->count = 0; + return; + } + context->provider = provider; + context->domain_id = domain; + page = (unsigned long)buffer & PAGE_MASK; + final_page = ((unsigned long)buffer + length - 1) & PAGE_MASK; + for (;;) { + rbr->reference[i] = context->reference[i] = + usbfront_buffer_resource_provider_allocate_grant_reference( + provider); + gnttab_grant_foreign_access_ref(context->reference[i], + context->domain_id, virt_to_mfn(page), ((access_flags & + USBFRONT_ACCESS_FLAGS_WRITE) == 0) ? 1 : 0); + i++; + if (page == final_page) + break; + page += PAGE_SIZE; + } + context->count = i; + rbr->byte_offset = (unsigned long)buffer & ~PAGE_MASK; + rbr->byte_count = length; +} + +static void +usbfront_rbr_provider_pool_service_request( +struct usbfront_rbr_provider_pool *pool, +struct usbfront_rbr_provider_request *request) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_create_rbr_request_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + usbfront_rbr_provider_pool_create_rbr( + element->buffer, element->length, + request->domain, pool->provider, &element->rbr, + element->access_flags, &element->context); + element->created = 1; + } + usbfront_callback_serialiser_complete_callback(&callback_serialiser, + usbfront_rbr_provider_request_to_create_callback(request), + USBIF_ERROR_SUCCESS); +} + +static void usbfront_rbr_provider_pool_kick_requests( +struct usbfront_rbr_provider_pool *pool) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + trace(); + spin_lock_irqsave(&pool->lock, flags); + while ((!pool->kicking_requests) && + (!list_empty(&pool->request_list))) { + struct usbfront_buffer_resource_list free_resources; + struct usbfront_rbr_provider_request *request = + usbfront_rbr_provider_request_link_to( + pool->request_list.next); + if (pool->head_request_req_calculated) + goto skip_calc; + if ((usbfront_rbr_provider_pool_calc_req(request, + &pool->head_request_req) != 0) || + !usbfront_buffer_resource_list_subset_of( + &pool->head_request_req, + &pool->dedicated_resources)) { + list_del_init(usbfront_rbr_provider_request_to_link( + request)); + usbfront_callback_serialiser_complete_callback( + &callback_serialiser, + usbfront_rbr_provider_request_to_create_callback( + request), + USBIF_ERROR_TOO_BIG); + continue; + } + pool->head_request_req_calculated = 1; + skip_calc: + free_resources = + usbfront_buffer_resource_provider_query_free_resources( + pool->provider); + if (!usbfront_buffer_resource_list_subset_of( + &pool->head_request_req, &free_resources)) + break; + list_del_init(usbfront_rbr_provider_request_to_link(request)); + pool->head_request_req_calculated = 0; + pool->kicking_requests = 1; + spin_unlock_irqrestore(&pool->lock, flags); + usbfront_rbr_provider_pool_service_request(pool, request); + spin_lock_irqsave(&pool->lock, flags); + pool->kicking_requests = 0; + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void +usbfront_rbr_provider_pool_abort_reserve_and_create_rbrs( +struct usbfront_rbr_provider_request *request, usbif_error error) +{ + struct usbfront_rbr_provider_pool *pool = request->pool; + unsigned long flags; + struct usbfront_rbr_provider_request *queued_request; + trace(); + spin_lock_irqsave(&pool->lock, flags); + list_for_each_entry(queued_request, &pool->request_list, + create_callback.work.link) { + if (request == queued_request) { + list_del_init(usbfront_rbr_provider_request_to_link( + request)); + pool->head_request_req_calculated = 0; + usbfront_callback_serialiser_complete_callback( + &callback_serialiser, + usbfront_rbr_provider_request_to_create_callback( + request), error); + break; + } + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +static void +usbfront_rbr_provider_pool_retry_revoke_rbr( +struct usbfront_rbr_context *context) +{ + int i = 0,j; + trace(); + while (i != context->count) { + if (gnttab_end_foreign_access_ref(context->reference[i], 0)) { + usbfront_buffer_resource_provider_free_grant_reference( + context->provider, context->reference[i]); + for (j = i + 1; j < context->count; j++) { + context->reference[j-1] =context->reference[j]; + } + --context->count; + } else { + i++; + } + } + if (context->count == 0) { + usbfront_callback_success(context->callback); + } else { + printk(KERN_WARNING + "usbfront failed to end foreign " + "access granted to domain id %d. Retry in 5 seconds.\n", + context->domain_id); + init_timer(&context->timer); + context->timer.data = (unsigned long)context; + context->timer.expires = jiffies + (5*HZ); + context->timer.function = (void (*)(unsigned long)) + usbfront_rbr_provider_pool_retry_revoke_rbr; + add_timer(&context->timer); + } +} + +static void +usbfront_rbr_provider_pool_revoke_rbr(struct usbfront_rbr_context *context, +struct usbfront_callback *callback) +{ + trace(); + context->callback = callback; + usbfront_rbr_provider_pool_retry_revoke_rbr(context); +} + +static void +usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs_1( +struct usbfront_callback *callback) +{ + struct usbfront_rbr_provider_request *request = container_of(callback, + struct usbfront_rbr_provider_request, + reserved_callback); + struct usbfront_create_rbr_request_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + if (element->created) { + element->created = 0; + usbfront_rbr_provider_pool_revoke_rbr( + &element->context, callback); + return; + } + } + usbfront_rbr_provider_pool_kick_requests(request->pool); + usbfront_callback_serialiser_complete_callback(&callback_serialiser, + usbfront_rbr_provider_request_to_revoke_callback(request), + USBIF_ERROR_SUCCESS); +} + +void usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs( +struct usbfront_rbr_provider_request *request) +{ + trace(); + usbfront_callback_init(&request->reserved_callback, + usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs_1); + usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs_1( + &request->reserved_callback); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_rbr_provider_pool.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_rbr_provider_pool.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,169 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_RBR_PROVIDER_POOL +#define USBFRONT_RBR_PROVIDER_POOL + +#include +#include "usbfront_buffer_resource_provider.h" +#include "usbfront_callback.h" + +struct usbfront_rbr_provider_pool; + +struct usbfront_rbr_provider_pool *usbfront_rbr_provider_pool_allocate(void); + +void usbfront_rbr_provider_pool_free(struct usbfront_rbr_provider_pool *pool); + +#define USBFRONT_ACCESS_FLAGS_READ 1 +#define USBFRONT_ACCESS_FLAGS_WRITE 2 + +struct usbfront_rbr_context { + struct usbfront_buffer_resource_provider *provider; + struct timer_list timer; + struct usbfront_callback *callback; + int domain_id; + int count; + grant_ref_t reference[USBIF_GRANT_TABLE_REFERENCE_COUNT]; +}; + +struct usbfront_create_rbr_request_element { + struct list_head link; + void *buffer; + usbif_buffer_byte_count length; + int access_flags; + struct usbif_rbr rbr; + int created; + struct usbfront_rbr_context context; +}; + +static inline void usbfront_create_rbr_request_element_init( +struct usbfront_create_rbr_request_element *element) +{ + memset(element, 0, sizeof(*element)); + INIT_LIST_HEAD(&element->link); +} + +static inline void +usbfront_create_rbr_request_element_set_buffer( +struct usbfront_create_rbr_request_element *element, void *buffer, +usbif_buffer_byte_count length, int access_flags) +{ + element->buffer = buffer; + element->length = length; + element->access_flags = access_flags; +} + +static inline struct usbif_rbr +usbfront_create_rbr_request_element_query_rbr( +struct usbfront_create_rbr_request_element *element) +{ + return element->rbr; +} + +static inline void +usbfront_create_rbr_request_element_ensure_removed( +struct usbfront_create_rbr_request_element *element) +{ + list_del_init(&element->link); +} + +struct usbfront_rbr_provider_request { + struct usbfront_callback create_callback; + struct usbfront_callback revoke_callback; + struct usbfront_rbr_provider_pool *pool; + domid_t domain; + struct list_head request_elements; + struct usbfront_callback reserved_callback; +}; + +static inline struct usbfront_callback * +usbfront_rbr_provider_request_to_create_callback( +struct usbfront_rbr_provider_request *request) +{ + return &request->create_callback; +} + +static inline struct usbfront_rbr_provider_request * +usbfront_rbr_provider_request_create_callback_to( +struct usbfront_callback * callback) +{ + return container_of(callback, struct usbfront_rbr_provider_request, + create_callback); +} + +static inline struct usbfront_callback * +usbfront_rbr_provider_request_to_revoke_callback( +struct usbfront_rbr_provider_request *request) +{ + return &request->revoke_callback; +} + +static inline struct usbfront_rbr_provider_request * +usbfront_rbr_provider_request_revoke_callback_to( +struct usbfront_callback * callback) +{ + return container_of(callback, struct usbfront_rbr_provider_request, + revoke_callback); +} + +static inline struct list_head * usbfront_rbr_provider_request_to_link( +struct usbfront_rbr_provider_request *request) +{ + return usbfront_callback_to_link( + usbfront_rbr_provider_request_to_create_callback(request)); +} + +static inline struct usbfront_rbr_provider_request * +usbfront_rbr_provider_request_link_to(struct list_head *link) +{ + return usbfront_rbr_provider_request_create_callback_to( + usbfront_callback_link_to(link)); +} + +static inline void usbfront_rbr_provider_request_init( +struct usbfront_rbr_provider_request *request, +usbfront_callback_function *create_callback, +usbfront_callback_function *revoke_callback) +{ + usbfront_callback_init(&request->create_callback, create_callback); + usbfront_callback_init(&request->revoke_callback, revoke_callback); + INIT_LIST_HEAD(&request->request_elements); +} + +static inline void usbfront_rbr_provider_request_add_element( +struct usbfront_rbr_provider_request *request, +struct usbfront_create_rbr_request_element *element) +{ + list_add(&element->link, &request->request_elements); +} + +void +usbfront_rbr_provider_pool_reserve_and_create_rbrs( +struct usbfront_rbr_provider_pool *pool, +struct usbfront_rbr_provider_request *request, domid_t domain); + +void +usbfront_rbr_provider_pool_abort_reserve_and_create_rbrs( +struct usbfront_rbr_provider_request *request, usbif_error error); + +void +usbfront_rbr_provider_pool_revoke_and_unreserve_rbrs( +struct usbfront_rbr_provider_request *request); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_ring_channel.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_ring_channel.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,744 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redisrfbute 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 disrfbuted 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#include +#include "usbfront_ring_channel.h" +#include "usbfront_trace.h" + +static inline void +usbfront_ring_channel_rsrc_init(struct usbfront_ring_channel_rsrc *rsrc, +struct usbfront_ring_channel *channel, usbfront_callback_function *callback) +{ + trace(); + usbfront_channel_ibmsg_init(&rsrc->message, callback); + rsrc->channel = channel; +} + +static inline struct list_head * +usbfront_ring_channel_rsrc_to_link(struct usbfront_ring_channel_rsrc *rsrc) +{ + return usbfront_channel_ibmsg_to_link(&rsrc->message); +} + +static inline struct usbfront_ring_channel_rsrc * +usbfront_ring_channel_rsrc_callback_to(struct usbfront_callback *callback) +{ + return container_of(usbfront_channel_ibmsg_callback_to(callback), + struct usbfront_ring_channel_rsrc, message); +} + +typedef enum { + usbfront_ring_channel_stimulus_cn,/* connect request */ + usbfront_ring_channel_stimulus_mq,/* message queued */ + usbfront_ring_channel_stimulus_dn,/* disconnect request */ + usbfront_ring_channel_stimulus_ri,/* ring interrupt */ + usbfront_ring_channel_stimulus_cs,/* connect successful */ + usbfront_ring_channel_stimulus_cf,/* connect failed */ + usbfront_ring_channel_stimulus_cc,/* connect client completed */ + usbfront_ring_channel_stimulus_dc,/* disconnect client completed */ + usbfront_ring_channel_stimulus_ds,/* disconnect successful */ + usbfront_ring_channel_stimulus_ks,/* kick send ring completed */ + usbfront_ring_channel_stimulus_kr,/* kick recv ring completed */ + usbfront_ring_channel_stimulus_rc,/* rsrc completed */ + usbfront_ring_channel_stimulus_rf /* rsrc final completion */ +} usbfront_ring_channel_stimulus; + +static void +usbfront_ring_channel_handle_stimulus(struct usbfront_ring_channel *channel, +usbfront_ring_channel_stimulus stimulus); + +void +usbfront_ring_channel_connect(struct usbfront_ring_channel *channel, +struct usbfront_ring_channel_connect_request *request) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = &request->callback; + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_submit_message(struct usbfront_channel *base_channel, +struct usbfront_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_ring_channel *channel = + usbfront_ring_channel_channel_to(base_channel); + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(usbfront_channel_obmsg_to_link(message), + &channel->message_list); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_mq); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void +usbfront_ring_channel_disconnect(struct usbfront_ring_channel *channel, +struct usbfront_callback *callback) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = callback; + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static irqreturn_t +usbfront_ring_channel_interrupt(int irq, void *context, struct pt_regs *ptregs) +{ + struct usbfront_ring_channel *channel = context; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_ri); + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +static void +usbfront_ring_channel_invalid_stimulus(struct usbfront_ring_channel *channel, +usbfront_ring_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void +usbfront_ring_channel_do_connect(struct usbfront_ring_channel *channel) +{ + trace(); + (void)usbfront_work_schedule(&channel->do_connect_1_work); +} + +static void usbfront_ring_channel_do_connect_1(void *data) +{ + struct usbfront_ring_channel *channel = data; + struct usbfront_ring_channel_connect_request *request = + usbfront_ring_channel_connect_request_callback_to( + channel->current_callback); + evtchn_op_t op = { + .cmd = EVTCHNOP_alloc_unbound, + .u.alloc_unbound.dom = DOMID_SELF, + .u.alloc_unbound.remote_dom = request->domain_id + }; + struct usbif_sring * sring = (struct usbif_sring *)channel->ring; + int error; + unsigned long flags; + trace(); + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&channel->front_ring, sring, PAGE_SIZE); + BUG_ON(HYPERVISOR_event_channel_op(&op) != 0); + channel->event_channel = op.u.alloc_unbound.port; + error = bind_evtchn_to_irqhandler(channel->event_channel, + usbfront_ring_channel_interrupt, + SA_SAMPLE_RANDOM, "usbif", channel); + BUG_ON(error < 0); + channel->irq = error; + channel->ring_ref = gnttab_claim_grant_reference( + &channel->grant_ref_pool); + channel->domain_id = request->domain_id; + gnttab_grant_foreign_access_ref(channel->ring_ref, + channel->domain_id, + virt_to_mfn(channel->ring), + 0 /* not read-only */ ); + request->ring_ref = channel->ring_ref; + request->event_channel = channel->event_channel; + spin_lock_irqsave(&channel->lock, flags); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_cs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_connect_client(struct usbfront_ring_channel *channel) +{ + trace(); + (void)usbfront_work_schedule(&channel->connect_client_1_work); +} + +static void usbfront_ring_channel_connect_client_1(void *data) +{ + struct usbfront_ring_channel *channel = data; + unsigned long flags; + trace(); + usbfront_channel_connect(&channel->channel); + spin_lock_irqsave(&channel->lock, flags); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_cc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_disconnect_client(struct usbfront_ring_channel *channel) +{ + trace(); + (void)usbfront_work_schedule(&channel->disconnect_client_1_work); +} + +static void usbfront_ring_channel_disconnect_client_1(void *data) +{ + struct usbfront_ring_channel *channel = data; + trace(); + usbfront_channel_disconnect(&channel->channel, + &channel->disconnect_client_2_callback); +} + +static void +usbfront_ring_channel_disconnect_client_2(struct usbfront_callback *callback) +{ + struct usbfront_ring_channel *channel = container_of(callback, + struct usbfront_ring_channel, disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_dc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_do_disconnect(struct usbfront_ring_channel *channel) +{ + trace(); + (void)usbfront_work_schedule(&channel->do_disconnect_1_work); +} + +static void usbfront_ring_channel_do_disconnect_1(void *data) +{ + struct usbfront_ring_channel *channel = data; + unsigned long flags; + trace(); + if (gnttab_end_foreign_access_ref(channel->ring_ref, 1)) { + gnttab_release_grant_reference(&channel->grant_ref_pool, + channel->ring_ref); + unbind_from_irqhandler(channel->irq, channel); + spin_lock_irqsave(&channel->lock, flags); + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_ds); + spin_unlock_irqrestore(&channel->lock, flags); + } else { + printk(KERN_WARNING + "usbfront_ring_channel failed to end foreign access " + "granted to domain id %d. Will retry in 5 seconds.\n", + channel->domain_id); + init_timer(&channel->timer); + channel->timer.data = (unsigned long)channel; + channel->timer.expires = jiffies + (5*HZ); + channel->timer.function = (void (*)(unsigned long)) + usbfront_ring_channel_do_disconnect; + add_timer(&channel->timer); + } +} + +static void +usbfront_ring_channel_kick_send_ring(struct usbfront_ring_channel *channel) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!channel->send_ring_kick_out) { + channel->send_ring_kick_out = 1; + (void)usbfront_work_schedule(&channel->kick_send_ring_1_work); + } +} + +static void usbfront_ring_channel_kick_send_ring_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct usbfront_ring_channel *channel = data; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + for (;;) { + struct usbif_front_ring *front_ring = &channel->front_ring; + int notify = 0; + while (!list_empty(&channel->message_list)) { + struct usbfront_channel_obmsg *message; + struct usbif_request *request; + message = list_entry(channel->message_list.next, + struct usbfront_channel_obmsg, + callback.work.link); + list_del_init(usbfront_channel_obmsg_to_link(message)); + spin_unlock_irqrestore(&channel->lock, flags); + request = RING_GET_REQUEST(front_ring, + front_ring->req_prod_pvt); + *request = message->request; + usbfront_callback_success( + usbfront_channel_obmsg_to_callback(message)); + front_ring->req_prod_pvt++; + notify = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (notify) { + spin_unlock_irqrestore(&channel->lock, flags); + RING_PUSH_REQUESTS(front_ring); + notify_remote_via_irq(channel->irq); + spin_lock_irqsave(&channel->lock, flags); + } else { + channel->send_ring_kick_out = 0; + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_ks); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_kick_recv_ring(struct usbfront_ring_channel *channel) +{ + if (!channel->recv_ring_kick_out) { + channel->recv_ring_kick_out = 1; + (void)usbfront_work_schedule(&channel->kick_recv_ring_1_work); + } +} + +static void usbfront_ring_channel_kick_recv_ring_1(void *data) +{ + struct usbfront_ring_channel *channel = data; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + for (;;) { + struct usbif_front_ring *front_ring = &channel->front_ring; + int progress = 0; + RING_IDX rp, rc; + rc = front_ring->rsp_cons; + /* To avoid going idle when there is a waiting response we */ + /* need a mb() here to make sure we see any requests which */ + /* are new this time around the outer 'for' loop. */ + /* FIXME: spin_lock below makes this unnecessary? */ + mb(); + rp = front_ring->sring->rsp_prod; + /* To make sure we see the correct data for the requests */ + /* found above we need a rmb() here to force reads to be */ + /* after the read of front_ring->sring->rsp_prod. */ + rmb(); + /* FIXME: we are trusting sring->rsp_prod here... */ + while (!list_empty(&channel->rsrc_list) && (rc != rp)) { + struct usbfront_ring_channel_rsrc *rsrc; + struct usbif_response *response; + rsrc = list_entry(channel->rsrc_list.next, + struct usbfront_ring_channel_rsrc, + message.callback.work.link); + list_del_init(usbfront_ring_channel_rsrc_to_link( + rsrc)); + response = RING_GET_RESPONSE(front_ring, rc); + rsrc->message.response = *response; + front_ring->rsp_cons = ++rc; + channel->rsrcs_out++; + spin_unlock_irqrestore(&channel->lock, flags); + usbfront_channel_handle_message(&channel->channel, + &rsrc->message); + progress = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (!progress) + { + channel->recv_ring_kick_out = 0; + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_kr); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_kick_recv_ring_2(struct usbfront_callback *callback) +{ + struct usbfront_ring_channel_rsrc *rsrc = + usbfront_ring_channel_rsrc_callback_to(callback); + struct usbfront_ring_channel *channel = rsrc->channel; + unsigned long flags; + if ((usbfront_callback_query_error(callback) != USBIF_ERROR_SUCCESS) + && (channel->protocol_error != NULL)) + channel->protocol_error(channel); + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(usbfront_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + if (--channel->rsrcs_out == 0) { + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_rf); + } else { + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_rc); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_ring_channel_complete_current_callback( +struct usbfront_ring_channel *channel) +{ + trace(); + usbfront_callback_success(channel->current_callback); +} + +static void +usbfront_ring_channel_fail_current_callback( +struct usbfront_ring_channel *channel) +{ + trace(); + usbfront_callback_complete(channel->current_callback, + USBIF_ERROR_FAILURE); +} + +static void +usbfront_ring_channel_fail_out_messages(struct usbfront_ring_channel *channel) +{ + trace(); + while (!list_empty(&channel->message_list)) { + struct usbfront_channel_obmsg *message = list_entry( + channel->message_list.next, + struct usbfront_channel_obmsg, + callback.work.link); + list_del_init(usbfront_channel_obmsg_to_link(message)); + usbfront_callback_success(usbfront_channel_obmsg_to_callback( + message)); + } +} + +static void +usbfront_ring_channel_test_rsrcs(struct usbfront_ring_channel *channel) +{ + trace(); + if (channel->rsrcs_out == 0) { + usbfront_ring_channel_handle_stimulus(channel, + usbfront_ring_channel_stimulus_rf); + } +} + +static int +usbfront_ring_channel_init_or_exit(struct usbfront_ring_channel *channel, +int exit) +{ + int return_value = 0, i; + trace(); + if (exit) + goto exit_path; + channel->ring = (void *)__get_free_page(GFP_KERNEL); + if (channel->ring == NULL) { + trace_info("failed to allocate ring"); + return_value = -ENOMEM; + goto exit_no_ring; + } + return_value = gnttab_alloc_grant_references(1, + &channel->grant_ref_pool); + if (return_value != 0) { + trace_info("failed to allocate grant reference pool"); + goto exit_no_grant_ref; + } + spin_lock_init(&channel->lock); + channel->state = usbfront_ring_channel_state_i; + INIT_LIST_HEAD(&channel->message_list); + usbfront_work_init(&channel->do_connect_1_work, + usbfront_ring_channel_do_connect_1, channel); + usbfront_work_init(&channel->connect_client_1_work, + usbfront_ring_channel_connect_client_1, channel); + usbfront_work_init(&channel->disconnect_client_1_work, + usbfront_ring_channel_disconnect_client_1, channel); + usbfront_callback_init(&channel->disconnect_client_2_callback, + usbfront_ring_channel_disconnect_client_2); + usbfront_work_init(&channel->do_disconnect_1_work, + usbfront_ring_channel_do_disconnect_1, channel); + usbfront_work_init(&channel->kick_send_ring_1_work, + usbfront_ring_channel_kick_send_ring_1, channel); + usbfront_work_init(&channel->kick_recv_ring_1_work, + usbfront_ring_channel_kick_recv_ring_1, channel); + INIT_LIST_HEAD(&channel->rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct usbfront_ring_channel_rsrc *rsrc = &channel->rsrcs[i]; + usbfront_ring_channel_rsrc_init(rsrc, channel, + usbfront_ring_channel_kick_recv_ring_2); + list_add_tail(usbfront_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + } + channel->send_ring_kick_out = 0; + channel->recv_ring_kick_out = 0; + channel->rsrcs_out = 0; + return 0; + exit_path: + gnttab_free_grant_references(channel->grant_ref_pool); + exit_no_grant_ref: + free_page((unsigned long)channel->ring); + exit_no_ring: + return return_value; +} + +int +usbfront_ring_channel_init(struct usbfront_ring_channel *channel, +void (*protocol_error) (struct usbfront_ring_channel *channel)) +{ + trace(); + usbfront_channel_init(&channel->channel, + usbfront_ring_channel_submit_message); + channel->protocol_error = protocol_error; + return usbfront_ring_channel_init_or_exit(channel, 0); +} + +void usbfront_ring_channel_exit(struct usbfront_ring_channel *channel) +{ + trace(); + (void)usbfront_ring_channel_init_or_exit(channel, 1); +} + +static void usbfront_ring_channel_handle_stimulus( +struct usbfront_ring_channel *channel, +usbfront_ring_channel_stimulus stimulus) +{ + switch (channel->state) { + case usbfront_ring_channel_state_i: + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_cn: + channel->state = usbfront_ring_channel_state_i_cn; + usbfront_ring_channel_do_connect(channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn: + /* Interface connecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* do connect in progress. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_cs: + channel->state = usbfront_ring_channel_state_i_cn_cs; + usbfront_ring_channel_connect_client(channel); + break; + case usbfront_ring_channel_stimulus_cf: + channel->state = usbfront_ring_channel_state_i; + usbfront_ring_channel_fail_current_callback(channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs: + /* Interface connecting. */ + /* Client connecting. */ + /* Maybe messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* Connected. */ + /* Connect client in progress */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_mq: + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_cc: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc; + usbfront_ring_channel_complete_current_callback( + channel); + usbfront_ring_channel_kick_send_ring(channel); + usbfront_ring_channel_kick_recv_ring(channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc: + /* Interface connected. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Maybe kick send in progress. */ + /* Maybe kick recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Connected. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_mq: + usbfront_ring_channel_kick_send_ring(channel); + break; + case usbfront_ring_channel_stimulus_dn: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc_dn; + usbfront_ring_channel_kick_send_ring(channel); + usbfront_ring_channel_kick_recv_ring(channel); + break; + case usbfront_ring_channel_stimulus_ri: + usbfront_ring_channel_kick_recv_ring(channel); + break; + case usbfront_ring_channel_stimulus_ks: + case usbfront_ring_channel_stimulus_kr: + break; + case usbfront_ring_channel_stimulus_rc: + case usbfront_ring_channel_stimulus_rf: + usbfront_ring_channel_kick_recv_ring(channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc_dn: + /* Interface disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Kick send in progress. */ + /* Kick recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Connected. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_mq: + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_ks: + case usbfront_ring_channel_stimulus_kr: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks; + break; + case usbfront_ring_channel_stimulus_rc: + case usbfront_ring_channel_stimulus_rf: + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc_dn_ks: + /* Interface disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* One of kick send/recv in progress. */ + /* Maybe target rsrcs busy. */ + /* Connected. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_mq: + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_ks: + case usbfront_ring_channel_stimulus_kr: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr; + usbfront_ring_channel_disconnect_client(channel); + usbfront_ring_channel_fail_out_messages(channel); + break; + case usbfront_ring_channel_stimulus_rc: + case usbfront_ring_channel_stimulus_rf: + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr: + /* Interface disconnecting. */ + /* Client disconnecting. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Maybe target rsrcs busy. */ + /* Connected. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_mq: + usbfront_ring_channel_fail_out_messages(channel); + break; + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_dc: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc; + usbfront_ring_channel_test_rsrcs(channel); + break; + case usbfront_ring_channel_stimulus_rc: + case usbfront_ring_channel_stimulus_rf: + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc: + /* Interface disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Testing target rsrcs/ target rsrcs busy */ + /* Connected. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_ri: + case usbfront_ring_channel_stimulus_rc: + break; + case usbfront_ring_channel_stimulus_rf: + channel->state = + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf; + usbfront_ring_channel_do_disconnect(channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf: + /* Interface disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send/recv idle. */ + /* Target rsrcs idle. */ + /* Disconnecting. */ + switch (stimulus) { + case usbfront_ring_channel_stimulus_ri: + break; + case usbfront_ring_channel_stimulus_ds: + channel->state = usbfront_ring_channel_state_i; + usbfront_ring_channel_complete_current_callback( + channel); + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + default: + usbfront_ring_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_ring_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_ring_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,114 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_RING_CHANNEL_H +#define USBFRONT_RING_CHANNEL_H + +#include +#include +#include "usbfront_channel.h" + +struct usbfront_ring_channel; + +struct usbfront_ring_channel_rsrc { + struct usbfront_channel_ibmsg message; + struct usbfront_ring_channel *channel; +}; + +typedef enum { + usbfront_ring_channel_state_i, + usbfront_ring_channel_state_i_cn, + usbfront_ring_channel_state_i_cn_cs, + usbfront_ring_channel_state_i_cn_cs_cc, + usbfront_ring_channel_state_i_cn_cs_cc_dn, + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks, + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr, + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc, + usbfront_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf +} usbfront_ring_channel_state; + +struct usbfront_ring_channel { + struct usbfront_channel channel; + void (*protocol_error) (struct usbfront_ring_channel *channel); + void *ring; + grant_ref_t grant_ref_pool; + spinlock_t lock; + usbfront_ring_channel_state state; + struct list_head message_list; + struct usbfront_work do_connect_1_work; + struct usbfront_work connect_client_1_work; + struct usbfront_work disconnect_client_1_work; + struct usbfront_callback disconnect_client_2_callback; + struct usbfront_work do_disconnect_1_work; + struct usbfront_work kick_send_ring_1_work; + struct usbfront_work kick_recv_ring_1_work; + struct list_head rsrc_list; + struct usbfront_ring_channel_rsrc rsrcs[USBIF_QUOTA]; + int send_ring_kick_out; + int recv_ring_kick_out; + int rsrcs_out; + struct usbfront_callback *current_callback; + domid_t domain_id; + struct timer_list timer; + unsigned int event_channel; + grant_ref_t ring_ref; + int irq; + struct usbif_front_ring front_ring; +}; + +static inline struct usbfront_channel * +usbfront_ring_channel_to_channel(struct usbfront_ring_channel *channel) +{ + return &channel->channel; +} + +static inline struct usbfront_ring_channel * +usbfront_ring_channel_channel_to(struct usbfront_channel *channel) +{ + return container_of(channel, struct usbfront_ring_channel, channel); +} + +int usbfront_ring_channel_init(struct usbfront_ring_channel *channel, +void (*protocol_error) (struct usbfront_ring_channel *channel)); + +struct usbfront_ring_channel_connect_request { + struct usbfront_callback callback; + domid_t domain_id; + grant_ref_t ring_ref; + unsigned int event_channel; +}; + +static inline struct usbfront_ring_channel_connect_request * +usbfront_ring_channel_connect_request_callback_to( +struct usbfront_callback *callback) +{ + return container_of(callback, + struct usbfront_ring_channel_connect_request, callback); +} + +void usbfront_ring_channel_connect(struct usbfront_ring_channel *channel, +struct usbfront_ring_channel_connect_request *request); + +void +usbfront_ring_channel_disconnect(struct usbfront_ring_channel *channel, +struct usbfront_callback *callback); + +void usbfront_ring_channel_exit(struct usbfront_ring_channel *channel); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_sll.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_sll.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,100 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_SLL_H +#define USBFRONT_SLL_H + +struct usbfront_slk { + struct usbfront_slk *next; +}; + +struct usbfront_sll { + struct usbfront_slk *first; + struct usbfront_slk *last; +}; + +static inline void usbfront_slk_init(struct usbfront_slk *slk) +{ + slk->next = slk; +} + +static inline int usbfront_slk_is_singular(struct usbfront_slk *slk) +{ + return (slk->next == slk); +} + +static inline void usbfront_sll_init(struct usbfront_sll *sll) +{ + sll->first = 0; +} + +static inline int usbfront_sll_is_empty(struct usbfront_sll *sll) +{ + return (sll->first == 0); +} + +static inline void +usbfront_sll_add_last(struct usbfront_sll *sll, struct usbfront_slk *slk) +{ + slk->next = 0; + if (sll->first != 0) { + sll->last->next = slk; + } else { + sll->first = slk; + } + sll->last = slk; +} + +static inline struct usbfront_slk * +usbfront_sll_remove_first(struct usbfront_sll *sll) +{ + struct usbfront_slk *slk = sll->first; + if (slk != 0) { + sll->first = slk->next; + slk->next = slk; + } + return slk; +} + +static inline int +usbfront_sll_remove_slk(struct usbfront_sll *sll, struct usbfront_slk *slk) +{ + struct usbfront_slk *prev_slk = NULL; + struct usbfront_slk *next_slk = sll->first; + while (next_slk != NULL) { + if (next_slk != slk) { + prev_slk = next_slk; + next_slk = next_slk->next; + } else { + next_slk = next_slk->next; + if (prev_slk != NULL) { + prev_slk->next = next_slk; + } else { + sll->first = next_slk; + } + if (next_slk == NULL) + sll->last = prev_slk; + slk->next = slk; + return 1; + } + } + return 0; +} + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_trace.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_trace.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,36 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_TRACE_H +#define USBFRONT_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_FRONTEND_TRACE + +#define trace_info(format, ...) \ +printk(KERN_INFO "usbfront %s: " format "\n", __FUNCTION__, ## __VA_ARGS__) +#define trace() trace_info("") +#else +#define trace_info(format, ...) +#define trace() +#endif + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_work.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_work.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,73 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbfront_work.h" + +static void usbfront_work_function(void *ignored); + +DEFINE_SPINLOCK(usbfront_work_list_lock); +LIST_HEAD(usbfront_work_list); +DECLARE_WORK(usbfront_work_work, usbfront_work_function, NULL); +DECLARE_WAIT_QUEUE_HEAD(usbfront_work_waitqueue); +LIST_HEAD(usbfront_work_condition); + +void usbfront_work_wake_up(void) +{ + unsigned long flags; + spin_lock_irqsave(&usbfront_work_list_lock, flags); + while (!list_empty(&usbfront_work_condition)) + list_del_init(usbfront_work_condition.next); + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); + wake_up(&usbfront_work_waitqueue); +} + +int usbfront_work_schedule(struct usbfront_work *work) +{ + int scheduled = 0; + unsigned long flags; + spin_lock_irqsave(&usbfront_work_list_lock, flags); + if (list_empty(&work->link)) { + list_add_tail(&work->link, &usbfront_work_list); + scheduled = 1; + } + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); + if (scheduled) { + usbfront_work_wake_up(); + schedule_work(&usbfront_work_work); + } + return scheduled; +} + +static void usbfront_work_function(void *ignored) +{ + unsigned long flags; + spin_lock_irqsave(&usbfront_work_list_lock, flags); + while (!list_empty(&usbfront_work_list)) { + struct usbfront_work *work = list_entry( + usbfront_work_list.next, + struct usbfront_work, + link); + list_del_init(&work->link); + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); + usbfront_work_perform_synchronously(work); + spin_lock_irqsave(&usbfront_work_list_lock, flags); + } + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_work.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_work.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,105 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_WORK_H +#define USBFRONT_WORK_H + +#include +#include +#include +#include +#include + +struct usbfront_work { + struct list_head link; + void (*function) (void *context); + void *context; +}; + +#define USBFRONT_WORK_INIT( name, fn, ctx ) \ +{ .link = LIST_HEAD_INIT( name.link ), .function = fn, .context = ctx } + +#define USBFRONT_WORK( name, fn, ctx ) \ +struct usbfront_work name = USBFRONT_WORK_INIT( name, fn, ctx ) + +static inline void +usbfront_work_init(struct usbfront_work *work, void (*function)(void *context), +void *context) +{ + INIT_LIST_HEAD(&work->link); + work->function = function; + work->context = context; +} + +static inline +struct list_head *usbfront_work_to_link(struct usbfront_work *work) +{ + return &work->link; +} + +static inline struct usbfront_work * +usbfront_work_link_to(struct list_head *link) +{ + return container_of(link, struct usbfront_work, link); +} + +int usbfront_work_schedule(struct usbfront_work *work); + +static inline void +usbfront_work_perform_synchronously(struct usbfront_work *work) +{ + work->function(work->context); +} + +extern spinlock_t usbfront_work_list_lock; +extern struct list_head usbfront_work_list; +extern wait_queue_head_t usbfront_work_waitqueue; +extern struct list_head usbfront_work_condition; + +#define usbfront_work_until( condition ) \ +do { \ + unsigned long flags; \ + spin_lock_irqsave(&usbfront_work_list_lock, flags); \ + for (;;) { \ + struct list_head link; \ + while (!list_empty(&usbfront_work_list) && !(condition)) { \ + struct usbfront_work *work; \ + work = list_entry(usbfront_work_list.next, \ + struct usbfront_work, link); \ + list_del_init(&work->link); \ + spin_unlock_irqrestore(&usbfront_work_list_lock, \ + flags); \ + usbfront_work_perform_synchronously(work); \ + spin_lock_irqsave(&usbfront_work_list_lock, flags); \ + } \ + if (condition) \ + break; \ + INIT_LIST_HEAD(&link); \ + list_add_tail(&link, &usbfront_work_condition); \ + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); \ + wait_event(usbfront_work_waitqueue, list_empty(&link)); \ + spin_lock_irqsave(&usbfront_work_list_lock, flags); \ + } \ + spin_unlock_irqrestore(&usbfront_work_list_lock, flags); \ +} \ +while (0) + +void usbfront_work_wake_up(void); + +#endif diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_xb_channel.c --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_xb_channel.c Wed Mar 1 14:34:28 2006 @@ -0,0 +1,382 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include "usbfront_xb_channel.h" +#include "usbfront_trace.h" + +typedef enum { + usbfront_xb_channel_stimulus_cn, + usbfront_xb_channel_stimulus_dn, + usbfront_xb_channel_stimulus_pe, + usbfront_xb_channel_stimulus_rs, + usbfront_xb_channel_stimulus_rf +} usbfront_xb_channel_stimulus; + +static void +usbfront_xb_channel_handle_stimulus(struct usbfront_xb_channel *channel, +usbfront_xb_channel_stimulus stimulus); + +void usbfront_xb_channel_connect(struct usbfront_xb_channel *channel, +struct xenbus_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->device = device; + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void usbfront_xb_channel_backend_changed(struct usbfront_xb_channel *channel, +struct xenbus_device *dev, XenbusState state) +{ + trace(); + + switch(state) { + case XenbusStateUnknown: + trace_info( "XenbusStateUnknown" ); + break; + case XenbusStateInitialising: + trace_info( "XenbusStateInitialising" ); + break; + case XenbusStateInitWait: + trace_info( "XenbusStateInitWait" ); + break; + case XenbusStateInitialised: + trace_info( "XenbusStateInitialised" ); + break; + case XenbusStateConnected: + trace_info( "XenbusStateConnected" ); + break; + case XenbusStateClosing: + trace_info( "XenbusStateClosing" ); + break; + case XenbusStateClosed: + trace_info( "XenbusStateClosed" ); + break; + } +} + +static void +usbfront_xb_channel_protocol_error(struct usbfront_ring_channel *ring_channel) +{ + struct usbfront_xb_channel *channel = + usbfront_xb_channel_ring_channel_to(ring_channel); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_pe); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void usbfront_xb_channel_disconnect(struct usbfront_xb_channel *channel) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->disconnected = 0; + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); + usbfront_work_until(channel->disconnected); +} + +static void +usbfront_xb_channel_invalid_stimulus(struct usbfront_xb_channel *channel, +usbfront_xb_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "usbfront: xb channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void +usbfront_xb_channel_connect_ring(struct usbfront_xb_channel *channel) +{ + trace(); + channel->ring_connect.domain_id = channel->device->otherend_id; + usbfront_ring_channel_connect(&channel->channel, + &channel->ring_connect); +} + +static void +usbfront_xb_channel_connect_ring_1(struct usbfront_callback *callback) +{ + struct usbfront_xb_channel *channel = container_of( + usbfront_ring_channel_connect_request_callback_to(callback), + struct usbfront_xb_channel, ring_connect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_xb_channel_publish_details(struct usbfront_xb_channel *channel) +{ + trace(); + usbfront_work_schedule(&channel->publish_details_1_work); +} + +static void +usbfront_xb_channel_publish_details_1(void *data) +{ + struct usbfront_xb_channel *channel = data; + xenbus_transaction_t transaction; + int error; + unsigned long flags; + trace(); + again: + error = xenbus_transaction_start(&transaction); + if (error) { + trace_info("error starting transaction"); + goto transaction_error; + } + error = xenbus_printf(transaction, channel->device->nodename, + "ring-ref", "%u", + channel->ring_connect.ring_ref); + if (error) { + trace_info("error writing ring-ref to store"); + goto abort_transaction; + } + error = xenbus_printf(transaction, channel->device->nodename, + "event-channel", "%u", + channel->ring_connect.event_channel); + if (error) { + trace_info("error writing event-channel to store"); + goto abort_transaction; + } + error = xenbus_switch_state(channel->device, transaction, + XenbusStateInitialised); + if (error) { + trace_info("error switching state"); + goto abort_transaction; + } + error = xenbus_transaction_end(transaction, 0); + if (error) { + if (error == -EAGAIN) { + goto again; + } else { + trace_info("error committing transaction"); + goto transaction_error; + } + } + spin_lock_irqsave(&channel->lock, flags); + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); + return; + abort_transaction: + xenbus_transaction_end(transaction, 1); + transaction_error: + spin_lock_irqsave(&channel->lock, flags); + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_rf); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_xb_channel_disconnect_ring(struct usbfront_xb_channel *channel) +{ + trace(); + usbfront_ring_channel_disconnect(&channel->channel, + &channel->ring_disconnect); +} + +static void +usbfront_xb_channel_disconnect_ring_1(struct usbfront_callback *callback) +{ + struct usbfront_xb_channel *channel = container_of(callback, + struct usbfront_xb_channel, ring_disconnect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + usbfront_xb_channel_handle_stimulus(channel, + usbfront_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +usbfront_xb_channel_complete_disconnect(struct usbfront_xb_channel *channel) +{ + trace(); + channel->disconnected = 1; + usbfront_work_wake_up(); +} + +static int +usbfront_xb_channel_init_or_exit(struct usbfront_xb_channel *channel, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + if ((return_value = usbfront_ring_channel_init(&channel->channel, + usbfront_xb_channel_protocol_error)) != 0) { + goto exit_no_ring_channel; + } + spin_lock_init(&channel->lock); + channel->state = usbfront_xb_channel_state_i; + usbfront_callback_init(&channel->ring_connect.callback, + usbfront_xb_channel_connect_ring_1); + usbfront_work_init(&channel->publish_details_1_work, + usbfront_xb_channel_publish_details_1, + channel); + usbfront_callback_init(&channel->ring_disconnect, + usbfront_xb_channel_disconnect_ring_1); + return 0; + exit_path: + usbfront_ring_channel_exit(&channel->channel); + exit_no_ring_channel: + return return_value; +} + +int usbfront_xb_channel_init(struct usbfront_xb_channel *channel) +{ + return usbfront_xb_channel_init_or_exit(channel, 0); +} + +void usbfront_xb_channel_exit(struct usbfront_xb_channel *channel) +{ + trace(); + (void)usbfront_xb_channel_init_or_exit(channel, 1); +} + +static void +usbfront_xb_channel_handle_stimulus(struct usbfront_xb_channel *channel, +usbfront_xb_channel_stimulus stimulus) { + trace_info("xb channel %p in state %d received stimulus %d", channel, + channel->state, stimulus); + switch (channel->state) { + case usbfront_xb_channel_state_i: + switch (stimulus) { + case usbfront_xb_channel_stimulus_cn: + channel->state = usbfront_xb_channel_state_i_cn; + usbfront_xb_channel_connect_ring(channel); + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn: + switch (stimulus) { + case usbfront_xb_channel_stimulus_dn: + channel->state = usbfront_xb_channel_state_i_cn_dn; + break; + case usbfront_xb_channel_stimulus_rs: + channel->state = usbfront_xb_channel_state_i_cn_rs; + usbfront_xb_channel_publish_details(channel); + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_dn: + switch (stimulus) { + case usbfront_xb_channel_stimulus_rs: + channel->state = usbfront_xb_channel_state_i_cn_dn_rs; + usbfront_xb_channel_disconnect_ring(channel); + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_rs: + switch (stimulus) { + case usbfront_xb_channel_stimulus_dn: + channel->state = usbfront_xb_channel_state_i_cn_rs_dn; + break; + case usbfront_xb_channel_stimulus_rs: + channel->state = usbfront_xb_channel_state_i_cn_rs_rs; + break; + case usbfront_xb_channel_stimulus_rf: + channel->state = usbfront_xb_channel_state_i_cn_rs_rf; + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_dn_rs: + switch (stimulus) { + case usbfront_xb_channel_stimulus_rs: + channel->state = usbfront_xb_channel_state_i; + usbfront_xb_channel_complete_disconnect(channel); + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_rs_dn: + switch (stimulus) { + case usbfront_xb_channel_stimulus_rs: + case usbfront_xb_channel_stimulus_rf: + usbfront_xb_channel_disconnect_ring(channel); + channel->state = usbfront_xb_channel_state_i_cn_dn_rs; + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_rs_rs: + switch (stimulus) { + case usbfront_xb_channel_stimulus_dn: + usbfront_xb_channel_disconnect_ring(channel); + channel->state = usbfront_xb_channel_state_i_cn_dn_rs; + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case usbfront_xb_channel_state_i_cn_rs_rf: + switch (stimulus) { + case usbfront_xb_channel_stimulus_dn: + usbfront_xb_channel_disconnect_ring(channel); + channel->state = usbfront_xb_channel_state_i_cn_dn_rs; + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + default: + usbfront_xb_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r a376bab39768 -r e187d61f5d77 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_xb_channel.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_xb_channel.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,75 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_XB_CHANNEL_H +#define USBFRONT_XB_CHANNEL_H + +#include +#include "usbfront_ring_channel.h" + +typedef enum { + usbfront_xb_channel_state_i, + usbfront_xb_channel_state_i_cn, + usbfront_xb_channel_state_i_cn_dn, + usbfront_xb_channel_state_i_cn_rs, + usbfront_xb_channel_state_i_cn_dn_rs, + usbfront_xb_channel_state_i_cn_rs_dn, + usbfront_xb_channel_state_i_cn_rs_rs, + usbfront_xb_channel_state_i_cn_rs_rf, +} usbfront_xb_channel_state; + +struct usbfront_xb_channel { + struct usbfront_ring_channel channel; + spinlock_t lock; + usbfront_xb_channel_state state; + struct xenbus_device *device; + struct usbfront_ring_channel_connect_request ring_connect; + struct usbfront_work publish_details_1_work; + struct usbfront_callback ring_disconnect; + int disconnected; +}; + +static inline struct usbfront_channel * +usbfront_xb_channel_to_channel(struct usbfront_xb_channel *channel) +{ + return usbfront_ring_channel_to_channel(&channel->channel); +} + +static inline struct usbfront_xb_channel * +usbfront_xb_channel_ring_channel_to(struct usbfront_ring_channel *ring_channel) +{ + return container_of(ring_channel, struct usbfront_xb_channel, channel); +} + +static inline struct usbfront_xb_channel * +usbfront_xb_channel_channel_to(struct usbfront_channel *channel) +{ + return usbfront_xb_channel_ring_channel_to( + usbfront_ring_channel_channel_to(channel)); +} + +void usbfront_xb_channel_connect(struct usbfront_xb_channel *channel, +struct xenbus_device *device); +void usbfront_xb_channel_backend_changed(struct usbfront_xb_channel *channel, +struct xenbus_device *dev, XenbusState state); +void usbfront_xb_channel_disconnect(struct usbfront_xb_channel *channel); +int usbfront_xb_channel_init(struct usbfront_xb_channel *channel); +void usbfront_xb_channel_exit(struct usbfront_xb_channel *channel); + +#endif diff -r a376bab39768 -r e187d61f5d77 tools/examples/usb --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/tools/examples/usb Wed Mar 1 14:34:28 2006 @@ -0,0 +1,6 @@ +#!/bin/sh + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +success diff -r a376bab39768 -r e187d61f5d77 xen/include/public/io/usbif.h --- /dev/null Tue Feb 28 21:57:38 2006 +++ b/xen/include/public/io/usbif.h Wed Mar 1 14:34:28 2006 @@ -0,0 +1,213 @@ +/*****************************************************************************/ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* 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., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on the previous usbif.h by Mark Williamson and blkif.h, original */ +/* copyright notice follows... */ +/*****************************************************************************/ + +/****************************************************************************** + * usbif.h + * + * Unified block-device I/O interface for Xen guest OSes. + * + * Copyright (c) 2003-2004, Keir Fraser + */ + +#ifndef USBIF_H +#define USBIF_H + +#include "../xen.h" +#include "../grant_table.h" +#include "ring.h" + +typedef uint32_t usbif_error; + +#define USBIF_ERROR_SUCCESS (usbif_error)0 +#define USBIF_ERROR_FAILURE (usbif_error)1 +#define USBIF_ERROR_DISCONNECT (usbif_error)2 +#define USBIF_ERROR_INVALID_PROTOCOL (usbif_error)3 +#define USBIF_ERROR_INVALID_PARAMETER (usbif_error)4 +#define USBIF_ERROR_TOO_BIG (usbif_error)5 +#define USBIF_ERROR_NO_DEVICE (usbif_error)6 +#define USBIF_ERROR_UNLINKED (usbif_error)7 +#define USBIF_ERROR_PIPE (usbif_error)8 + +#define USBIF_GRANT_TABLE_REFERENCE_COUNT 16 + +typedef uint32_t usbif_buffer_byte_count; + +struct usbif_rbr { + grant_ref_t reference[USBIF_GRANT_TABLE_REFERENCE_COUNT]; + usbif_buffer_byte_count byte_offset; + usbif_buffer_byte_count byte_count; +}; + +static inline usbif_buffer_byte_count +usbif_rbr_query_byte_count(struct usbif_rbr *rbr) +{ + return rbr->byte_count; +} + +struct usbif_tra_parameters_header { + uint8_t tra_type; + uint8_t reserved[7]; +}; + +#define USBIF_TRA_TYPE_PROBE 0 +#define USBIF_TRA_TYPE_RESET 1 +#define USBIF_TRA_TYPE_IO 2 +#define USBIF_TRA_TYPE_UNLINK 3 + +struct usbif_probe_tra_parameters { + struct usbif_tra_parameters_header header; + uint8_t reserved[8]; +}; + +struct usbif_probe_tra_status { + uint8_t result; + uint8_t reserved[7]; +}; + +#define USBIF_PROBE_RESULT_NO_DEVICE 0 +#define USBIF_PROBE_RESULT_DEVICE_PRESENT 1 + +struct usbif_reset_tra_parameters { + struct usbif_tra_parameters_header header; + uint8_t reserved[8]; +}; + +struct usbif_reset_tra_status { + uint8_t result; + uint8_t reserved[7]; +}; + +#define USBIF_RESET_RESULT_FULL_SPEED 0 +#define USBIF_RESET_RESULT_LOW_SPEED 1 +#define USBIF_RESET_RESULT_HIGH_SPEED 2 + +struct usbif_io_tra_parameters_header { + struct usbif_tra_parameters_header header; + uint8_t io_tra_type; + uint8_t device_number; + uint8_t endpoint; + uint8_t direction; + uint32_t unlink_id; + uint32_t flags; + uint32_t reserved; + struct usbif_rbr rbr; +}; + +#define USBIF_IO_TRA_TYPE_CONTROL 0 +#define USBIF_IO_TRA_TYPE_BULK 1 +#define USBIF_IO_TRA_TYPE_INTERRUPT 2 +#define USBIF_IO_TRA_TYPE_ISOCHRONOUS 3 + +#define USBIF_IO_TRA_DIRECTION_OUT 0 +#define USBIF_IO_TRA_DIRECTION_IN 1 + +#define USBIF_IO_FLAGS_SHORT_NOT_OK 0x00000001 +#define USBIF_IO_FLAGS_ZERO_PACKET 0x00000002 + +struct usbif_control_io_tra_parameters { + struct usbif_io_tra_parameters_header header; + uint8_t setup[8]; +}; + +struct usbif_bulk_io_tra_parameters { + struct usbif_io_tra_parameters_header header; +}; + +struct usbif_interrupt_io_tra_parameters { + struct usbif_io_tra_parameters_header header; + uint32_t interval; +}; + +struct usbif_isochronous_io_tra_parameters { + struct usbif_io_tra_parameters_header header; + uint32_t interval; + uint32_t packet_count; + struct usbif_rbr schedule_rbr; +}; + +struct usbif_isochronous_io_schedule_element { + usbif_buffer_byte_count offset; /* IN offset into header's rbr */ + usbif_buffer_byte_count length; /* IN = expected, OUT = actual */ + usbif_error error; /* OUT for this packet. */ +}; + +union usbif_io_tra_parameters { + struct usbif_io_tra_parameters_header header; + struct usbif_control_io_tra_parameters control; + struct usbif_bulk_io_tra_parameters bulk; + struct usbif_interrupt_io_tra_parameters interrupt; + struct usbif_isochronous_io_tra_parameters isochronous; +}; + +struct usbif_io_tra_status { + usbif_buffer_byte_count length; +}; + +struct usbif_unlink_tra_parameters { + struct usbif_tra_parameters_header header; + uint32_t unlink_id; + uint32_t reserved; +}; + +struct usbif_ring_gw_parameters_element { + uint32_t id; +}; + +struct usbif_ring_gw_status_element { + uint32_t id; + usbif_error error; +}; + +union usbif_parameters { + struct usbif_tra_parameters_header header; + struct usbif_probe_tra_parameters probe; + struct usbif_reset_tra_parameters reset; + union usbif_io_tra_parameters io; + struct usbif_unlink_tra_parameters unlink; +}; + +union usbif_status { + struct usbif_probe_tra_status probe; + struct usbif_reset_tra_status reset; + struct usbif_io_tra_status io; +}; + +struct usbif_request { + struct usbif_ring_gw_parameters_element gw_parameters; + union usbif_parameters usbif_parameters; +}; + +struct usbif_response { + struct usbif_ring_gw_status_element gw_status; + union usbif_status usbif_status; +}; + +DEFINE_RING_TYPES(usbif, struct usbif_request, struct usbif_response); + +#define USBIF_MAX_SCHEDULE_PACKET_COUNT 64 + +/* USBIF_QUOTA is constrained by lack of flow control in RING macros. */ +#define USBIF_QUOTA (__RING_SIZE(((struct usbif_sring *)0), PAGE_SIZE) - 1) + +#endif