diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/Kconfig --- a/linux-2.6-xen-sparse/drivers/xen/Kconfig Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig Tue Oct 3 16:29:44 2006 @@ -145,6 +145,29 @@ depends on XEN_PCIDEV_BACKEND default n +config XEN_USBDEV_BACKEND + bool "USB-device backend driver" + depends on XEN_BACKEND + 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_TPMDEV_BACKEND tristate "TPM-device backend driver" depends on XEN_BACKEND @@ -171,6 +194,30 @@ network interfaces within another guest OS. Unless you are building a dedicated device-driver domain, or your master control domain (domain 0), then you almost certainly want to say Y here. + +config XEN_USBDEV_FRONTEND + bool "USB-device frontend driver" + depends on XEN + default y + select USB + 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 f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Tue Oct 3 16:29:44 2006 @@ -11,7 +11,9 @@ obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/ 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_PCIDEV_BACKEND) += pciback/ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += usbfront/ diff -r f426f6e646eb -r 404b4ec94253 tools/examples/Makefile --- a/tools/examples/Makefile Mon Oct 2 17:04:56 2006 +++ b/tools/examples/Makefile Tue Oct 3 16:29:44 2006 @@ -29,6 +29,7 @@ XEN_SCRIPTS += block XEN_SCRIPTS += block-enbd block-nbd XEN_SCRIPTS += blktap +XEN_SCRIPTS += usb XEN_SCRIPTS += vtpm vtpm-delete XEN_SCRIPTS += xen-hotplug-cleanup XEN_SCRIPTS += external-device-migrate diff -r f426f6e646eb -r 404b4ec94253 tools/examples/xen-backend.agent --- a/tools/examples/xen-backend.agent Mon Oct 2 17:04:56 2006 +++ b/tools/examples/xen-backend.agent Tue Oct 3 16:29:44 2006 @@ -9,6 +9,9 @@ case "$XENBUS_TYPE" in tap) /etc/xen/scripts/blktap "$ACTION" + ;; + usb) + /etc/xen/scripts/usb "$ACTION" ;; vbd) /etc/xen/scripts/block "$ACTION" diff -r f426f6e646eb -r 404b4ec94253 tools/examples/xen-backend.rules --- a/tools/examples/xen-backend.rules Mon Oct 2 17:04:56 2006 +++ b/tools/examples/xen-backend.rules Tue Oct 3 16:29:44 2006 @@ -1,4 +1,5 @@ SUBSYSTEM=="xen-backend", KERNEL=="tap*", RUN+="/etc/xen/scripts/blktap $env{ACTION}" +SUBSYSTEM=="xen-backend", KERNEL=="usb*", RUN+="/etc/xen/scripts/usb $env{ACTION}" SUBSYSTEM=="xen-backend", KERNEL=="vbd*", RUN+="/etc/xen/scripts/block $env{ACTION}" SUBSYSTEM=="xen-backend", KERNEL=="vtpm*", RUN+="/etc/xen/scripts/vtpm $env{ACTION}" SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="online", RUN+="$env{script} online" diff -r f426f6e646eb -r 404b4ec94253 tools/examples/xmexample1 --- a/tools/examples/xmexample1 Mon Oct 2 17:04:56 2006 +++ b/tools/examples/xmexample1 Tue Oct 3 16:29:44 2006 @@ -64,6 +64,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 4 on bus 2. +#usbport = ['path=1-3','path=2-4'] #---------------------------------------------------------------------------- # Define to which TPM instance the user domain should communicate. diff -r f426f6e646eb -r 404b4ec94253 tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Mon Oct 2 17:04:56 2006 +++ b/tools/python/xen/xend/XendDomainInfo.py Tue Oct 3 16:29:44 2006 @@ -1794,5 +1794,5 @@ addControllerClass('pci', pciif.PciController) addControllerClass('ioports', iopif.IOPortsController) addControllerClass('irq', irqif.IRQController) -addControllerClass('usb', usbif.UsbifController) +addControllerClass('usbport', usbif.UsbifController) addControllerClass('tap', BlktapController) diff -r f426f6e646eb -r 404b4ec94253 tools/python/xen/xend/server/usbif.py --- a/tools/python/xen/xend/server/usbif.py Mon Oct 2 17:04:56 2006 +++ b/tools/python/xen/xend/server/usbif.py Tue Oct 3 16:29:44 2006 @@ -22,6 +22,8 @@ """Support for virtual USB hubs. """ +from xen.xend import sxp + from xen.xend.server.DevController import DevController @@ -36,7 +38,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 f426f6e646eb -r 404b4ec94253 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Mon Oct 2 17:04:56 2006 +++ b/tools/python/xen/xm/create.py Tue Oct 3 16:29:44 2006 @@ -245,6 +245,10 @@ fn=append_value, default=0, use="Make the domain a TPM interface backend.") +gopts.var('usbif', val='no|yes', + fn=set_bool, default=0, + use="Make the domain a USB device backend.") + gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]', fn=append_value, default=[], use="""Add a disk device to a domain. The physical device is DEV, @@ -271,10 +275,13 @@ For example 'irq=7'. This option may be repeated to add more than one IRQ.""") -gopts.var('usbport', val='PATH', +gopts.var('usbport', 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=[], @@ -528,10 +535,14 @@ config_devs.append(['device', config_irq]) def configure_usb(config_devs, vals): - for path in vals.usbport: - config_usb = ['usbport', ['path', path]] + for d in vals.usbport: + path = d.get('path') + backend = d.get('backend') + config_usb = ['usbport'] + config_usb.append(['path', path]) + if backend: + config_usb.append(['backend', backend]) config_devs.append(['device', config_usb]) - def configure_security(config, vals): """Create the config for ACM security labels. @@ -685,6 +696,8 @@ config.append(['backend', ['netif']]) if vals.tpmif: config.append(['backend', ['tpmif']]) + if vals.usbif: + config.append(['backend', ['usbif']]) if vals.localtime: config.append(['localtime', vals.localtime]) @@ -756,7 +769,23 @@ hexd = map(lambda v: '0x'+v, d) ioports.append(hexd) vals.ioports = ioports - + +def preprocess_usb(vals): + if not vals.usbport: return + usbport = [] + for port in vals.usbport: + 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 usbport specifier: ' + port) + d[k] = v + usbport.append(d) + vals.usbport = usbport + def preprocess_vtpm(vals): if not vals.vtpm: return vtpms = [] @@ -927,6 +956,7 @@ preprocess_ioports(vals) preprocess_ip(vals) preprocess_nfs(vals) + preprocess_usb(vals) preprocess_vnc(vals) preprocess_vtpm(vals) preprocess_access_control(vals) diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/Makefile --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/Makefile Tue Oct 3 16:29:44 2006 @@ -0,0 +1,12 @@ +obj-y = \ +ub_buffer_rsrc_provider.o \ +ub_callback.o \ +ub_device.o \ +ub_ep.o \ +ub_gw.o \ +ub_gw_rsrc.o \ +ub_rbr_mapper.o \ +ub_rsrc.o \ +ub_ring_channel.o \ +ub_work.o \ +ub_xb_channel.o diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_buffer_rsrc_provider.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_buffer_rsrc_provider.c Tue Oct 3 16:29:44 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 "ub_buffer_rsrc_provider.h" +#include "ub_trace.h" + +struct ub_buffer_rsrc_provider { + spinlock_t lock; + struct ub_buffer_rsrc_list rsrcs; + struct ub_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 ub_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 +ub_buffer_rsrc_provider_init_or_exit( +struct ub_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 = ub_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 ub_buffer_rsrc_provider * +ub_allocate_buffer_rsrc_provider(struct ub_buffer_rsrc_list rsrcs) +{ + struct ub_buffer_rsrc_provider *provider; + trace(); + provider = kmalloc(sizeof(struct ub_buffer_rsrc_provider), + GFP_KERNEL); + if (provider != NULL) { + provider->rsrcs = rsrcs; + if (ub_buffer_rsrc_provider_init_or_exit(provider, 0) + != 0) { + kfree(provider); + provider = NULL; + } + } + return provider; +} + +void ub_free_buffer_rsrc_provider( +struct ub_buffer_rsrc_provider *provider) +{ + trace(); + (void)ub_buffer_rsrc_provider_init_or_exit(provider, 1); + kfree(provider); +} + +struct ub_buffer_rsrc_list +ub_buffer_rsrc_provider_query_free_rsrcs( +struct ub_buffer_rsrc_provider *provider) +{ + struct ub_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 +ub_buffer_rsrc_provider_allocate_page_range( +struct ub_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 ub_buffer_rsrc_provider_free_page_range( +struct ub_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 f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_buffer_rsrc_provider.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_buffer_rsrc_provider.h Tue Oct 3 16:29:44 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 UB_BUFFER_RSRC_PROVIDER_H +#define UB_BUFFER_RSRC_PROVIDER_H + +#include +#include +#include + +struct ub_buffer_rsrc_list { + u32 page_ranges; + u32 page_range_page_count; +}; + +static inline struct ub_buffer_rsrc_list +ub_buffer_rsrc_list_null(void) +{ + struct ub_buffer_rsrc_list list; + memset(&list, 0, sizeof(list)); + return list; +} + +static inline int +ub_buffer_rsrc_list_subset_of(struct ub_buffer_rsrc_list *a, +struct ub_buffer_rsrc_list *b) +{ + return ((a->page_ranges <= b->page_ranges) && + (a->page_range_page_count <= b->page_range_page_count)); +} + +static inline void +ub_buffer_rsrc_list_plus_equals(struct ub_buffer_rsrc_list *a, +struct ub_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 ub_buffer_rsrc_provider * +ub_allocate_buffer_rsrc_provider( +struct ub_buffer_rsrc_list rsrc_allocation); + +extern void +ub_free_buffer_rsrc_provider( +struct ub_buffer_rsrc_provider *provider); + +extern struct ub_buffer_rsrc_list +ub_buffer_rsrc_provider_query_free_rsrcs( +struct ub_buffer_rsrc_provider *provider); + +extern unsigned long +ub_buffer_rsrc_provider_allocate_page_range( +struct ub_buffer_rsrc_provider *provider, unsigned long page_count); + +extern void +ub_buffer_rsrc_provider_free_page_range( +struct ub_buffer_rsrc_provider *provider, unsigned long page_range); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_callback.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_callback.c Tue Oct 3 16:29:44 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 "ub_callback.h" + +void ub_callback_serialiser_function(void *context) +{ + struct ub_callback_serialiser *serialiser = + (struct ub_callback_serialiser *)context; + unsigned long flags; + + spin_lock_irqsave(&serialiser->lock, flags); + while (!list_empty(&serialiser->list) && !serialiser->running) { + struct ub_callback *callback = + ub_callback_link_to(serialiser->list.next); + list_del_init(ub_callback_to_link(callback)); + serialiser->running = 1; + spin_unlock_irqrestore(&serialiser->lock, flags); + ub_callback_complete_synchronously(callback); + spin_lock_irqsave(&serialiser->lock, flags); + serialiser->running = 0; + } + spin_unlock_irqrestore(&serialiser->lock, flags); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_callback.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_callback.h Tue Oct 3 16:29:44 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 UB_CALLBACK_H +#define UB_CALLBACK_H + +#include +#include +#include "ub_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 ub_callback { + struct ub_work work; + usbif_error error; +}; + +typedef void ub_callback_function(struct ub_callback *callback); + +static inline void +ub_callback_init(struct ub_callback *callback, +ub_callback_function *function) +{ + ub_work_init(&callback->work, (void (*)(void *))function, + callback); + callback->error = USBIF_ERROR_SUCCESS; +} + +static inline struct list_head * +ub_callback_to_link(struct ub_callback *callback) +{ + return ub_work_to_link(&callback->work); +} + +static inline struct ub_callback * +ub_callback_link_to(struct list_head *link) +{ + return container_of(ub_work_link_to(link), + struct ub_callback, work); +} + +static inline void +ub_callback_complete(struct ub_callback *callback, usbif_error error) +{ + callback->error = error; + ub_work_schedule(&callback->work); +} + +static inline void +ub_callback_success(struct ub_callback *callback) +{ + ub_callback_complete(callback, 0); +} + +static inline void +ub_callback_set_error(struct ub_callback *callback, +usbif_error error) +{ + callback->error = error; +} + +static inline void +ub_callback_complete_synchronously(struct ub_callback *callback) +{ + ub_work_perform_synchronously(&callback->work); +} + +static inline usbif_error +ub_callback_query_error(struct ub_callback *callback) +{ + return callback->error; +} + +struct ub_callback_serialiser { + spinlock_t lock; + struct list_head list; + struct ub_work work; + int running; +}; + +void ub_callback_serialiser_function(void *context); + +#define UB_CALLBACK_SERIALISER_INIT( name ) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .list = LIST_HEAD_INIT( name.list ), \ + .work = UB_WORK_INIT \ + ( name.work, ub_callback_serialiser_function, &name ),\ + .running = 0 \ +} + +#define UB_CALLBACK_SERIALISER( name ) \ +struct ub_callback_serialiser name = \ +UB_CALLBACK_SERIALISER_INIT( name ) + +static inline void +ub_callback_serialiser_complete_callback( +struct ub_callback_serialiser *serialiser, +struct ub_callback *callback, usbif_error error) +{ + unsigned long flags; + ub_callback_set_error(callback, error); + spin_lock_irqsave(&serialiser->lock, flags); + list_add_tail(ub_callback_to_link(callback), &serialiser->list); + spin_unlock_irqrestore(&serialiser->lock, flags); + ub_work_schedule(&serialiser->work); +} + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_channel.h Tue Oct 3 16:29:44 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 UB_CHANNEL_H +#define UB_CHANNEL_H + +#include +#include "ub_callback.h" + +struct ub_channel_ibmsg { + struct ub_callback callback; + struct usbif_request request; +}; + +static inline struct list_head * +ub_channel_ibmsg_to_link(struct ub_channel_ibmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct ub_callback * +ub_channel_ibmsg_to_callback(struct ub_channel_ibmsg *message) +{ + return &message->callback; +} + +static inline struct ub_channel_ibmsg * +ub_channel_ibmsg_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_channel_ibmsg, callback); +} + +static inline void +ub_channel_ibmsg_init(struct ub_channel_ibmsg *message, +ub_callback_function *callback) +{ + ub_callback_init(ub_channel_ibmsg_to_callback(message), + callback); +} + +struct ub_channel_obmsg { + struct ub_callback callback; + struct usbif_response response; +}; + +static inline struct list_head * +ub_channel_obmsg_to_link(struct ub_channel_obmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct ub_callback * +ub_channel_obmsg_to_callback(struct ub_channel_obmsg *message) +{ + return &message->callback; +} + +static inline struct ub_channel_obmsg * +ub_channel_obmsg_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_channel_obmsg, callback); +} + +static inline void +ub_channel_obmsg_init(struct ub_channel_obmsg *message, +ub_callback_function *callback) +{ + ub_callback_init(ub_channel_obmsg_to_callback(message), + callback); +} + +struct ub_channel; + +typedef void +ub_channel_submit_message_function(struct ub_channel *channel, +struct ub_channel_obmsg *message); + +typedef void +ub_channel_connect_function(void *client_context); + +typedef void +ub_channel_handle_message_function(void *client_context, +struct ub_channel_ibmsg *message); + +typedef void +ub_channel_disconnect_function(void *client_context, +struct ub_callback *callback); + +struct ub_channel { + ub_channel_submit_message_function *submit_message; + void *client_context; + ub_channel_connect_function *connect; + ub_channel_handle_message_function *handle_message; + ub_channel_disconnect_function *disconnect; +}; + +static inline void +ub_channel_init(struct ub_channel *channel, +ub_channel_submit_message_function *submit_message ) +{ + channel->submit_message = submit_message; +} + +static inline void ub_channel_connect(struct ub_channel *channel) +{ + channel->connect(channel->client_context); +} + +static inline void +ub_channel_handle_message(struct ub_channel *channel, +struct ub_channel_ibmsg *message) +{ + channel->handle_message(channel->client_context, message); +} + +static inline void +ub_channel_disconnect(struct ub_channel *channel, +struct ub_callback *callback) +{ + channel->disconnect(channel->client_context, callback); +} + +static inline void +ub_channel_install_client(struct ub_channel *channel, +void *client_context, ub_channel_connect_function *connect, +ub_channel_handle_message_function *handle_message, +ub_channel_disconnect_function *disconnect) +{ + channel->client_context = client_context; + channel->connect = connect; + channel->handle_message = handle_message; + channel->disconnect = disconnect; +} + +static inline void +ub_channel_submit_message(struct ub_channel *channel, +struct ub_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + channel->submit_message(channel, message); +} + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_device.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_device.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,766 @@ +/*****************************************************************************/ +/* 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 "ub_device.h" +#include "ub_ep.h" +#include "ub_trace.h" +#include "ub_xb_channel.h" + +#define UB_DEVICE_EP_COUNT 32 + +static struct usb_driver ub_device_usb_driver; + +static DECLARE_RWSEM(ub_device_list_sem); +static LIST_HEAD(ub_device_list); + +typedef enum { + ub_device_state_i, + ub_device_state_i_up, + ub_device_state_i_cn, + ub_device_state_i_up_cn, + ub_device_state_i_up_cn_ud, + ub_device_state_i_up_cn_dn, + ub_device_state_i_up_cn_ud_dn +} ub_device_state; + +typedef enum { + ub_device_stimulus_up, /* USB probe */ + ub_device_stimulus_ud, /* USB disconnect */ + ub_device_stimulus_cn, /* Gateway connect */ + ub_device_stimulus_dn, /* Gateway disconnect */ + ub_device_stimulus_ed /* Endpoints disconnected */ +} ub_device_stimulus; + +struct ub_ep_disconnect { + struct ub_callback callback; + struct ub_device *device; +}; + +struct ub_device { + struct xenbus_device *dev; + struct list_head link; + struct usb_device *usbdev; + char *path; + spinlock_t lock; + ub_device_state state; + int usbdev_is_connected; + struct ub_ep ep[UB_DEVICE_EP_COUNT]; + struct ub_ep_disconnect ep_disconnect[UB_DEVICE_EP_COUNT]; + int ep_disconnects_out; + struct ub_callback *gw_disconnect_callback; + int usb_disconnect_done; + struct ub_xb_channel channel; + struct ub_gw gw; +}; + +static void ub_device_handle_stimulus(struct ub_device *device, +ub_device_stimulus stimulus); + +domid_t ub_device_query_remote_domain(struct ub_device *device) +{ + trace(); + return device->dev->otherend_id; +} + +struct usb_device *ub_device_query_usbdev(struct ub_device *device) +{ + trace(); + return device->usbdev; +} + +static inline struct ub_device *ub_device_gw_to(struct ub_gw *gw) +{ + return container_of(gw, struct ub_device, gw); +} + +static int ub_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 ub_device *device = NULL, *cur = NULL; + trace(); + dev_info(&usbdev->dev, "probe for %s\n", usbdev->dev.bus_id); + down_read(&ub_device_list_sem); + list_for_each_entry(cur, &ub_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( + &ub_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; + ub_device_handle_stimulus(device, ub_device_stimulus_up); + spin_unlock_irq(&device->lock); + exit_interface_already_claimed: + exit_no_device: + up_read(&ub_device_list_sem); + return return_value; +} + +static void ub_device_disconnect_usb_device(struct usb_interface *intf) +{ + struct ub_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->usb_disconnect_done = 0; + ub_device_handle_stimulus(device, ub_device_stimulus_ud); + spin_unlock_irq(&device->lock); + ub_work_until(device->usb_disconnect_done); + 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( + &ub_device_usb_driver, other_intf); + } + } + usb_set_intfdata(intf, NULL); + usb_put_dev(usbdev); + } +} + +static void ub_device_gw_connect(struct ub_gw *gw) +{ + struct ub_device *device = ub_device_gw_to(gw); + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + ub_device_handle_stimulus(device, ub_device_stimulus_cn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +ub_device_handle_probe(struct ub_device *device, struct ub_gw_tra *tra) +{ + tra->status.probe.result = device->usbdev_is_connected ? + USBIF_PROBE_RESULT_DEVICE_PRESENT : + USBIF_PROBE_RESULT_NO_DEVICE; + ub_gw_tra_complete(tra, USBIF_ERROR_SUCCESS); +} + +int ub_device_reset(struct ub_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; + } + } + spin_unlock_irq(&device->lock); + return status; +} + +static void +ub_device_handle_reset(struct ub_device *device, struct ub_gw_tra *tra) +{ + usbif_error error = USBIF_ERROR_SUCCESS; + int result; + trace(); + result = ub_device_reset(device); + if (result < 0) { + error = USBIF_ERROR_NO_DEVICE; + goto complete; + } + tra->status.reset.result = result; + complete: + ub_gw_tra_complete(tra, error); +} + +static void +ub_device_handle_io(struct ub_device *device, struct ub_gw_tra *tra) +{ + int ep_index = tra->parameters.io.header.endpoint + + ( tra->parameters.io.header.direction ? 16 : 0 ); + trace(); + if( ep_index < UB_DEVICE_EP_COUNT ) + ub_ep_handle_io(&device->ep[ep_index], tra); + else + ub_gw_tra_complete(tra, USBIF_ERROR_INVALID_PARAMETER); +} + +static void +ub_device_handle_stall(struct ub_device *device, struct ub_gw_tra *tra) +{ + int ep_index = tra->parameters.stall.endpoint + + ( tra->parameters.stall.direction ? 16 : 0 ); + trace(); + if( ep_index < UB_DEVICE_EP_COUNT ) { + ub_ep_stall(&device->ep[ep_index]); + ub_gw_tra_complete(tra, USBIF_ERROR_SUCCESS); + } else { + ub_gw_tra_complete(tra, USBIF_ERROR_INVALID_PARAMETER); + } +} + +static void ub_device_gw_tra(struct ub_gw *gw, struct ub_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct ub_device *device = ub_device_gw_to(gw); + switch (tra->parameters.header.tra_type) { + case USBIF_TRA_TYPE_PROBE: + ub_device_handle_probe(device, tra); + break; + case USBIF_TRA_TYPE_RESET: + ub_device_handle_reset(device, tra); + break; + case USBIF_TRA_TYPE_IO: + ub_device_handle_io(device, tra); + break; + case USBIF_TRA_TYPE_STALL: + ub_device_handle_stall(device, tra); + break; + default: + ub_gw_tra_complete(tra, USBIF_ERROR_INVALID_PARAMETER); + break; + } +} + +static void +ub_device_gw_disconnect(struct ub_gw *gw, struct ub_callback *callback) +{ + struct ub_device *device = ub_device_gw_to(gw); + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + device->gw_disconnect_callback = callback; + ub_device_handle_stimulus(device, ub_device_stimulus_dn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void ub_device_invalid_stimulus(struct ub_device *device, +ub_device_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: device %p in state %d" + " received invalid stimulus %d", device, device->state, stimulus); +} + +static void ub_device_connect_endpoints(struct ub_device *device) +{ + int i; + trace(); + for (i = 0; i < UB_DEVICE_EP_COUNT; i++) + ub_ep_connect(&device->ep[i]); +} + +static void ub_device_disconnect_endpoints(struct ub_device *device) +{ + int i; + trace(); + device->ep_disconnects_out = UB_DEVICE_EP_COUNT; + for (i = 0; i < UB_DEVICE_EP_COUNT; i++) + ub_ep_disconnect(&device->ep[i], + &device->ep_disconnect[i].callback); +} + +static void ub_device_ep_disconnect_callback(struct ub_callback *callback) +{ + struct ub_device *device = container_of(callback, + struct ub_ep_disconnect, callback)->device; + unsigned long flags; + trace(); + spin_lock_irqsave(&device->lock, flags); + if (--device->ep_disconnects_out == 0) + ub_device_handle_stimulus(device, ub_device_stimulus_ed); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void ub_device_complete_usb_disconnect(struct ub_device *device) +{ + trace(); + device->usb_disconnect_done = 1; + ub_work_wake_up(); +} + +static void ub_device_complete_gw_disconnect(struct ub_device *device) +{ + trace(); + ub_callback_success(device->gw_disconnect_callback); +} + +static void +ub_device_handle_stimulus(struct ub_device *device, +ub_device_stimulus stimulus) +{ + trace_info("device %p in state %d received stimulus %d", device, + device->state, stimulus); + switch (device->state) { + case ub_device_state_i: + /* USB disconnected */ + /* Gateway disconnected */ + /* Endpoints disconnected */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case ub_device_stimulus_up: + device->state = ub_device_state_i_up; + break; + case ub_device_stimulus_cn: + device->state = ub_device_state_i_cn; + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_up: + /* USB probed */ + /* Gateway disconnected */ + /* Endpoints disconnected */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case ub_device_stimulus_ud: + device->state = ub_device_state_i; + ub_device_complete_usb_disconnect(device); + break; + case ub_device_stimulus_cn: + device->state = ub_device_state_i_up_cn; + ub_device_connect_endpoints(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_cn: + /* USB disconnected */ + /* Gateway connected */ + /* Endpoints disconnected */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case ub_device_stimulus_up: + device->state = ub_device_state_i_up_cn; + ub_device_connect_endpoints(device); + break; + case ub_device_stimulus_dn: + device->state = ub_device_state_i; + ub_device_complete_gw_disconnect(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_up_cn: + /* USB probed */ + /* Gateway connected */ + /* Endpoints connected */ + /* USB disconnect not outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case ub_device_stimulus_ud: + device->state = ub_device_state_i_up_cn_ud; + ub_device_disconnect_endpoints(device); + break; + case ub_device_stimulus_dn: + device->state = ub_device_state_i_up_cn_dn; + ub_device_disconnect_endpoints(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_up_cn_ud: + /* USB disconnecting */ + /* Gateway connected */ + /* Endpoints disconnecting */ + /* USB disconnect outstanding */ + /* Gateway disconnect not outstanding */ + switch (stimulus) { + case ub_device_stimulus_dn: + device->state = ub_device_state_i_up_cn_ud_dn; + break; + case ub_device_stimulus_ed: + device->state = ub_device_state_i_cn; + ub_device_complete_usb_disconnect(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_up_cn_dn: + /* USB probed */ + /* Gateway disconnecting */ + /* Endpoints disconnecting */ + /* USB disconnect not outstanding */ + /* Gateway disconnect outstanding */ + switch (stimulus) { + case ub_device_stimulus_ud: + device->state = ub_device_state_i_up_cn_ud_dn; + break; + case ub_device_stimulus_ed: + device->state = ub_device_state_i_up; + ub_device_complete_gw_disconnect(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + case ub_device_state_i_up_cn_ud_dn: + /* USB disconnecting */ + /* Gateway disconnecting */ + /* Endpoints disconnecting */ + /* USB disconnect outstanding */ + /* Gateway disconnect outstanding */ + switch (stimulus) { + case ub_device_stimulus_ed: + device->state = ub_device_state_i; + ub_device_complete_usb_disconnect(device); + ub_device_complete_gw_disconnect(device); + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } + break; + default: + ub_device_invalid_stimulus(device, stimulus); + break; + } +} + +static int +ub_device_init_or_exit(struct ub_device *device, struct xenbus_device *dev, +int exit) +{ + struct xenbus_transaction tra; + int return_value = 0, i = UB_DEVICE_EP_COUNT; + trace(); + if (exit) + goto exit_path; + device->dev = dev; + INIT_LIST_HEAD(&device->link); + spin_lock_init(&device->lock); + device->state = ub_device_state_i; + device->usbdev_is_connected = 0; + for (i = 0; i < UB_DEVICE_EP_COUNT; i++) { + struct ub_ep *ep = &device->ep[i]; + struct ub_ep_disconnect *ep_disconnect = + &device->ep_disconnect[i]; + return_value = ub_ep_init(ep, device); + if (return_value != 0) + goto exit_no_ep; + ub_callback_init(&ep_disconnect->callback, + ub_device_ep_disconnect_callback); + ep_disconnect->device = device; + } + return_value = ub_xb_channel_init(&device->channel); + if (return_value != 0) + goto exit_no_channel; + return_value = ub_gw_init(&device->gw, + ub_xb_channel_to_channel(&device->channel), + ub_device_gw_connect, + ub_device_gw_tra, + ub_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(&ub_device_list_sem); + list_add_tail(&device->link, &ub_device_list); + up_write(&ub_device_list_sem); + bus_rescan_devices(&usb_bus_type); + ub_xb_channel_connect(&device->channel, dev); + return 0; + exit_path: + ub_xb_channel_disconnect(&device->channel); + down_write(&ub_device_list_sem); + list_del_init(&device->link); + up_write(&ub_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( + &ub_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: + ub_gw_exit(&device->gw); + exit_no_gw: + ub_xb_channel_exit(&device->channel); + exit_no_channel: + exit_no_ep: + for( i--; i > 0; i-- ){ + struct ub_ep *ep = &device->ep[i]; + ub_ep_exit(ep); + } + return return_value; +} + +static int ub_device_init(struct ub_device *device, struct xenbus_device *dev) +{ + trace(); + return ub_device_init_or_exit(device, dev, 0); +} + +static void ub_device_exit(struct ub_device *device) +{ + trace(); + (void)ub_device_init_or_exit(device, NULL, 1); +} + +static int +ub_device_probe_or_remove_xenbus_device(struct xenbus_device *dev, int remove) +{ + int return_value = 0; + struct ub_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->dev.driver_data = device; + return_value = ub_device_init(device, dev); + if (return_value != 0) + goto exit_no_init; + return 0; + remove_path: + device = dev->dev.driver_data; + ub_device_exit(device); + exit_no_init: + dev->dev.driver_data = NULL; + kfree(device); + exit_no_device: + return return_value; +} + +static int ub_device_probe_xenbus_device(struct xenbus_device *dev, +const struct xenbus_device_id *id) +{ + trace(); + return ub_device_probe_or_remove_xenbus_device(dev, 0); +} + +static int ub_device_remove_xenbus_device(struct xenbus_device *dev) +{ + trace(); + return ub_device_probe_or_remove_xenbus_device(dev, 1); +} + +static void +ub_device_frontend_changed(struct xenbus_device *dev, XenbusState state) +{ + struct ub_device *device = dev->dev.driver_data; + trace(); + ub_xb_channel_frontend_changed(&device->channel, dev, state); +} + +static struct usb_device_id ub_device_usb_id_table[] = { + {.driver_info = 1}, /* Matches all devices. */ + {} +}; + +static struct usb_driver ub_device_usb_driver = { + .name = "ub", + .probe = ub_device_probe_usb_device, + .disconnect = ub_device_disconnect_usb_device, + .id_table = ub_device_usb_id_table, +}; + +static struct xenbus_device_id ub_device_xenbus_ids[] = { + {"usbport"}, + {""} +}; + +static struct xenbus_driver ub_device_xenbus_driver = { + .name = "usbport", + .owner = THIS_MODULE, + .ids = ub_device_xenbus_ids, + .probe = ub_device_probe_xenbus_device, + .remove = ub_device_remove_xenbus_device, + .otherend_changed = ub_device_frontend_changed, +}; + +static int __init ub_device_class_init(void) +{ + int return_value = 0; + trace(); + return_value = ub_ep_class_init(); + if (return_value != 0) + goto exit_no_ep_class; + return_value = usb_register(&ub_device_usb_driver); + if (return_value != 0) + goto exit_no_usb_register; + return_value = xenbus_register_backend(&ub_device_xenbus_driver); + if (return_value != 0) + goto exit_no_xenbus_register; + return 0; + exit_no_xenbus_register: + usb_deregister(&ub_device_usb_driver); + exit_no_usb_register: + ub_ep_class_exit(); + exit_no_ep_class: + return return_value; +} + +__initcall(ub_device_class_init); diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_device.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_device.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,28 @@ +/*****************************************************************************/ +/* 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 UB_DEVICE_H +#define UB_DEVICE_H + +struct ub_device; + +domid_t ub_device_query_remote_domain(struct ub_device *device); +struct usb_device * ub_device_query_usbdev(struct ub_device *device); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_ep.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_ep.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,416 @@ +/*****************************************************************************/ +/* 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 "ub_device.h" +#include "ub_ep.h" +#include "ub_trace.h" +#include "ub_xb_channel.h" + +/* Between disconnect and connect we purge requests with an error indicating */ +/* the device is disconnected. It either is really disconnected or the */ +/* gateway is disconnected in which case the FE will not see the error code. */ +/* When connected, we submit requests maintaining submission order. */ +/* If an IO completes with an error then we are notified before the */ +/* completion returns to the usb stack allowing us to purge all outstanding */ +/* requests whilst the URB queue is stalled. In this case requests are */ +/* purged indicating they have been unlinked so the FE can requeue them and */ +/* if necessary resubmit after outstanding FE error completions are */ +/* quiesced. */ +/* Explicit unlinks in the FE result in a request to stall the endpoint. All */ +/* queued URBs are unlinked and returned. The FE can again resubmit any as */ +/* necessary. */ +/* The FE can distinguish between which URBs have been intentionally */ +/* unlinked by the FE and which have been unlinked as a consequence of an */ +/* unlink or error since the FE sees all FE unlink requests and knows which */ +/* are which. */ +/* After an error or unlink, the endpoint stalls and subsequent requests */ +/* are purged with an unlinked error until one arrives with the clear stall */ +/* flag set when we go back to normal operation. */ +/* If the gateway disconnects then we purge and wait for quiesce before */ +/* completing the gateway disconnect. */ + +typedef enum { + ub_ep_stimulus_cn, /* Connect */ + ub_ep_stimulus_dn, /* Disconnect */ + ub_ep_stimulus_tc, /* tra queued with clear stall flag */ + ub_ep_stimulus_tq, /* tra queued without clear stall flag */ + ub_ep_stimulus_rc, /* rsrc completed */ + ub_ep_stimulus_st, /* stall */ + ub_ep_stimulus_ri /* test_io rsrcs idle (reent) */ +} ub_ep_stimulus; + +static void ub_ep_handle_stimulus(struct ub_ep *ep, ub_ep_stimulus stimulus); + +domid_t ub_ep_query_remote_domain(struct ub_ep *ep) +{ + trace(); + return ub_device_query_remote_domain(ep->device); +} + +struct usb_device *ub_ep_query_usbdev(struct ub_ep *ep) +{ + trace(); + return ub_device_query_usbdev(ep->device); +} + +void ub_ep_connect(struct ub_ep *ep) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + ub_ep_handle_stimulus(ep, ub_ep_stimulus_cn); + spin_unlock_irqrestore(&ep->lock, flags); +} + +void ub_ep_disconnect(struct ub_ep *ep, struct ub_callback *callback) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + ep->disconnect_callback = callback; + ub_ep_handle_stimulus(ep, ub_ep_stimulus_dn); + spin_unlock_irqrestore(&ep->lock, flags); +} + +void ub_ep_handle_io(struct ub_ep *ep, struct ub_gw_tra *tra) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + list_add_tail(ub_gw_tra_to_link(tra), &ep->tra_list); + if (tra->parameters.io.header.flags & USBIF_IO_FLAGS_CLEAR_STALL) + ub_ep_handle_stimulus(ep, ub_ep_stimulus_tc); + else + ub_ep_handle_stimulus(ep, ub_ep_stimulus_tq); + spin_unlock_irqrestore(&ep->lock, flags); +} + +void ub_ep_stall(struct ub_ep *ep) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + ub_ep_handle_stimulus(ep, ub_ep_stimulus_st); + spin_unlock_irqrestore(&ep->lock, flags); +} + +static void ub_ep_invalid_stimulus(struct ub_ep *ep, ub_ep_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: ep %p in state %d" + " received invalid stimulus %d", ep, ep->state, stimulus); +} + +static void ub_ep_purge_io(struct ub_ep *ep, usbif_error error) +{ + int i; + trace(); + while (!list_empty(&ep->tra_list)) { + struct ub_gw_tra *tra = list_entry(ep->tra_list.next, + struct ub_gw_tra, callback.work.link); + list_del_init(ub_gw_tra_to_link(tra)); + ub_gw_tra_complete(tra, error); + } + for (i = 0; i < UB_EP_QUOTA; i++) { + struct ub_rsrc *rsrc = ep->rsrcs[i]; + ub_rsrc_flush_io(rsrc); + } +} + +static void ub_ep_test_io(struct ub_ep *ep) +{ + trace(); + if (ep->rsrcs_out == 0) + ub_ep_handle_stimulus(ep, ub_ep_stimulus_ri); +} + +static void ub_ep_kick_io(struct ub_ep *ep) +{ + trace(); + while (!list_empty(&ep->tra_list) && + !list_empty(&ep->free_rsrc_list)) { + struct ub_gw_tra *tra = list_entry(ep->tra_list.next, + struct ub_gw_tra, callback.work.link); + struct ub_rsrc *rsrc = list_entry( + ep->free_rsrc_list.next, + struct ub_rsrc, link); + list_del_init(ub_gw_tra_to_link(tra)); + list_del_init(&rsrc->link); + ep->rsrcs_out++; + ub_rsrc_start_io(rsrc, tra); + } +} + +void ub_ep_rsrc_completed_io(struct ub_ep *ep, struct ub_rsrc *rsrc) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + list_add(&rsrc->link, &ep->free_rsrc_list); + ep->rsrcs_out--; + ub_ep_handle_stimulus(ep, ub_ep_stimulus_rc); + spin_unlock_irqrestore(&ep->lock, flags); +} + +static void ub_ep_complete_disconnect(struct ub_ep *ep) +{ + trace(); + ub_callback_success(ep->disconnect_callback); +} + +static void ub_ep_handle_stimulus(struct ub_ep *ep, ub_ep_stimulus stimulus) +{ + trace_info("ep %p in state %d received stimulus %d", ep, + ep->state, stimulus); + switch (ep->state) { + case ub_ep_state_i: + /* Disconnected */ + /* I/Os idle */ + /* Disconnect not outstanding */ + switch (stimulus) { + case ub_ep_stimulus_cn: + ep->state = ub_ep_state_i_cn; + break; + case ub_ep_stimulus_tc: + case ub_ep_stimulus_tq: + ub_ep_purge_io(ep, USBIF_ERROR_DISCONNECT); + break; + case ub_ep_stimulus_st: + break; + default: + ub_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + case ub_ep_state_i_cn: + /* Connected */ + /* Maybe I/Os in progress */ + /* Disconnect not outstanding */ + switch (stimulus) { + case ub_ep_stimulus_dn: + ep->state = ub_ep_state_i_cn_dn; + ub_ep_purge_io(ep, USBIF_ERROR_DISCONNECT); + ub_ep_test_io(ep); + break; + case ub_ep_stimulus_tc: + case ub_ep_stimulus_tq: + case ub_ep_stimulus_rc: + ub_ep_kick_io(ep); + break; + case ub_ep_stimulus_st: + ep->state = ub_ep_state_i_cn_er; + ub_ep_purge_io(ep, USBIF_ERROR_UNLINKED); + break; + default: + ub_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + case ub_ep_state_i_cn_dn: + /* Connected */ + /* I/Os in progress/Testing I/Os */ + /* Disconnect outstanding */ + switch (stimulus) { + case ub_ep_stimulus_tc: + case ub_ep_stimulus_tq: + case ub_ep_stimulus_rc: + ub_ep_purge_io(ep, USBIF_ERROR_DISCONNECT); + ub_ep_test_io(ep); + break; + case ub_ep_stimulus_st: + break; + case ub_ep_stimulus_ri: + ep->state = ub_ep_state_i; + ub_ep_complete_disconnect(ep); + break; + default: + ub_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + case ub_ep_state_i_cn_er: + /* Connected */ + /* Maybe I/Os in progress */ + /* Disconnect not outstanding */ + /* Stalling */ + switch (stimulus) { + case ub_ep_stimulus_dn: + ep->state = ub_ep_state_i_cn_dn; + ub_ep_purge_io(ep, USBIF_ERROR_DISCONNECT); + ub_ep_test_io(ep); + break; + case ub_ep_stimulus_tc: + ep->state = ub_ep_state_i_cn; + ub_ep_kick_io(ep); + break; + case ub_ep_stimulus_tq: + ub_ep_purge_io(ep, USBIF_ERROR_UNLINKED); + break; + case ub_ep_stimulus_rc: + case ub_ep_stimulus_st: + break; + default: + ub_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + default: + ub_ep_invalid_stimulus(ep, stimulus); + break; + } +} + +static int +ub_ep_init_or_exit(struct ub_ep *ep, struct ub_device *device, int exit) +{ + int return_value = 0, i = UB_EP_QUOTA; + trace(); + if (exit) + goto exit_path; + ep->device = device; + spin_lock_init(&ep->lock); + ep->state = ub_ep_state_i; + ep->rsrcs_out = 0; + INIT_LIST_HEAD(&ep->tra_list); + INIT_LIST_HEAD(&ep->free_rsrc_list); + for (i = 0; i < UB_EP_QUOTA; i++) { + ep->rsrcs[i]=kzalloc(sizeof(struct ub_rsrc),GFP_KERNEL); + if (ep->rsrcs[i]==NULL) { + return_value = -ENOMEM; + goto exit_no_rsrc; + } + } + for (i = 0; i < UB_EP_QUOTA; i++) { + struct ub_rsrc *rsrc = ep->rsrcs[i]; + return_value = ub_rsrc_init(rsrc, ep); + if (return_value != 0) + goto exit_no_rsrc_init; + list_add(&rsrc->link, &ep->free_rsrc_list); + } + return 0; + exit_path: + exit_no_rsrc_init: + while (!list_empty(&ep->free_rsrc_list)) { + struct ub_rsrc *rsrc = list_entry(ep->free_rsrc_list.next, + struct ub_rsrc, link); + list_del_init(&rsrc->link); + ub_rsrc_exit(rsrc); + } + exit_no_rsrc: + for(i--;i>=0;i--) + kfree(ep->rsrcs[i]); + return return_value; +} + +int ub_ep_init(struct ub_ep *ep, struct ub_device *device) +{ + trace(); + return ub_ep_init_or_exit(ep, device, 0); +} + +void ub_ep_exit(struct ub_ep *ep) +{ + trace(); + (void)ub_ep_init_or_exit(ep, NULL, 1); +} + +int __init ub_ep_class_init(void) +{ + trace(); + return ub_rsrc_class_init(); +} + +void ub_ep_class_exit(void) +{ + trace(); + ub_rsrc_class_exit(); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_ep.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_ep.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* 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 UB_EP_H +#define UB_EP_H + +#include "ub_rsrc.h" + +#define UB_EP_QUOTA 4 + +typedef enum { + ub_ep_state_i, + ub_ep_state_i_cn, + ub_ep_state_i_cn_dn, + ub_ep_state_i_cn_er +} ub_ep_state; + +struct ub_ep { + struct ub_device *device; + spinlock_t lock; + ub_ep_state state; + unsigned long rsrcs_out; + struct list_head tra_list; + struct list_head free_rsrc_list; + struct ub_rsrc *rsrcs[UB_EP_QUOTA]; + struct ub_callback *disconnect_callback; +}; + +domid_t ub_ep_query_remote_domain(struct ub_ep *ep); +struct usb_device *ub_ep_query_usbdev(struct ub_ep *ep); +void ub_ep_connect(struct ub_ep *ep); +void ub_ep_disconnect(struct ub_ep *ep,struct ub_callback *callback); +void ub_ep_handle_io(struct ub_ep *ep, struct ub_gw_tra *tra); +void ub_ep_stall(struct ub_ep *ep); +void ub_ep_rsrc_completed_io(struct ub_ep *ep, struct ub_rsrc *rsrc); +int ub_ep_init(struct ub_ep *ep, struct ub_device *device); +void ub_ep_exit(struct ub_ep *ep); +int ub_ep_class_init(void); +void ub_ep_class_exit(void); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw.c Tue Oct 3 16:29:44 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 "ub_gw.h" +#include "ub_trace.h" + +typedef enum { + ub_gw_stimulus_cc, /* Channel connect. */ + ub_gw_stimulus_cm, /* Channel message. */ + ub_gw_stimulus_cd, /* Channel disconnect. */ + ub_gw_stimulus_kc, /* Kick channel messages completed. */ + ub_gw_stimulus_tc, /* Target rsrc completed. */ + ub_gw_stimulus_ti, /* Target rsrcs idle. */ + ub_gw_stimulus_lc, /* Client connect completed. */ + ub_gw_stimulus_lg, /* Client disconnect called. */ + ub_gw_stimulus_ld, /* Client disconnect completed. */ +} ub_gw_stimulus; + +static void ub_gw_handle_stimulus(struct ub_gw *gw, +ub_gw_stimulus stimulus); + +static void ub_gw_channel_connect(void *context) +{ + struct ub_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + ub_gw_handle_stimulus(gw, ub_gw_stimulus_cc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void ub_gw_handle_channel_message(void *context, +struct ub_channel_ibmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct ub_gw *gw = context; + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(ub_channel_ibmsg_to_link(message), + &gw->channel_message_list); + ub_gw_handle_stimulus(gw,ub_gw_stimulus_cm); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void +ub_gw_channel_disconnect(void *context, struct ub_callback *callback) +{ + struct ub_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + gw->channel_disconnect_callback = callback; + ub_gw_handle_stimulus(gw, ub_gw_stimulus_cd); + spin_unlock_irqrestore(&gw->lock, flags); +} + +void ub_gw_submit_channel_message(struct ub_gw *gw, +struct ub_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + ub_channel_submit_message(gw->channel, message); +} + +void ub_gw_submit_tra_to_client(struct ub_gw *gw, +struct ub_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + gw->handle_tra(gw, tra); +} + +static void ub_gw_invalid_stimulus(struct ub_gw *gw, +ub_gw_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: gw %p in state %d" + "received invalid stimulus %d", gw, gw->state, stimulus); +} + +static void +ub_gw_kick_channel_messages(struct ub_gw *gw) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!gw->kick_channel_messages_out) { + gw->kick_channel_messages_out = 1; + (void)ub_work_schedule(&gw->kick_channel_messages_1_work); + } +} + +static void ub_gw_kick_channel_messages_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct ub_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 ub_channel_ibmsg *message; + struct ub_gw_rsrc *rsrc; + message = list_entry(gw->channel_message_list.next, + struct ub_channel_ibmsg, + callback.work.link); + rsrc = list_entry(gw->target_rsrc_list.next, + struct ub_gw_rsrc, + callback.work.link); + list_del_init(ub_channel_ibmsg_to_link(message)); + list_del_init(ub_gw_rsrc_to_link(rsrc)); + gw->target_rsrcs_out++; + spin_unlock_irqrestore(&gw->lock, flags); + ub_gw_rsrc_start_tra(rsrc, &message->request); + spin_lock_irqsave(&gw->lock, flags); + ub_callback_complete(ub_channel_ibmsg_to_callback( + message), error); + } + gw->kick_channel_messages_out = 0; + ub_gw_handle_stimulus(gw, ub_gw_stimulus_kc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void +ub_gw_kick_channel_messages_2(struct ub_callback *callback) +{ + struct ub_gw_rsrc *rsrc = ub_gw_rsrc_callback_to(callback); + struct ub_gw *gw = ub_gw_rsrc_query_gw(rsrc); + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(ub_gw_rsrc_to_link(rsrc), &gw->target_rsrc_list); + if (--gw->target_rsrcs_out != 0) { + ub_gw_handle_stimulus(gw, ub_gw_stimulus_tc); + } else { + ub_gw_handle_stimulus(gw, ub_gw_stimulus_ti); + } + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void ub_gw_complete_channel_messages(struct ub_gw *gw) +{ + trace(); + while (!list_empty(&gw->channel_message_list)) { + struct ub_channel_ibmsg *message = list_entry( + gw->channel_message_list.next, + struct ub_channel_ibmsg, + callback.work.link); + list_del_init(ub_channel_ibmsg_to_link(message)); + ub_callback_success(ub_channel_ibmsg_to_callback( + message)); + } +} + +static void ub_gw_connect_client(struct ub_gw *gw) +{ + trace(); + (void)ub_work_schedule(&gw->connect_client_1_work); +} + +static void ub_gw_connect_client_1(void *data) +{ + struct ub_gw *gw = data; + unsigned long flags; + trace(); + gw->connect(gw); + spin_lock_irqsave(&gw->lock, flags); + ub_gw_handle_stimulus(gw, ub_gw_stimulus_lc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void ub_gw_disconnect_client(struct ub_gw *gw) +{ + trace(); + (void)ub_work_schedule(&gw->disconnect_client_1_work); +} + +static void ub_gw_disconnect_client_1(void *data) +{ + struct ub_gw *gw = data; + unsigned long flags; + trace(); + gw->disconnect(gw, &gw->disconnect_client_2_callback); + spin_lock_irqsave(&gw->lock, flags); + ub_gw_handle_stimulus(gw, ub_gw_stimulus_lg); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void ub_gw_disconnect_client_2(struct ub_callback *callback) +{ + struct ub_gw *gw = container_of(callback, struct ub_gw, + disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + ub_gw_handle_stimulus(gw, ub_gw_stimulus_ld); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void ub_gw_test_target_rsrcs(struct ub_gw *gw) +{ + trace(); + if (gw->target_rsrcs_out == 0) + ub_gw_handle_stimulus(gw, ub_gw_stimulus_ti); +} + +static void ub_gw_complete_channel_disconnect(struct ub_gw *gw) +{ + trace(); + ub_callback_success(gw->channel_disconnect_callback); +} + +static int ub_gw_init_or_exit(struct ub_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 ub_gw_rsrc *rsrc = &gw->target_rsrcs[i]; + ub_gw_rsrc_init(rsrc, gw, + ub_gw_kick_channel_messages_2); + list_add_tail(ub_gw_rsrc_to_link(rsrc), + &gw->target_rsrc_list); + } + spin_lock_init(&gw->lock); + gw->state = ub_gw_state_i; + INIT_LIST_HEAD(&gw->channel_message_list); + ub_work_init(&gw->kick_channel_messages_1_work, + ub_gw_kick_channel_messages_1, gw); + ub_work_init(&gw->connect_client_1_work, + ub_gw_connect_client_1, gw); + ub_work_init(&gw->disconnect_client_1_work, + ub_gw_disconnect_client_1, gw); + ub_callback_init(&gw->disconnect_client_2_callback, + ub_gw_disconnect_client_2); + gw->kick_channel_messages_out = 0; + gw->target_rsrcs_out = 0; + return 0; + exit_path: + return return_value; +} + +int ub_gw_init(struct ub_gw *gw, struct ub_channel *channel, +ub_gw_connect_function *connect, +ub_gw_handle_tra_function *handle_tra, +ub_gw_disconnect_function *disconnect) +{ + trace(); + gw->channel = channel; + gw->connect = connect; + gw->handle_tra = handle_tra; + gw->disconnect = disconnect; + ub_channel_install_client(channel, gw, + ub_gw_channel_connect, + ub_gw_handle_channel_message, + ub_gw_channel_disconnect); + return ub_gw_init_or_exit(gw, 0); +} + +void ub_gw_exit(struct ub_gw *gw) +{ + trace(); + (void)ub_gw_init_or_exit(gw, 1); +} + +static void +ub_gw_handle_stimulus(struct ub_gw *gw, +ub_gw_stimulus stimulus) +{ + switch (gw->state) { + case ub_gw_state_i: + /* Channel disconnected. */ + /* Client disconnected. */ + /* No channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case ub_gw_stimulus_cc: + gw->state = ub_gw_state_i_cc; + ub_gw_connect_client(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_gw_state_i_cc: + /* Channel connected. */ + /* Client connecting. */ + /* Maybe channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case ub_gw_stimulus_cm: + break; + case ub_gw_stimulus_cd: + gw->state = ub_gw_state_i_cc_cd; + ub_gw_complete_channel_messages(gw); + break; + case ub_gw_stimulus_lc: + gw->state = ub_gw_state_i_cc_lc; + ub_gw_kick_channel_messages(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_gw_state_i_cc_cd: + /* Channel disconnecting. */ + /* Client connecting. */ + /* No channel messages queued. */ + /* Target rsrcs idle. */ + /* Not kicking cm. */ + switch (stimulus) { + case ub_gw_stimulus_lc: + gw->state = ub_gw_state_i_cc_cd_lc; + ub_gw_disconnect_client(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_gw_state_i_cc_lc: + /* Channel connected. */ + /* Client connected. */ + /* Maybe channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Maybe kicking cm. */ + switch (stimulus) { + case ub_gw_stimulus_cm: + ub_gw_kick_channel_messages(gw); + break; + case ub_gw_stimulus_cd: + gw->state = ub_gw_state_i_cc_lc_cd; + ub_gw_complete_channel_messages(gw); + ub_gw_kick_channel_messages(gw); + break; + case ub_gw_stimulus_kc: + break; + case ub_gw_stimulus_tc: + case ub_gw_stimulus_ti: + ub_gw_kick_channel_messages(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_gw_state_i_cc_lc_cd: + /* Channel disconnecting. */ + /* Client connected. */ + /* No channel messages queued. */ + /* Maybe target rsrcs busy. */ + /* Kicking cm. */ + switch (stimulus) { + case ub_gw_stimulus_kc: + gw->state = ub_gw_state_i_cc_cd_lc; + ub_gw_disconnect_client(gw); + break; + case ub_gw_stimulus_tc: + case ub_gw_stimulus_ti: + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_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 ub_gw_stimulus_tc: + case ub_gw_stimulus_ti: + break; + case ub_gw_stimulus_lg: + gw->state = ub_gw_state_i_cc_cd_lc_lg; + break; + case ub_gw_stimulus_ld: + gw->state = ub_gw_state_i_cc_cd_lc_ld; + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_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 ub_gw_stimulus_tc: + case ub_gw_stimulus_ti: + break; + case ub_gw_stimulus_ld: + gw->state = ub_gw_state_i_cc_cd_lc_lg_ld; + ub_gw_test_target_rsrcs(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_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 ub_gw_stimulus_tc: + case ub_gw_stimulus_ti: + break; + case ub_gw_stimulus_lg: + gw->state = + ub_gw_state_i_cc_cd_lc_lg_ld; + ub_gw_test_target_rsrcs(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case ub_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 ub_gw_stimulus_tc: + break; + case ub_gw_stimulus_ti: + gw->state = ub_gw_state_i; + ub_gw_complete_channel_disconnect(gw); + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + default: + ub_gw_invalid_stimulus(gw, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw.h Tue Oct 3 16:29:44 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 UB_GW_H +#define UB_GW_H + +#include +#include +#include "ub_callback.h" +#include "ub_channel.h" + +struct ub_gw_rsrc; + +struct ub_gw_tra { + struct ub_callback callback; + union usbif_parameters parameters; + union usbif_status status; +}; + +static inline struct ub_callback * +ub_gw_tra_to_callback(struct ub_gw_tra *tra) +{ + return &tra->callback; +} + +static inline struct ub_gw_tra * +ub_gw_tra_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_gw_tra, callback); +} + +static inline struct list_head * +ub_gw_tra_to_link(struct ub_gw_tra *tra) +{ + return ub_callback_to_link(ub_gw_tra_to_callback(tra)); +} + +static inline struct ub_gw_tra * +ub_gw_tra_link_to(struct list_head *link) +{ + return ub_gw_tra_callback_to(ub_callback_link_to(link)); +} + +static inline void +ub_gw_tra_init(struct ub_gw_tra *tra, +ub_callback_function *callback) +{ + ub_callback_init(ub_gw_tra_to_callback(tra), callback); +} + +static inline void +ub_gw_tra_complete(struct ub_gw_tra *tra, usbif_error error) +{ + ub_callback_complete(ub_gw_tra_to_callback(tra), error); +} + +static inline +usbif_error ub_gw_tra_query_error(struct ub_gw_tra *tra) +{ + return ub_callback_query_error(ub_gw_tra_to_callback(tra)); +} + +struct ub_gw_rsrc { + struct ub_callback callback; + struct ub_gw *gw; + struct ub_gw_tra tra; + struct ub_channel_obmsg channel_message; +}; + +static inline struct list_head * +ub_gw_rsrc_to_link(struct ub_gw_rsrc *rsrc) +{ + return &rsrc->callback.work.link; +} + +static inline struct ub_gw_rsrc * +ub_gw_rsrc_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_gw_rsrc, callback); +} + +static inline struct ub_gw * +ub_gw_rsrc_query_gw(struct ub_gw_rsrc *rsrc) +{ + return rsrc->gw; +} + +void +ub_gw_rsrc_init(struct ub_gw_rsrc *rsrc, struct ub_gw *gw, +ub_callback_function callback); + +void +ub_gw_rsrc_start_tra(struct ub_gw_rsrc *rsrc, +struct usbif_request *request); + +typedef enum { + ub_gw_state_i, + ub_gw_state_i_cc, + ub_gw_state_i_cc_cd, + ub_gw_state_i_cc_lc, + ub_gw_state_i_cc_lc_cd, + ub_gw_state_i_cc_cd_lc, + ub_gw_state_i_cc_cd_lc_lg, + ub_gw_state_i_cc_cd_lc_ld, + ub_gw_state_i_cc_cd_lc_lg_ld +} ub_gw_state; + +struct ub_gw; + +typedef void ub_gw_connect_function(struct ub_gw *gw); + +typedef void +ub_gw_handle_tra_function(struct ub_gw *gw, +struct ub_gw_tra *tra); + +typedef void +ub_gw_disconnect_function(struct ub_gw *gw, +struct ub_callback *callback); + +struct ub_gw { + struct ub_channel *channel; + ub_gw_connect_function *connect; + ub_gw_handle_tra_function *handle_tra; + ub_gw_disconnect_function *disconnect; + struct list_head target_rsrc_list; + struct ub_gw_rsrc target_rsrcs[USBIF_QUOTA]; + spinlock_t lock; + ub_gw_state state; + struct list_head channel_message_list; + struct ub_work kick_channel_messages_1_work; + struct ub_work connect_client_1_work; + struct ub_work disconnect_client_1_work; + struct ub_callback disconnect_client_2_callback; + int kick_channel_messages_out:1; + u32 target_rsrcs_out; + struct ub_callback *channel_disconnect_callback; +}; + +int +ub_gw_init(struct ub_gw *gw, struct ub_channel *channel, +ub_gw_connect_function *connect, +ub_gw_handle_tra_function *handle_tra, +ub_gw_disconnect_function *disconnect); + +extern void ub_gw_exit(struct ub_gw *gw); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw_rsrc.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_gw_rsrc.c Tue Oct 3 16:29:44 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 "ub_gw.h" +#include "ub_trace.h" + +void +ub_gw_submit_channel_message(struct ub_gw *gw, +struct ub_channel_obmsg *message); + +void +ub_gw_submit_tra_to_client(struct ub_gw *gw, +struct ub_gw_tra *tra); + +void +ub_gw_rsrc_start_tra(struct ub_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; + ub_gw_submit_tra_to_client(rsrc->gw, &rsrc->tra); +} + +static void +ub_gw_rsrc_tra_callback(struct ub_callback *callback) +{ + struct ub_gw_rsrc *rsrc = container_of( + ub_gw_tra_callback_to(callback), + struct ub_gw_rsrc, tra); + rsrc->channel_message.response.gw_status.error = + ub_callback_query_error(callback); + rsrc->channel_message.response.usbif_status = rsrc->tra.status; + ub_gw_submit_channel_message(rsrc->gw, &rsrc->channel_message); +} + +static void +ub_gw_rsrc_channel_message_callback(struct ub_callback *callback) +{ + struct ub_gw_rsrc *rsrc = container_of( + ub_channel_obmsg_callback_to(callback), + struct ub_gw_rsrc, + channel_message); + ub_callback_success(&rsrc->callback); +} + +void +ub_gw_rsrc_init(struct ub_gw_rsrc *rsrc, struct ub_gw *gw, +ub_callback_function callback) +{ + trace(); + ub_callback_init(&rsrc->callback, callback); + rsrc->gw = gw; + ub_gw_tra_init(&rsrc->tra, ub_gw_rsrc_tra_callback); + ub_channel_obmsg_init(&rsrc->channel_message, + ub_gw_rsrc_channel_message_callback); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_rbr_mapper.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_rbr_mapper.c Tue Oct 3 16:29:44 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 "ub_rbr_mapper.h" +#include "ub_trace.h" + +static UB_CALLBACK_SERIALISER(callback_serialiser); + +struct ub_rbr_mapper { + struct ub_buffer_rsrc_list dedicated_rsrcs; + struct ub_buffer_rsrc_provider *provider; + spinlock_t lock; + struct ub_buffer_rsrc_list head_request_rqm; + struct list_head request_list; + int kicking_requests; + int head_request_rqm_calculated; +}; + +static int ub_rbr_mapper_calc_rbr_rqm(struct usbif_rbr *rbr, +struct ub_buffer_rsrc_list *list) +{ + int request_invalid = 0; + trace(); + *list = ub_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 +ub_rbr_mapper_calc_rqm(struct ub_rbr_mapper_request *request, +struct ub_buffer_rsrc_list *list) +{ + struct ub_rbr_mapper_element *element; + trace(); + *list = ub_buffer_rsrc_list_null(); + list_for_each_entry(element, &request->request_elements, link) { + struct ub_buffer_rsrc_list element_list; + if (ub_rbr_mapper_calc_rbr_rqm(&element->rbr, + &element_list) != 0) + return 1; + ub_buffer_rsrc_list_plus_equals(list, &element_list); + } + return 0; +} + +static void +ub_rbr_mapper_unmap_rbr(struct ub_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) + ub_buffer_rsrc_provider_free_page_range( + context->provider, context->mmap_vaddress); +} + +static int +ub_rbr_mapper_map_rbr(struct usbif_rbr *rbr, domid_t domain, +struct ub_buffer_rsrc_provider *provider, void **mapping, +int access_flags, struct ub_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 & + UB_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 = + ub_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) { + ub_rbr_mapper_unmap_rbr(context); + return -EINVAL; + } + *mapping = (void *)(context->mmap_vaddress + (rbr->byte_offset % + PAGE_SIZE)); + return 0; +} + +static void +ub_rbr_mapper_service_request( +struct ub_rbr_mapper *pool, +struct ub_rbr_mapper_request *request) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + int error = 0; + struct ub_rbr_mapper_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + if (!error) { + error = ub_rbr_mapper_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; + ub_callback_serialiser_complete_callback(&callback_serialiser, + ub_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) + ub_rbr_mapper_unmap_rbr(&element->context); + } + ub_callback_serialiser_complete_callback(&callback_serialiser, + ub_rbr_mapper_request_to_map_callback(request), + usbif_error_map_local_to(error)); +} + +static void ub_rbr_mapper_kick_requests( +struct ub_rbr_mapper *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 ub_rbr_mapper_request *request = + ub_rbr_mapper_request_link_to( + pool->request_list.next); + struct ub_buffer_rsrc_list free_rsrcs; + if (pool->head_request_rqm_calculated) + goto skip_calc; + if ((ub_rbr_mapper_calc_rqm(request, + &pool->head_request_rqm) != 0) || + !ub_buffer_rsrc_list_subset_of( + &pool->head_request_rqm, + &pool->dedicated_rsrcs)) { + list_del_init(ub_rbr_mapper_request_to_link( + request)); + ub_callback_serialiser_complete_callback( + &callback_serialiser, + ub_rbr_mapper_request_to_map_callback( + request), + USBIF_ERROR_TOO_BIG); + continue; + } + pool->head_request_rqm_calculated = 1; + skip_calc: + free_rsrcs = ub_buffer_rsrc_provider_query_free_rsrcs( + pool->provider); + if (!ub_buffer_rsrc_list_subset_of( + &pool->head_request_rqm, &free_rsrcs)) + break; + list_del_init(ub_rbr_mapper_request_to_link(request)); + pool->head_request_rqm_calculated = 0; + pool->kicking_requests = 1; + spin_unlock_irqrestore(&pool->lock, flags); + ub_rbr_mapper_service_request(pool, request); + spin_lock_irqsave(&pool->lock, flags); + pool->kicking_requests = 0; + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void +ub_rbr_mapper_reserve_and_map_rbrs( +struct ub_rbr_mapper *pool, +struct ub_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(ub_rbr_mapper_request_to_link(request), + &pool->request_list); + spin_unlock_irqrestore(&pool->lock, flags); + ub_rbr_mapper_kick_requests(pool); +} + +void +ub_rbr_mapper_abort_reserve_and_map_rbrs( +struct ub_rbr_mapper_request *request, usbif_error error) +{ + struct ub_rbr_mapper *pool = request->pool; + struct ub_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(ub_rbr_mapper_request_to_link( + request)); + pool->head_request_rqm_calculated = 0; + ub_callback_serialiser_complete_callback( + &callback_serialiser, + ub_rbr_mapper_request_to_map_callback( + request), + error); + break; + } + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void ub_rbr_mapper_unmap_and_unreserve_rbrs( +struct ub_rbr_mapper_request *request) +{ + struct ub_rbr_mapper_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + ub_rbr_mapper_unmap_rbr(&element->context); + } + ub_rbr_mapper_kick_requests(request->pool); + ub_callback_serialiser_complete_callback(&callback_serialiser, + ub_rbr_mapper_request_to_unmap_callback(request), + USBIF_ERROR_SUCCESS); +} + +static int +ub_rbr_mapper_init_or_exit(struct ub_rbr_mapper *pool, +int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit; + pool->provider = ub_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: + ub_free_buffer_rsrc_provider(pool->provider); + exit_no_provider: + return return_value; +} + +struct ub_rbr_mapper * ub_rbr_mapper_allocate(void) +{ + struct ub_rbr_mapper *pool; + trace(); + pool = (struct ub_rbr_mapper *)kmalloc(sizeof( + struct ub_rbr_mapper),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 (ub_rbr_mapper_init_or_exit(pool, 0) != 0) { + kfree(pool); + pool = NULL; + } + } + return pool; +} + +void ub_rbr_mapper_free(struct ub_rbr_mapper *pool) +{ + trace(); + (void)ub_rbr_mapper_init_or_exit(pool, 1); + kfree(pool); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_rbr_mapper.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_rbr_mapper.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,145 @@ +/*****************************************************************************/ +/* 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 UB_RBR_MAPPER_H +#define UB_RBR_MAPPER_H + +#include "ub_callback.h" +#include "ub_buffer_rsrc_provider.h" + +struct ub_rbr_mapper; + +struct ub_rbr_mapper *ub_rbr_mapper_allocate(void); + +void ub_rbr_mapper_free(struct ub_rbr_mapper *pool); + +#define UB_ACCESS_FLAGS_READ 1 +#define UB_ACCESS_FLAGS_WRITE 2 + +struct ub_rbr_context { + struct ub_buffer_rsrc_provider *provider; + unsigned long page_count; + unsigned long mmap_vaddress; + grant_handle_t handle[USBIF_GRANT_TABLE_REFERENCE_COUNT]; +}; + +struct ub_rbr_mapper_element { + struct list_head link; + struct usbif_rbr rbr; + int access_flags; + int mapped; + void *mapping; + struct ub_rbr_context context; +}; + +static inline void +ub_rbr_mapper_element_init(struct ub_rbr_mapper_element *element) +{ + memset(element, 0, sizeof(*element)); + INIT_LIST_HEAD(&element->link); +} + +static inline void +ub_rbr_mapper_element_set_rbr(struct ub_rbr_mapper_element *element, +struct usbif_rbr rbr, int access_flags) +{ + element->rbr = rbr; + element->access_flags = access_flags; +} + +static inline void +ub_rbr_mapper_element_ensure_removed(struct ub_rbr_mapper_element *element) +{ + list_del_init(&element->link); +} + +struct ub_rbr_mapper_request { + struct ub_callback map_callback; + struct ub_callback unmap_callback; + struct ub_rbr_mapper *pool; + domid_t domain; + struct list_head request_elements; +}; + +static inline struct ub_callback * +ub_rbr_mapper_request_to_map_callback(struct ub_rbr_mapper_request *request) +{ + return &request->map_callback; +} + +static inline struct ub_rbr_mapper_request * +ub_rbr_mapper_request_map_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_rbr_mapper_request, + map_callback); +} + +static inline struct ub_callback * +ub_rbr_mapper_request_to_unmap_callback(struct ub_rbr_mapper_request *request) +{ + return &request->unmap_callback; +} + +static inline struct ub_rbr_mapper_request * +ub_rbr_mapper_request_unmap_callback_to(struct ub_callback *callback) +{ + return container_of(callback, struct ub_rbr_mapper_request, + unmap_callback); +} + +static inline struct list_head * +ub_rbr_mapper_request_to_link(struct ub_rbr_mapper_request *request) +{ + return ub_callback_to_link( + ub_rbr_mapper_request_to_map_callback(request)); +} + +static inline struct ub_rbr_mapper_request * +ub_rbr_mapper_request_link_to(struct list_head *link) +{ + return ub_rbr_mapper_request_map_callback_to( + ub_callback_link_to(link)); +} + +static inline void +ub_rbr_mapper_request_init(struct ub_rbr_mapper_request *request, +ub_callback_function *map_callback, ub_callback_function *unmap_callback) +{ + ub_callback_init(&request->map_callback, map_callback); + ub_callback_init(&request->unmap_callback, unmap_callback); + INIT_LIST_HEAD(&request->request_elements); +} + +static inline void +ub_rbr_mapper_request_add_element(struct ub_rbr_mapper_request *request, +struct ub_rbr_mapper_element *element) +{ + list_add(&element->link, &request->request_elements); +} + +void ub_rbr_mapper_reserve_and_map_rbrs(struct ub_rbr_mapper *pool, +struct ub_rbr_mapper_request *request, domid_t domain); + +void ub_rbr_mapper_abort_reserve_and_map_rbrs( +struct ub_rbr_mapper_request *request, usbif_error error); + +void +ub_rbr_mapper_unmap_and_unreserve_rbrs(struct ub_rbr_mapper_request *request); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_ring_channel.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_ring_channel.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,755 @@ +/*****************************************************************************/ +/* 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 "ub_ring_channel.h" +#include "ub_trace.h" + +static inline void +ub_ring_channel_rsrc_init(struct ub_ring_channel_rsrc *rsrc, +struct ub_ring_channel *channel, ub_callback_function *callback) +{ + trace(); + ub_channel_ibmsg_init(&rsrc->message, callback); + rsrc->channel = channel; +} + +static inline struct list_head * +ub_ring_channel_rsrc_to_link(struct ub_ring_channel_rsrc *rsrc) +{ + return ub_channel_ibmsg_to_link(&rsrc->message); +} + +static inline struct ub_ring_channel_rsrc * +ub_ring_channel_rsrc_callback_to(struct ub_callback *callback) +{ + return container_of(ub_channel_ibmsg_callback_to(callback), + struct ub_ring_channel_rsrc, message); +} + +typedef enum { + ub_ring_channel_stimulus_cn,/* connect request */ + ub_ring_channel_stimulus_mq,/* message queued */ + ub_ring_channel_stimulus_dn,/* disconnect request */ + ub_ring_channel_stimulus_ri,/* ring interrupt */ + ub_ring_channel_stimulus_cs,/* connect successful */ + ub_ring_channel_stimulus_cf,/* connect failed */ + ub_ring_channel_stimulus_cc,/* connect client completed */ + ub_ring_channel_stimulus_dc,/* disconnect client completed */ + ub_ring_channel_stimulus_ds,/* disconnect successful */ + ub_ring_channel_stimulus_ks,/* kick send ring completed */ + ub_ring_channel_stimulus_kr,/* kick recv ring completed */ + ub_ring_channel_stimulus_rc,/* rsrc completed */ + ub_ring_channel_stimulus_rf /* rsrc final completion */ +} ub_ring_channel_stimulus; + +static void +ub_ring_channel_handle_stimulus(struct ub_ring_channel *channel, +ub_ring_channel_stimulus stimulus); + +void +ub_ring_channel_connect(struct ub_ring_channel *channel, +struct ub_ring_channel_connect_request *request) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = &request->callback; + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_submit_message(struct ub_channel *base_channel, +struct ub_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct ub_ring_channel *channel = ub_ring_channel_channel_to( + base_channel); + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(ub_channel_obmsg_to_link(message), + &channel->message_list); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_mq); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void +ub_ring_channel_disconnect(struct ub_ring_channel *channel, +struct ub_callback *callback) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = callback; + ub_ring_channel_handle_stimulus(channel, ub_ring_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static irqreturn_t +ub_ring_channel_interrupt(int irq, void *context, struct pt_regs *ptregs) +{ + struct ub_ring_channel *channel = context; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + ub_ring_channel_handle_stimulus(channel, ub_ring_channel_stimulus_ri); + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +static void +ub_ring_channel_invalid_stimulus(struct ub_ring_channel *channel, +ub_ring_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void +ub_ring_channel_do_connect(struct ub_ring_channel *channel) +{ + trace(); + (void)ub_work_schedule(&channel->do_connect_1_work); +} + +static void ub_ring_channel_do_connect_1(void *data) +{ + struct ub_ring_channel *channel = data; + struct ub_ring_channel_connect_request *request = + ub_ring_channel_connect_request_callback_to( + channel->current_callback); + struct gnttab_map_grant_ref map_op; + struct gnttab_unmap_grant_ref unmap_op; + struct evtchn_bind_interdomain bind_interdomain; + struct evtchn_close close; + 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; + bind_interdomain.remote_dom = request->domain_id; + bind_interdomain.remote_port = request->event_channel; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, + &bind_interdomain) != 0) { + trace_info("failed to bind to remote event channel"); + goto exit_no_bind; + } + channel->event_channel = 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, + ub_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); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_cs); + spin_unlock_irqrestore(&channel->lock, flags); + return; + exit_no_irq: + close.port = channel->event_channel; + BUG_ON(HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 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); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_cf); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_connect_client(struct ub_ring_channel *channel) +{ + trace(); + (void)ub_work_schedule(&channel->connect_client_1_work); +} + +static void ub_ring_channel_connect_client_1(void *data) +{ + struct ub_ring_channel *channel = data; + unsigned long flags; + trace(); + ub_channel_connect(&channel->channel); + spin_lock_irqsave(&channel->lock, flags); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_cc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_disconnect_client(struct ub_ring_channel *channel) +{ + trace(); + (void)ub_work_schedule(&channel->disconnect_client_1_work); +} + +static void ub_ring_channel_disconnect_client_1(void *data) +{ + struct ub_ring_channel *channel = data; + trace(); + ub_channel_disconnect(&channel->channel, + &channel->disconnect_client_2_callback); +} + +static void +ub_ring_channel_disconnect_client_2(struct ub_callback *callback) +{ + struct ub_ring_channel *channel = container_of(callback, + struct ub_ring_channel, disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_dc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_do_disconnect(struct ub_ring_channel *channel) +{ + trace(); + (void)ub_work_schedule(&channel->do_disconnect_1_work); +} + +static void ub_ring_channel_do_disconnect_1(void *data) +{ + struct ub_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); + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_ds); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_kick_recv_ring(struct ub_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)ub_work_schedule(&channel->kick_recv_ring_1_work); + } +} + +static void ub_ring_channel_kick_recv_ring_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct ub_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 ub_ring_channel_rsrc *rsrc; + struct usbif_request *request; + rsrc = list_entry(channel->rsrc_list.next, + struct ub_ring_channel_rsrc, + message.callback.work.link); + list_del_init(ub_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); + ub_channel_handle_message(&channel->channel, + &rsrc->message); + progress = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (!progress) + { + channel->recv_ring_kick_out = 0; + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_kr); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_kick_recv_ring_2(struct ub_callback *callback) +{ + struct ub_ring_channel_rsrc *rsrc = + ub_ring_channel_rsrc_callback_to(callback); + struct ub_ring_channel *channel = rsrc->channel; + unsigned long flags; + if ((ub_callback_query_error(callback) != USBIF_ERROR_SUCCESS) && + (channel->protocol_error != NULL)) + channel->protocol_error(channel); + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(ub_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + if (--channel->rsrcs_out == 0) { + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_rf); + } else { + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_rc); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_kick_send_ring(struct ub_ring_channel *channel) +{ + if (!channel->send_ring_kick_out) { + channel->send_ring_kick_out = 1; + (void)ub_work_schedule(&channel->kick_send_ring_1_work); + } +} + +static void ub_ring_channel_kick_send_ring_1(void *data) +{ + struct ub_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 ub_channel_obmsg *message; + struct usbif_response *response; + message = list_entry(channel->message_list.next, + struct ub_channel_obmsg, + callback.work.link); + list_del_init(ub_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; + ub_callback_success( + ub_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; + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_ks); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +ub_ring_channel_complete_current_callback( +struct ub_ring_channel *channel) +{ + trace(); + ub_callback_success(channel->current_callback); +} + +static void +ub_ring_channel_fail_current_callback( +struct ub_ring_channel *channel) +{ + trace(); + ub_callback_complete(channel->current_callback, + USBIF_ERROR_FAILURE); +} + +static void +ub_ring_channel_fail_out_messages(struct ub_ring_channel *channel) +{ + trace(); + while (!list_empty(&channel->message_list)) { + struct ub_channel_obmsg *message = list_entry( + channel->message_list.next, + struct ub_channel_obmsg, + callback.work.link); + list_del_init(ub_channel_obmsg_to_link(message)); + /* Message completion doesn't guarantee message delivery so */ + /* there is no need for an error code here. */ + ub_callback_success(ub_channel_obmsg_to_callback( + message)); + } +} + +static void +ub_ring_channel_test_rsrcs(struct ub_ring_channel *channel) +{ + trace(); + if (channel->rsrcs_out == 0) { + ub_ring_channel_handle_stimulus(channel, + ub_ring_channel_stimulus_rf); + } +} + +static int +ub_ring_channel_init_or_exit(struct ub_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 = ub_ring_channel_state_i; + INIT_LIST_HEAD(&channel->message_list); + ub_work_init(&channel->do_connect_1_work, + ub_ring_channel_do_connect_1, channel); + ub_work_init(&channel->connect_client_1_work, + ub_ring_channel_connect_client_1, channel); + ub_work_init(&channel->disconnect_client_1_work, + ub_ring_channel_disconnect_client_1, channel); + ub_callback_init(&channel->disconnect_client_2_callback, + ub_ring_channel_disconnect_client_2); + ub_work_init(&channel->do_disconnect_1_work, + ub_ring_channel_do_disconnect_1, channel); + ub_work_init(&channel->kick_recv_ring_1_work, + ub_ring_channel_kick_recv_ring_1, channel); + ub_work_init(&channel->kick_send_ring_1_work, + ub_ring_channel_kick_send_ring_1, channel); + INIT_LIST_HEAD(&channel->rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct ub_ring_channel_rsrc *rsrc = &channel->rsrcs[i]; + ub_ring_channel_rsrc_init(rsrc, channel, + ub_ring_channel_kick_recv_ring_2); + list_add_tail(ub_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 +ub_ring_channel_init(struct ub_ring_channel *channel, +void (*protocol_error) (struct ub_ring_channel *channel)) +{ + trace(); + ub_channel_init(&channel->channel, + ub_ring_channel_submit_message); + channel->protocol_error = protocol_error; + return ub_ring_channel_init_or_exit(channel, 0); +} + +void ub_ring_channel_exit(struct ub_ring_channel *channel) +{ + trace(); + (void)ub_ring_channel_init_or_exit(channel, 1); +} + +static void ub_ring_channel_handle_stimulus( +struct ub_ring_channel *channel, ub_ring_channel_stimulus stimulus) +{ + switch (channel->state) { + case ub_ring_channel_state_i: + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + /* Ring disconnected. */ + switch (stimulus) { + case ub_ring_channel_stimulus_cn: + channel->state = ub_ring_channel_state_i_cn; + ub_ring_channel_do_connect(channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_cs: + channel->state = ub_ring_channel_state_i_cn_cs; + ub_ring_channel_connect_client(channel); + break; + case ub_ring_channel_stimulus_cf: + channel->state = ub_ring_channel_state_i; + ub_ring_channel_fail_current_callback(channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_mq: + case ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_cc: + channel->state = + ub_ring_channel_state_i_cn_cs_cc; + ub_ring_channel_complete_current_callback( + channel); + ub_ring_channel_kick_send_ring(channel); + ub_ring_channel_kick_recv_ring(channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_mq: + ub_ring_channel_kick_send_ring(channel); + break; + case ub_ring_channel_stimulus_dn: + channel->state = + ub_ring_channel_state_i_cn_cs_cc_dn; + ub_ring_channel_kick_send_ring(channel); + ub_ring_channel_kick_recv_ring(channel); + break; + case ub_ring_channel_stimulus_ri: + ub_ring_channel_kick_recv_ring(channel); + break; + case ub_ring_channel_stimulus_ks: + case ub_ring_channel_stimulus_kr: + break; + case ub_ring_channel_stimulus_rc: + case ub_ring_channel_stimulus_rf: + ub_ring_channel_kick_recv_ring(channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_mq: + case ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_ks: + case ub_ring_channel_stimulus_kr: + channel->state = + ub_ring_channel_state_i_cn_cs_cc_dn_ks; + break; + case ub_ring_channel_stimulus_rc: + case ub_ring_channel_stimulus_rf: + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_mq: + case ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_ks: + case ub_ring_channel_stimulus_kr: + channel->state = + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr; + ub_ring_channel_disconnect_client(channel); + ub_ring_channel_fail_out_messages(channel); + break; + case ub_ring_channel_stimulus_rc: + case ub_ring_channel_stimulus_rf: + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_mq: + ub_ring_channel_fail_out_messages(channel); + break; + case ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_dc: + channel->state = + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc; + ub_ring_channel_test_rsrcs(channel); + break; + case ub_ring_channel_stimulus_rc: + case ub_ring_channel_stimulus_rf: + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_ri: + case ub_ring_channel_stimulus_rc: + break; + case ub_ring_channel_stimulus_rf: + channel->state = + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf; + ub_ring_channel_do_disconnect(channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case ub_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 ub_ring_channel_stimulus_ri: + break; + case ub_ring_channel_stimulus_ds: + channel->state = ub_ring_channel_state_i; + ub_ring_channel_complete_current_callback( + channel); + break; + default: + ub_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + default: + ub_ring_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_ring_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_ring_channel.h Tue Oct 3 16:29:44 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 UB_RING_CHANNEL_H +#define UB_RING_CHANNEL_H + +#include +#include +#include +#include "ub_channel.h" + +struct ub_ring_channel; + +struct ub_ring_channel_rsrc { + struct ub_channel_ibmsg message; + struct ub_ring_channel *channel; +}; + +typedef enum { + ub_ring_channel_state_i, + ub_ring_channel_state_i_cn, + ub_ring_channel_state_i_cn_cs, + ub_ring_channel_state_i_cn_cs_cc, + ub_ring_channel_state_i_cn_cs_cc_dn, + ub_ring_channel_state_i_cn_cs_cc_dn_ks, + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr, + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc, + ub_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf +} ub_ring_channel_state; + +struct ub_ring_channel { + struct ub_channel channel; + void (*protocol_error) (struct ub_ring_channel *channel); + struct vm_struct *ring_area; + spinlock_t lock; + ub_ring_channel_state state; + struct list_head message_list; + struct ub_work do_connect_1_work; + struct ub_work connect_client_1_work; + struct ub_work disconnect_client_1_work; + struct ub_callback disconnect_client_2_callback; + struct ub_work do_disconnect_1_work; + struct ub_work kick_recv_ring_1_work; + struct ub_work kick_send_ring_1_work; + struct list_head rsrc_list; + struct ub_ring_channel_rsrc rsrcs[USBIF_QUOTA]; + int recv_ring_kick_out; + int send_ring_kick_out; + int rsrcs_out; + struct ub_callback *current_callback; + grant_handle_t ring_handle; + unsigned int event_channel; + int irq; + struct usbif_back_ring back_ring; +}; + +static inline struct ub_channel * +ub_ring_channel_to_channel(struct ub_ring_channel *channel) +{ + return &channel->channel; +} + +static inline struct ub_ring_channel * +ub_ring_channel_channel_to(struct ub_channel *channel) +{ + return container_of(channel, struct ub_ring_channel, channel); +} + +int ub_ring_channel_init(struct ub_ring_channel *channel, +void (*protocol_error) (struct ub_ring_channel *channel)); + +struct ub_ring_channel_connect_request { + struct ub_callback callback; + domid_t domain_id; + grant_ref_t ring_ref; + evtchn_port_t event_channel; +}; + +static inline struct ub_ring_channel_connect_request * +ub_ring_channel_connect_request_callback_to( +struct ub_callback *callback) +{ + return container_of(callback, + struct ub_ring_channel_connect_request,callback); +} + +void ub_ring_channel_connect(struct ub_ring_channel *channel, +struct ub_ring_channel_connect_request *request); + +void ub_ring_channel_disconnect(struct ub_ring_channel *channel, +struct ub_callback *callback); + +void ub_ring_channel_exit(struct ub_ring_channel *channel); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_rsrc.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_rsrc.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,529 @@ +/*****************************************************************************/ +/* 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 "ub_ep.h" +#include "ub_rbr_mapper.h" +#include "ub_rsrc.h" +#include "ub_trace.h" + +static struct ub_rbr_mapper *rbr_mapper; + +typedef enum { + ub_rsrc_stimulus_st, /* Start IO */ + ub_rsrc_stimilus_fl, /* Flush IO */ + ub_rsrc_stimulus_ms, /* Map success */ + ub_rsrc_stimulus_mf, /* Map failure */ + ub_rsrc_stimulus_us, /* URB submitted */ + ub_rsrc_stimulus_un, /* URB not submitted */ + ub_rsrc_stimulus_uc /* URB completed */ +} ub_rsrc_stimulus; + +static void +ub_rsrc_handle_stimulus(struct ub_rsrc *rsrc, ub_rsrc_stimulus stimulus); + +void ub_rsrc_start_io(struct ub_rsrc *rsrc, struct ub_gw_tra *tra) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->tra_error = USBIF_ERROR_SUCCESS; + rsrc->tra = tra; + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimulus_st); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +void ub_rsrc_flush_io(struct ub_rsrc *rsrc) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + if (rsrc->tra != NULL) + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimilus_fl); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void +ub_rsrc_invalid_stimulus(struct ub_rsrc *rsrc, ub_rsrc_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: port rsrc %p in state %d received" + " invalid stimulus %d", rsrc, rsrc->state, stimulus); +} + +static void ub_rsrc_map_buffer(struct ub_rsrc *rsrc) +{ + struct usbif_io_tra_parameters_header *header = + &rsrc->tra->parameters.io.header; + trace(); + ub_rbr_mapper_element_ensure_removed(&rsrc->request_element[0]); + ub_rbr_mapper_element_ensure_removed(&rsrc->request_element[1]); + ub_rbr_mapper_request_add_element(&rsrc->rbr_mapper_request, + &rsrc->request_element[0]); + ub_rbr_mapper_element_set_rbr(&rsrc->request_element[0], + header->rbr, + (header->direction == USBIF_IO_TRA_DIRECTION_OUT) ? + UB_ACCESS_FLAGS_READ : + UB_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; + ub_rbr_mapper_request_add_element( + &rsrc->rbr_mapper_request, + &rsrc->request_element[1]); + ub_rbr_mapper_element_set_rbr( + &rsrc->request_element[1], + parameters->schedule_rbr, + UB_ACCESS_FLAGS_READ | + UB_ACCESS_FLAGS_WRITE); + } + ub_rbr_mapper_reserve_and_map_rbrs(rbr_mapper, + &rsrc->rbr_mapper_request, + ub_ep_query_remote_domain(rsrc->ep)); +} + +static void ub_rsrc_abort_map(struct ub_rsrc *rsrc) +{ + trace(); + ub_rbr_mapper_abort_reserve_and_map_rbrs( + &rsrc->rbr_mapper_request, + USBIF_ERROR_UNLINKED); +} + +static void ub_rsrc_map_callback(struct ub_callback * callback) +{ + struct ub_rbr_mapper_request *request = + ub_rbr_mapper_request_map_callback_to(callback); + struct ub_rsrc *rsrc = container_of(request, + struct ub_rsrc, + rbr_mapper_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->tra_error = ub_callback_query_error(callback); + if (rsrc->tra_error == USBIF_ERROR_SUCCESS) { + rsrc->rbrs_mapped = 1; + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimulus_ms); + } else { + trace_info("failed to map FE buffer"); + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimulus_mf); + } + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void ub_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 ub_rsrc_submit_urb(struct ub_rsrc *rsrc) +{ + 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 + }; + union usbif_io_tra_parameters *io_parameters = + &rsrc->tra->parameters.io; + struct urb *urb = rsrc->urb; + struct usb_device * usbdev = ub_ep_query_usbdev(rsrc->ep); + unsigned int pipe; + 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), + ub_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), + ub_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), + ub_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 = ub_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_ATOMIC)); + if (rsrc->tra_error != USBIF_ERROR_SUCCESS) { + printk(KERN_WARNING "URB %p submission failed.\n", urb); + goto urb_error; + } + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimulus_us); + return; + urb_error: + schedule_error: + handled_special_case: + protocol_error: + rsrc->completing_tra = rsrc->tra; + rsrc->tra = NULL; + ub_rsrc_handle_stimulus(rsrc, ub_rsrc_stimulus_un); +} + +static void ub_rsrc_end_io(struct urb *urb, struct pt_regs *regs) +{ + struct ub_rsrc *rsrc = urb->context; + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->completing_tra = rsrc->tra; + rsrc->tra = NULL; + spin_unlock_irqrestore(&rsrc->lock, flags); + 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); + /* Report the error here before completing the rsrc so that */ + /* the outstanding rsrc keeps the ep alive for the call. */ + ub_ep_stall(rsrc->ep); + } + rsrc->completing_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); + ub_rsrc_handle_stimulus(rsrc,ub_rsrc_stimulus_uc); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void ub_rsrc_unlink_urb(struct ub_rsrc *rsrc) +{ + trace(); + usb_unlink_urb(rsrc->urb); +} + +static void ub_rsrc_set_unlinked_error(struct ub_rsrc *rsrc) +{ + trace(); + rsrc->tra_error = USBIF_ERROR_UNLINKED; +} + +static void ub_rsrc_complete(struct ub_rsrc *rsrc) +{ + trace(); + if (rsrc->rbrs_mapped) { + rsrc->rbrs_mapped = 0; + ub_rbr_mapper_unmap_and_unreserve_rbrs( + &rsrc->rbr_mapper_request); + } else { + ub_callback_success( + ub_rbr_mapper_request_to_unmap_callback( + &rsrc->rbr_mapper_request)); + } +} + +static void ub_rsrc_unmap_callback(struct ub_callback * callback) +{ + struct ub_rbr_mapper_request *request = + ub_rbr_mapper_request_unmap_callback_to(callback); + struct ub_rsrc *rsrc = container_of(request, struct ub_rsrc, + rbr_mapper_request); + trace(); + ub_gw_tra_complete(rsrc->completing_tra, rsrc->tra_error); + ub_ep_rsrc_completed_io(rsrc->ep, rsrc); +} + +static void +ub_rsrc_handle_stimulus(struct ub_rsrc *rsrc, ub_rsrc_stimulus stimulus) +{ + trace(); + switch (rsrc->state) { + case ub_rsrc_state_i: + /* Idle */ + switch (stimulus) { + case ub_rsrc_stimulus_st: + rsrc->state = ub_rsrc_state_i_st; + ub_rsrc_map_buffer(rsrc); + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case ub_rsrc_state_i_st: + /* Handling request */ + /* map_buffer outstanding */ + switch (stimulus) { + case ub_rsrc_stimilus_fl: + rsrc->state = ub_rsrc_state_i_st_fl; + ub_rsrc_abort_map(rsrc); + break; + case ub_rsrc_stimulus_ms: + rsrc->state = ub_rsrc_state_i_st_ms; + ub_rsrc_submit_urb(rsrc); + break; + case ub_rsrc_stimulus_mf: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_complete(rsrc); + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case ub_rsrc_state_i_st_fl: + /* Handling request */ + /* map_buffer outstanding */ + /* unlinking */ + switch (stimulus) { + case ub_rsrc_stimilus_fl: + break; + case ub_rsrc_stimulus_ms: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_set_unlinked_error(rsrc); + ub_rsrc_complete(rsrc); + break; + case ub_rsrc_stimulus_mf: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_complete(rsrc); + break; + default: + ub_rsrc_invalid_stimulus + (rsrc, stimulus); + break; + } + break; + case ub_rsrc_state_i_st_ms: + /* Handling request */ + /* submit_urb outstanding */ + switch (stimulus) { + case ub_rsrc_stimulus_us: + rsrc->state = ub_rsrc_state_i_st_ms_us; + break; + case ub_rsrc_stimulus_un: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_complete(rsrc); + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case ub_rsrc_state_i_st_ms_us: + /* Handling request */ + /* URB submitted */ + switch (stimulus) { + case ub_rsrc_stimilus_fl: + rsrc->state = ub_rsrc_state_i_st_ms_us_fl; + ub_rsrc_unlink_urb(rsrc); + break; + case ub_rsrc_stimulus_uc: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_complete(rsrc); + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case ub_rsrc_state_i_st_ms_us_fl: + /* Handling request */ + /* URB outstanding and unlinked */ + switch (stimulus) { + case ub_rsrc_stimilus_fl: + break; + case ub_rsrc_stimulus_uc: + rsrc->state = ub_rsrc_state_i; + ub_rsrc_complete(rsrc); + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + default: + ub_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } +} + +int ub_rsrc_init(struct ub_rsrc *rsrc, struct ub_ep *ep) +{ + trace(); + rsrc->ep = ep; + INIT_LIST_HEAD(&rsrc->link); + spin_lock_init(&rsrc->lock); + rsrc->state = ub_rsrc_state_i; + rsrc->tra = NULL; + ub_rbr_mapper_element_init(&rsrc->request_element[0]); + ub_rbr_mapper_element_init(&rsrc->request_element[1]); + ub_rbr_mapper_request_init( + &rsrc->rbr_mapper_request, + ub_rsrc_map_callback, + ub_rsrc_unmap_callback); + rsrc->rbrs_mapped = 0; + rsrc->urb = usb_alloc_urb(USBIF_MAX_SCHEDULE_PACKET_COUNT, GFP_KERNEL); + return (rsrc->urb != NULL) ? 0 : -ENOMEM; +} + +void ub_rsrc_exit(struct ub_rsrc *rsrc) +{ + trace(); + usb_free_urb(rsrc->urb); +} + +int ub_rsrc_class_init(void) +{ + trace(); + rbr_mapper = ub_rbr_mapper_allocate(); + return (rbr_mapper != NULL) ? 0 : -ENOMEM; +} + +void ub_rsrc_class_exit(void) +{ + trace(); + ub_rbr_mapper_free(rbr_mapper); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_rsrc.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_rsrc.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* 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 UB_RSRC_H +#define UB_RSRC_H + +#include "ub_gw.h" +#include "ub_rbr_mapper.h" + +struct ub_ep; +struct ub_rsrc; + +void ub_rsrc_start_io(struct ub_rsrc *rsrc, struct ub_gw_tra *tra); +void ub_rsrc_flush_io(struct ub_rsrc *rsrc); +int ub_rsrc_init(struct ub_rsrc *rsrc, struct ub_ep *ep); +void ub_rsrc_exit(struct ub_rsrc *rsrc); +int ub_rsrc_class_init(void); +void ub_rsrc_class_exit(void); + +typedef enum { + ub_rsrc_state_i, + ub_rsrc_state_i_st, + ub_rsrc_state_i_st_fl, + ub_rsrc_state_i_st_ms, + ub_rsrc_state_i_st_ms_us, + ub_rsrc_state_i_st_ms_us_fl +} ub_rsrc_state; + +struct ub_rsrc { + struct ub_ep *ep; + struct list_head link; + spinlock_t lock; + ub_rsrc_state state; + usbif_error tra_error; + unsigned long tra_status_length; + struct ub_gw_tra *tra; + struct ub_gw_tra *completing_tra; + struct ub_rbr_mapper_request rbr_mapper_request; + struct ub_rbr_mapper_element request_element[2]; + int rbrs_mapped; + struct urb *urb; + u8 setup[8]; +}; + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_trace.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_trace.h Tue Oct 3 16:29:44 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 UB_TRACE_H +#define UB_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_BACKEND_TRACE + +#define trace_info(format, ...) \ +printk(KERN_INFO "ub %s: " format "\n", __FUNCTION__, ## __VA_ARGS__) +#define trace() trace_info("") +#else +#define trace_info(format, ...) +#define trace() +#endif + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_work.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_work.c Tue Oct 3 16:29:44 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 "ub_work.h" + +static void ub_work_function(void *ignored); + +DEFINE_SPINLOCK(ub_work_list_lock); +LIST_HEAD(ub_work_list); +DECLARE_WORK(ub_work_work, ub_work_function, NULL); +DECLARE_WAIT_QUEUE_HEAD(ub_work_waitqueue); +LIST_HEAD(ub_work_condition); + +void ub_work_wake_up(void) +{ + unsigned long flags; + spin_lock_irqsave(&ub_work_list_lock, flags); + while (!list_empty(&ub_work_condition)) + list_del_init(ub_work_condition.next); + spin_unlock_irqrestore(&ub_work_list_lock, flags); + wake_up(&ub_work_waitqueue); +} + +int ub_work_schedule(struct ub_work *work) +{ + int scheduled = 0; + unsigned long flags; + spin_lock_irqsave(&ub_work_list_lock, flags); + if (list_empty(&work->link)) { + list_add_tail(&work->link, &ub_work_list); + scheduled = 1; + } + spin_unlock_irqrestore(&ub_work_list_lock, flags); + if (scheduled) { + ub_work_wake_up(); + schedule_work(&ub_work_work); + } + return scheduled; +} + +static void ub_work_function(void *ignored) +{ + unsigned long flags; + spin_lock_irqsave(&ub_work_list_lock, flags); + while (!list_empty(&ub_work_list)) { + struct ub_work *work = list_entry(ub_work_list.next, + struct ub_work, + link); + list_del_init(&work->link); + spin_unlock_irqrestore(&ub_work_list_lock, flags); + ub_work_perform_synchronously(work); + spin_lock_irqsave(&ub_work_list_lock, flags); + } + spin_unlock_irqrestore(&ub_work_list_lock, flags); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_work.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_work.h Tue Oct 3 16:29:44 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 UB_WORK_H +#define UB_WORK_H + +#include +#include +#include +#include +#include + +struct ub_work { + struct list_head link; + void (*function)(void *context); + void *context; +}; + +#define UB_WORK_INIT( name, fn, ctx ) \ +{ .link = LIST_HEAD_INIT( name.link ), .function = fn, .context = ctx } + +#define UB_WORK( name, fn, ctx ) \ +struct ub_work name = UB_WORK_INIT( name, fn, ctx ) + +static inline void +ub_work_init(struct ub_work *work, void (*function) (void *context), +void *context) +{ + INIT_LIST_HEAD(&work->link); + work->function = function; + work->context = context; +} + +static inline struct list_head *ub_work_to_link(struct ub_work *work) +{ + return &work->link; +} + +static inline struct ub_work *ub_work_link_to(struct list_head *link) +{ + return container_of(link, struct ub_work, link); +} + +int ub_work_schedule(struct ub_work *work); + +static inline void +ub_work_perform_synchronously(struct ub_work *work) +{ + work->function(work->context); +} + +extern spinlock_t ub_work_list_lock; +extern struct list_head ub_work_list; +extern wait_queue_head_t ub_work_waitqueue; +extern struct list_head ub_work_condition; + +#define ub_work_until( condition ) \ +do { \ + unsigned long flags; \ + spin_lock_irqsave(&ub_work_list_lock, flags); \ + for (;;) { \ + struct list_head link; \ + \ + while (!list_empty(&ub_work_list) && !(condition)) { \ + struct ub_work *work; \ + work = list_entry(ub_work_list.next, \ + struct ub_work, link); \ + list_del_init(&work->link); \ + spin_unlock_irqrestore(&ub_work_list_lock, \ + flags); \ + ub_work_perform_synchronously(work); \ + spin_lock_irqsave(&ub_work_list_lock, flags); \ + } \ + if (condition) \ + break; \ + INIT_LIST_HEAD(&link); \ + list_add_tail(&link, &ub_work_condition); \ + spin_unlock_irqrestore(&ub_work_list_lock, flags); \ + wait_event(ub_work_waitqueue, list_empty(&link)); \ + spin_lock_irqsave(&ub_work_list_lock, flags); \ + } \ + spin_unlock_irqrestore(&ub_work_list_lock, flags); \ +} \ +while (0) + +void ub_work_wake_up(void); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_xb_channel.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_xb_channel.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,372 @@ +/*****************************************************************************/ +/* 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 "ub_xb_channel.h" +#include "ub_trace.h" + +typedef enum { + ub_xb_channel_stimulus_cn, + ub_xb_channel_stimulus_dn, + ub_xb_channel_stimulus_pe, /* Protocol error. */ + ub_xb_channel_stimulus_fi, /* Frontend initialised. */ + ub_xb_channel_stimulus_fc, /* Frontend closing. */ + ub_xb_channel_stimulus_rs, + ub_xb_channel_stimulus_rf +} ub_xb_channel_stimulus; + +static void ub_xb_channel_handle_stimulus(struct ub_xb_channel *channel, +ub_xb_channel_stimulus stimulus); + +void ub_xb_channel_connect(struct ub_xb_channel *channel, +struct xenbus_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->device = device; + ub_xb_channel_handle_stimulus(channel, ub_xb_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void ub_xb_channel_frontend_initialised(struct ub_xb_channel *channel, +struct xenbus_device *dev) +{ + unsigned long ring_ref; + unsigned int event_channel; + unsigned long flags; + int error; + trace_info("%s", dev->otherend); + error = xenbus_gather(XBT_NIL, 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; + ub_xb_channel_handle_stimulus(channel, + ub_xb_channel_stimulus_fi); + } else { + xenbus_dev_fatal(dev, error, + "reading %s/ring-ref and event-channel", + dev->otherend); + ub_xb_channel_handle_stimulus(channel, + ub_xb_channel_stimulus_pe); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +void ub_xb_channel_frontend_closing(struct ub_xb_channel *channel, +struct xenbus_device *dev) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + ub_xb_channel_handle_stimulus(channel, ub_xb_channel_stimulus_fc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void ub_xb_channel_frontend_changed(struct ub_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: + case XenbusStateConnected: + ub_xb_channel_frontend_initialised(channel, dev); + break; + case XenbusStateClosing: + ub_xb_channel_frontend_closing(channel, dev); + break; + case XenbusStateClosed: + xenbus_switch_state(dev, XenbusStateClosed); + device_unregister(&dev->dev); + break; + default: + break; + } +} + +static void ub_xb_channel_protocol_error(struct ub_ring_channel *ring_channel) +{ + struct ub_xb_channel *channel = + ub_xb_channel_ring_channel_to(ring_channel); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + ub_xb_channel_handle_stimulus(channel, ub_xb_channel_stimulus_pe); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void ub_xb_channel_disconnect(struct ub_xb_channel *channel) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->disconnected = 0; + ub_xb_channel_handle_stimulus(channel, ub_xb_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); + ub_work_until(channel->disconnected); +} + +static void ub_xb_channel_invalid_stimulus(struct ub_xb_channel *channel, +ub_xb_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "ub: xb channel %p in state %d" + " received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void ub_xb_channel_connect_ring(struct ub_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; + ub_ring_channel_connect(&channel->channel, &channel->ring_connect); +} + +static void ub_xb_channel_connect_ring_1(struct ub_callback *callback) +{ + struct ub_xb_channel *channel = container_of( + ub_ring_channel_connect_request_callback_to(callback), + struct ub_xb_channel, ring_connect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + if (ub_callback_query_error(callback) == USBIF_ERROR_SUCCESS) { + xenbus_switch_state(channel->device, XenbusStateConnected); + ub_xb_channel_handle_stimulus(channel, + ub_xb_channel_stimulus_rs); + } else { + ub_xb_channel_handle_stimulus(channel, + ub_xb_channel_stimulus_rf); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void ub_xb_channel_disconnect_ring(struct ub_xb_channel *channel) +{ + trace(); + ub_ring_channel_disconnect(&channel->channel, + &channel->ring_disconnect); +} + +static void ub_xb_channel_disconnect_ring_1(struct ub_callback *callback) +{ + struct ub_xb_channel *channel = container_of(callback, + struct ub_xb_channel, ring_disconnect); + unsigned long flags; + trace(); + xenbus_switch_state(channel->device, XenbusStateClosing); + spin_lock_irqsave(&channel->lock, flags); + ub_xb_channel_handle_stimulus(channel, ub_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void ub_xb_channel_complete_disconnect(struct ub_xb_channel *channel) +{ + trace(); + channel->disconnected = 1; + ub_work_wake_up(); +} + +static int ub_xb_channel_init_or_exit(struct ub_xb_channel *channel, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + if ((return_value = ub_ring_channel_init(&channel->channel, + ub_xb_channel_protocol_error)) != 0) { + goto exit_no_ring_channel; + } + spin_lock_init(&channel->lock); + channel->state = ub_xb_channel_state_i; + ub_callback_init(&channel->ring_connect.callback, + ub_xb_channel_connect_ring_1); + ub_callback_init(&channel->ring_disconnect, + ub_xb_channel_disconnect_ring_1); + return 0; + exit_path: + ub_ring_channel_exit(&channel->channel); + exit_no_ring_channel: + return return_value; +} + +int ub_xb_channel_init(struct ub_xb_channel *channel) +{ + trace(); + return ub_xb_channel_init_or_exit(channel, 0); +} + +void ub_xb_channel_exit(struct ub_xb_channel *channel) +{ + trace(); + (void)ub_xb_channel_init_or_exit(channel, 1); +} + +static void +ub_xb_channel_handle_stimulus(struct ub_xb_channel *channel, +ub_xb_channel_stimulus stimulus) { + trace_info("xb channel %p in state %d received stimulus %d", channel, + channel->state, stimulus); + switch (channel->state) { + case ub_xb_channel_state_i: + switch (stimulus) { + case ub_xb_channel_stimulus_cn: + channel->state = ub_xb_channel_state_i_cn; + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn: + switch (stimulus) { + case ub_xb_channel_stimulus_dn: + channel->state = ub_xb_channel_state_i; + ub_xb_channel_complete_disconnect(channel); + break; + case ub_xb_channel_stimulus_fi: + channel->state = ub_xb_channel_state_i_cn_fi; + ub_xb_channel_connect_ring(channel); + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi: + switch (stimulus) { + case ub_xb_channel_stimulus_dn: + channel->state = ub_xb_channel_state_i_cn_fi_dn; + break; + case ub_xb_channel_stimulus_fi: + break; + case ub_xb_channel_stimulus_rs: + channel->state = ub_xb_channel_state_i_cn_fi_rs; + break; + case ub_xb_channel_stimulus_rf: + channel->state = ub_xb_channel_state_i_cn; + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi_dn: + switch (stimulus) { + case ub_xb_channel_stimulus_rs: + channel->state = ub_xb_channel_state_i_cn_fi_dn_rs; + ub_xb_channel_disconnect_ring(channel); + break; + case ub_xb_channel_stimulus_rf: + channel->state = ub_xb_channel_state_i; + ub_xb_channel_complete_disconnect(channel); + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi_rs: + switch (stimulus) { + case ub_xb_channel_stimulus_dn: + channel->state = ub_xb_channel_state_i_cn_fi_dn_rs; + ub_xb_channel_disconnect_ring(channel); + break; + case ub_xb_channel_stimulus_fi: + break; + case ub_xb_channel_stimulus_fc: + channel->state = ub_xb_channel_state_i_cn_fi_rs_fc; + ub_xb_channel_disconnect_ring(channel); + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi_dn_rs: + switch (stimulus) { + case ub_xb_channel_stimulus_rs: + channel->state = ub_xb_channel_state_i; + ub_xb_channel_complete_disconnect(channel); + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi_rs_fc: + switch (stimulus) { + case ub_xb_channel_stimulus_dn: + channel->state = ub_xb_channel_state_i_cn_fi_dn_rs; + break; + case ub_xb_channel_stimulus_rs: + channel->state = ub_xb_channel_state_i_cn_fi_rs_fc_rs; + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case ub_xb_channel_state_i_cn_fi_rs_fc_rs: + switch (stimulus) { + case ub_xb_channel_stimulus_dn: + channel->state = ub_xb_channel_state_i; + ub_xb_channel_complete_disconnect(channel); + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + default: + ub_xb_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbback/ub_xb_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/ub_xb_channel.h Tue Oct 3 16:29:44 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 UB_XB_CHANNEL_H +#define UB_XB_CHANNEL_H + +#include +#include "ub_ring_channel.h" + +typedef enum { + ub_xb_channel_state_i, + ub_xb_channel_state_i_cn, + ub_xb_channel_state_i_cn_fi, + ub_xb_channel_state_i_cn_fi_dn, + ub_xb_channel_state_i_cn_fi_rs, + ub_xb_channel_state_i_cn_fi_dn_rs, + ub_xb_channel_state_i_cn_fi_rs_fc, + ub_xb_channel_state_i_cn_fi_rs_fc_rs +} ub_xb_channel_state; + +struct ub_xb_channel { + struct ub_ring_channel channel; + spinlock_t lock; + ub_xb_channel_state state; + struct xenbus_device *device; + domid_t domain_id; + grant_ref_t ring_ref; + evtchn_port_t event_channel; + struct ub_ring_channel_connect_request ring_connect; + struct ub_callback ring_disconnect; + int disconnected; +}; + +static inline struct ub_channel * +ub_xb_channel_to_channel(struct ub_xb_channel *channel) +{ + return ub_ring_channel_to_channel(&channel->channel); +} + +static inline struct ub_xb_channel * +ub_xb_channel_ring_channel_to(struct ub_ring_channel *ring_channel) +{ + return container_of(ring_channel, struct ub_xb_channel, channel); +} + +static inline struct ub_xb_channel * +ub_xb_channel_channel_to(struct ub_channel *channel) +{ + return ub_xb_channel_ring_channel_to( + ub_ring_channel_channel_to(channel)); +} + +void ub_xb_channel_connect(struct ub_xb_channel *channel, +struct xenbus_device *device); +void ub_xb_channel_frontend_changed(struct ub_xb_channel *channel, +struct xenbus_device *dev, XenbusState state); +void ub_xb_channel_disconnect(struct ub_xb_channel *channel); +int ub_xb_channel_init(struct ub_xb_channel *channel); +void ub_xb_channel_exit(struct ub_xb_channel *channel); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile Tue Oct 3 16:29:44 2006 @@ -0,0 +1,16 @@ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += uf.o + +uf-objs := \ +uf_buffer_rsrc_provider.o \ +uf_callback.o \ +uf_device.o \ +uf_driver.o \ +uf_ep.o \ +uf_gw.o \ +uf_gw_rsrc.o \ +uf_module.o \ +uf_rbr_provider_pool.o \ +uf_ring_channel.o \ +uf_work.o \ +uf_xb_channel.o \ +uf_rsrc.o diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_buffer_rsrc_provider.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_buffer_rsrc_provider.c Tue Oct 3 16:29:44 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 "uf_buffer_rsrc_provider.h" +#include "uf_trace.h" + +struct uf_buffer_rsrc_provider { + spinlock_t lock; + struct uf_buffer_rsrc_list rsrcs; + struct uf_buffer_rsrc_list free_rsrcs; + grant_ref_t grant_ref_pool; +}; + +static int +alloc_or_free_grant_refs(struct uf_buffer_rsrc_provider *provider, +int free) +{ + int return_value = 0; + if (provider->rsrcs.grant_references == 0) + return 0; + if (free) + goto exit_path; + return_value = gnttab_alloc_grant_references( + provider->rsrcs.grant_references, + &provider->grant_ref_pool); + if (return_value != 0) + goto exit_no_grant_references; + provider->free_rsrcs.grant_references = + provider->rsrcs.grant_references; + return 0; + + exit_path: + gnttab_free_grant_references(provider->grant_ref_pool); + + exit_no_grant_references: + return return_value; +} + +static int +uf_buffer_rsrc_provider_init_or_exit( +struct uf_buffer_rsrc_provider *provider, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + spin_lock_init(&provider->lock); + provider->free_rsrcs = uf_buffer_rsrc_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 uf_buffer_rsrc_provider * +uf_allocate_buffer_rsrc_provider( +struct uf_buffer_rsrc_list rsrcs) +{ + struct uf_buffer_rsrc_provider *provider; + trace(); + provider = kmalloc(sizeof(struct uf_buffer_rsrc_provider), + GFP_KERNEL); + if (provider != NULL) { + provider->rsrcs = rsrcs; + if (uf_buffer_rsrc_provider_init_or_exit(provider, 0) + != 0) { + kfree(provider); + provider = NULL; + } + } + return provider; +} + +void uf_free_buffer_rsrc_provider( +struct uf_buffer_rsrc_provider *provider) +{ + trace(); + (void)uf_buffer_rsrc_provider_init_or_exit(provider, 1); + kfree(provider); +} + +struct uf_buffer_rsrc_list +uf_buffer_rsrc_provider_query_free_rsrcs( +struct uf_buffer_rsrc_provider *provider) +{ + struct uf_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; +} + +grant_ref_t +uf_buffer_rsrc_provider_allocate_grant_reference( +struct uf_buffer_rsrc_provider *provider) +{ + grant_ref_t reference; + unsigned long flags; + trace(); + spin_lock_irqsave(&provider->lock, flags); + provider->free_rsrcs.grant_references--; + reference = gnttab_claim_grant_reference(&provider->grant_ref_pool); + spin_unlock_irqrestore(&provider->lock, flags); + return reference; +} + +void +uf_buffer_rsrc_provider_free_grant_reference( +struct uf_buffer_rsrc_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_rsrcs.grant_references++; + spin_unlock_irqrestore(&provider->lock, flags); +} + +void +uf_buffer_rsrc_list_trace(struct uf_buffer_rsrc_list list) +{ + trace(); + printk(KERN_INFO "uf %s: grant_references:%d\n", + __PRETTY_FUNCTION__, list.grant_references); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_buffer_rsrc_provider.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_buffer_rsrc_provider.h Tue Oct 3 16:29:44 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 UF_BUFFER_RSRC_PROVIDER_H +#define UF_BUFFER_RSRC_PROVIDER_H + +#include +#include +#include + +struct uf_buffer_rsrc_list { + u32 grant_references; +}; + +static inline struct uf_buffer_rsrc_list +uf_buffer_rsrc_list_null(void) +{ + struct uf_buffer_rsrc_list list; + memset(&list, 0, sizeof(list)); + return list; +} + +static inline int +uf_buffer_rsrc_list_subset_of( +struct uf_buffer_rsrc_list *a, +struct uf_buffer_rsrc_list *b) +{ + return a->grant_references <= b->grant_references; +} + +static inline void +uf_buffer_rsrc_list_plus_equals( +struct uf_buffer_rsrc_list *a, +struct uf_buffer_rsrc_list *b) +{ + a->grant_references += b->grant_references; +} + +struct uf_buffer_rsrc_provider * +uf_allocate_buffer_rsrc_provider( +struct uf_buffer_rsrc_list rsrc_allocation); + +void +uf_free_buffer_rsrc_provider( +struct uf_buffer_rsrc_provider *provider); + +struct uf_buffer_rsrc_list +uf_buffer_rsrc_provider_query_free_rsrcs( +struct uf_buffer_rsrc_provider *provider); + +grant_ref_t +uf_buffer_rsrc_provider_allocate_grant_reference( +struct uf_buffer_rsrc_provider *provider); + +void +uf_buffer_rsrc_provider_free_grant_reference( +struct uf_buffer_rsrc_provider *provider, grant_ref_t reference); + +void +uf_buffer_rsrc_list_trace(struct uf_buffer_rsrc_list list); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_callback.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_callback.c Tue Oct 3 16:29:44 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 "uf_callback.h" + +void uf_callback_serialiser_function(void *context) +{ + struct uf_callback_serialiser *serialiser = context; + unsigned long flags; + spin_lock_irqsave(&serialiser->lock, flags); + while (!list_empty(&serialiser->list) && !serialiser->running) { + struct uf_callback *callback = + uf_callback_link_to(serialiser->list.next); + list_del_init(uf_callback_to_link(callback)); + serialiser->running = 1; + spin_unlock_irqrestore(&serialiser->lock, flags); + uf_callback_complete_synchronously(callback); + spin_lock_irqsave(&serialiser->lock, flags); + serialiser->running = 0; + } + spin_unlock_irqrestore(&serialiser->lock, flags); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_callback.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_callback.h Tue Oct 3 16:29:44 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 UF_CALLBACK_H +#define UF_CALLBACK_H + +#include +#include +#include "uf_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 uf_callback { + struct uf_work work; + usbif_error error; +}; + +typedef void uf_callback_function(struct uf_callback *callback); + +static inline void +uf_callback_init(struct uf_callback *callback, +uf_callback_function *function) +{ + uf_work_init(&callback->work, (void (*)(void *))function, + callback); + callback->error = USBIF_ERROR_SUCCESS; +} + +static inline struct list_head * +uf_callback_to_link(struct uf_callback *callback) +{ + return uf_work_to_link(&callback->work); +} + +static inline struct uf_callback * +uf_callback_link_to(struct list_head *link) +{ + return container_of(uf_work_link_to(link), + struct uf_callback, work); +} + +static inline void +uf_callback_complete(struct uf_callback *callback, +usbif_error error) +{ + callback->error = error; + uf_work_schedule(&callback->work); +} + +static inline void +uf_callback_success(struct uf_callback *callback) +{ + uf_callback_complete(callback, 0); +} + +static inline void +uf_callback_set_error(struct uf_callback *callback, +usbif_error error) +{ + callback->error = error; +} + +static inline void +uf_callback_complete_synchronously(struct uf_callback *callback) +{ + uf_work_perform_synchronously(&callback->work); +} + +static inline usbif_error +uf_callback_query_error(struct uf_callback *callback) +{ + return callback->error; +} + +struct uf_callback_serialiser { + spinlock_t lock; + struct list_head list; + struct uf_work work; + int running; +}; + +void uf_callback_serialiser_function(void *context); + +#define UF_CALLBACK_SERIALISER_INIT( name ) { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .list = LIST_HEAD_INIT( name.list ), \ + .work = UF_WORK_INIT \ + ( name.work, uf_callback_serialiser_function, &name ),\ + .running = 0 \ +} + +#define UF_CALLBACK_SERIALISER( name ) \ +struct uf_callback_serialiser name = \ +UF_CALLBACK_SERIALISER_INIT( name ) + +static inline void +uf_callback_serialiser_complete_callback( +struct uf_callback_serialiser *serialiser, +struct uf_callback *callback, usbif_error error) +{ + unsigned long flags; + uf_callback_set_error(callback, error); + spin_lock_irqsave(&serialiser->lock, flags); + list_add_tail(uf_callback_to_link(callback), &serialiser->list); + spin_unlock_irqrestore(&serialiser->lock, flags); + uf_work_schedule(&serialiser->work); +} + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_channel.h Tue Oct 3 16:29:44 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 UF_CHANNEL_H +#define UF_CHANNEL_H + +#include +#include "uf_callback.h" + +struct uf_channel_ibmsg { + struct uf_callback callback; + struct usbif_response response; +}; + +static inline struct list_head * +uf_channel_ibmsg_to_link(struct uf_channel_ibmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct uf_callback * +uf_channel_ibmsg_to_callback(struct uf_channel_ibmsg *message) +{ + return &message->callback; +} + +static inline struct uf_channel_ibmsg * +uf_channel_ibmsg_callback_to(struct uf_callback *callback) +{ + return container_of(callback, struct uf_channel_ibmsg, callback); +} + +static inline void +uf_channel_ibmsg_init(struct uf_channel_ibmsg *message, +uf_callback_function *callback) +{ + uf_callback_init(uf_channel_ibmsg_to_callback(message), + callback); +} + +struct uf_channel_obmsg { + struct uf_callback callback; + struct usbif_request request; +}; + +static inline struct list_head * +uf_channel_obmsg_to_link(struct uf_channel_obmsg *message) +{ + return &message->callback.work.link; +} + +static inline struct uf_callback * +uf_channel_obmsg_to_callback(struct uf_channel_obmsg *message) +{ + return &message->callback; +} + +static inline struct uf_channel_obmsg * +uf_channel_obmsg_callback_to(struct uf_callback *callback) +{ + return container_of(callback, struct uf_channel_obmsg, callback); +} + +static inline void +uf_channel_obmsg_init(struct uf_channel_obmsg *message, +uf_callback_function *callback) +{ + uf_callback_init(uf_channel_obmsg_to_callback(message), + callback); +} + +struct uf_channel; + +typedef void +uf_channel_submit_message_function(struct uf_channel *channel, +struct uf_channel_obmsg *message); + +typedef void +uf_channel_connect_function(void *client_context); + +typedef void +uf_channel_handle_message_function(void *client_context, +struct uf_channel_ibmsg *message); + +typedef void +uf_channel_disconnect_function(void *client_context, +struct uf_callback *callback); + +struct uf_channel { + uf_channel_submit_message_function *submit_message; + void *client_context; + uf_channel_connect_function *connect; + uf_channel_handle_message_function *handle_message; + uf_channel_disconnect_function *disconnect; +}; + +static inline void +uf_channel_init(struct uf_channel *channel, +uf_channel_submit_message_function *submit_message ) +{ + channel->submit_message = submit_message; +} + +static inline void uf_channel_connect(struct uf_channel *channel) +{ + channel->connect(channel->client_context); +} + +static inline void +uf_channel_handle_message(struct uf_channel *channel, +struct uf_channel_ibmsg *message) +{ + channel->handle_message(channel->client_context, message); +} + +static inline void +uf_channel_disconnect(struct uf_channel *channel, +struct uf_callback *callback) +{ + channel->disconnect(channel->client_context, callback); +} + +static inline void +uf_channel_install_client(struct uf_channel *channel, +void *client_context, uf_channel_connect_function *connect, +uf_channel_handle_message_function *handle_message, +uf_channel_disconnect_function *disconnect) +{ + channel->client_context = client_context; + channel->connect = connect; + channel->handle_message = handle_message; + channel->disconnect = disconnect; +} + +static inline void +uf_channel_submit_message(struct uf_channel *channel, +struct uf_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + channel->submit_message(channel, message); +} + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_device.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_device.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,795 @@ +/*****************************************************************************/ +/* 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 "uf_device.h" +#include "uf_driver.h" +#include "uf_xb_channel.h" +#include "uf_trace.h" + +typedef enum { + uf_device_state_i, + uf_device_state_i_cn, + uf_device_state_i_cn_dn, + uf_device_state_i_cn_ps, + uf_device_state_i_cn_pf, + uf_device_state_i_cn_dn_ps, + uf_device_state_i_cn_ps_dn, + uf_device_state_i_cn_ps_pc, + uf_device_state_i_cn_ps_dn_rc +} uf_device_state; + +typedef enum { + uf_device_stimulus_cn, /* Gateway connect. */ + uf_device_stimulus_dn, /* Gateway disconnect. */ + uf_device_stimulus_ps, /* Probe driver success. */ + uf_device_stimulus_pf, /* Probe driver failure. */ + uf_device_stimulus_rc, /* Remove driver complete. */ + uf_device_stimulus_pt, /* Polling tick. */ + uf_device_stimulus_pc /* Probe complete. */ +} uf_device_stimulus; + +struct uf_device { + struct list_head lookup_link; + struct xenbus_device *dev; + void *drvdata; + spinlock_t lock; + uf_device_state state; + struct uf_xb_channel channel; + struct uf_gw gw; + struct uf_callback *gw_disconnect_callback; + int port_probe_count; + struct uf_gw_tra probe; + struct usb_port_status port_status; + struct uf_work probe_driver_1_work; + struct uf_work remove_driver_1_work; +}; + +static spinlock_t lookup_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(lookup_list); + +static void +uf_device_handle_stimulus(struct uf_device *device, +uf_device_stimulus stimulus); + +static inline struct uf_device *uf_device_gw_to(struct uf_gw *gw) +{ + return container_of(gw, struct uf_device, gw); +} + +void uf_device_set_drvdata(struct uf_device *device, void *data) +{ + device->drvdata = data; +} + +void *uf_device_get_drvdata(struct uf_device *device) +{ + return device->drvdata; +} + +struct device *uf_device_to_dev(struct uf_device *device) +{ + return &device->dev->dev; +} + +static void uf_device_add_to_lookup_list(struct uf_device *device) +{ + unsigned long flags; + spin_lock_irqsave(&lookup_lock, flags); + list_add(&device->lookup_link, &lookup_list); + spin_unlock_irqrestore(&lookup_lock, flags); +} + +static void uf_device_del_from_lookup_list(struct uf_device *device) +{ + unsigned long flags; + spin_lock_irqsave(&lookup_lock, flags); + list_del_init(&device->lookup_link); + spin_unlock_irqrestore(&lookup_lock, flags); +} + +struct uf_device *uf_device_xenbus_dev_to(struct xenbus_device *dev) +{ + struct uf_device * device = NULL; + struct uf_device * cur; + unsigned long flags; + spin_lock_irqsave(&lookup_lock, flags); + list_for_each_entry(cur, &lookup_list, lookup_link){ + if(cur->dev == dev ) { + device = cur; + break; + } + } + spin_unlock_irqrestore(&lookup_lock, flags); + return device; +} + +struct uf_device *uf_device_dev_to(struct device *dev) +{ + return uf_device_xenbus_dev_to(to_xenbus_device(dev)); +} + +int uf_device_query_port_status_changed(struct uf_device *device) +{ + int changed; + unsigned long flags; + spin_lock_irqsave(&device->lock, flags); + uf_device_handle_stimulus(device, uf_device_stimulus_pt); + changed = (device->port_status.wPortChange != 0); + spin_unlock_irqrestore(&device->lock, flags); + return changed; +} + +struct usb_port_status uf_device_query_port_status(struct uf_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 uf_device_set_port_power(struct uf_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 uf_device_set_port_reset(struct uf_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; + uf_device_handle_stimulus(device, uf_device_stimulus_pt); + spin_unlock_irqrestore(&device->lock, flags); +} + +void uf_device_clear_port_enable(struct uf_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 uf_device_clear_port_connection_change(struct uf_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 uf_device_clear_port_reset_change(struct uf_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 uf_device_query_domain(struct uf_device *device) +{ + trace(); + return device->dev->otherend_id; +} + +void uf_device_submit_tra(struct uf_device *device, struct uf_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + uf_gw_submit_tra(&device->gw, tra); +} + +static void uf_device_gw_connect(struct uf_gw *gw) +{ + struct uf_device *device = uf_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); + uf_device_handle_stimulus(device, uf_device_stimulus_cn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void +uf_device_gw_disconnect(struct uf_gw *gw, struct uf_callback *callback) +{ + struct uf_device *device = uf_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; + uf_device_handle_stimulus(device, uf_device_stimulus_dn); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void uf_device_invalid_stimulus(struct uf_device *device, +uf_device_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: device %p in state %d received invalid " + "stimulus %d\n", device, device->state, stimulus); +} + +static void uf_device_probe_driver(struct uf_device *device) +{ + trace(); + (void)uf_work_schedule(&device->probe_driver_1_work); +} + +static void uf_device_probe_driver_1(void *data) +{ + struct uf_device *device = data; + uf_device_stimulus stimulus; + unsigned long flags; + trace(); + if (uf_driver_probe(device) == 0) { + stimulus = uf_device_stimulus_ps; + } else { + stimulus = uf_device_stimulus_pf; + } + spin_lock_irqsave(&device->lock, flags); + uf_device_handle_stimulus(device, stimulus); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void uf_device_remove_driver(struct uf_device *device) +{ + trace(); + (void)uf_work_schedule(&device->remove_driver_1_work); +} + +static void uf_device_remove_driver_1(void *data) +{ + struct uf_device *device = data; + unsigned long flags; + trace(); + uf_driver_remove(device); + spin_lock_irqsave(&device->lock, flags); + uf_device_handle_stimulus(device, uf_device_stimulus_rc); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void uf_device_probe_all_ports(struct uf_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; + } + uf_gw_submit_tra(&device->gw, &device->probe); +} + +static void uf_device_probe_all_ports_1(struct uf_callback *callback) +{ + struct uf_device *device = container_of( + uf_gw_tra_callback_to(callback), + struct uf_device, probe); + unsigned long flags; + spin_lock_irqsave(&device->lock, flags); + if (device->probe.parameters.header.tra_type == + USBIF_TRA_TYPE_PROBE) { + if (uf_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( + uf_callback_query_error(callback))); + } + } else { + struct usb_port_status *port_status = &device->port_status; + if ((uf_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 (uf_callback_query_error(callback) != + USBIF_ERROR_SUCCESS) { + printk(KERN_ERR "uf: device %p: error %d" + " resetting port", device, + usbif_error_map_to_local( + uf_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) + uf_device_handle_stimulus(device, + uf_device_stimulus_pc); + spin_unlock_irqrestore(&device->lock, flags); +} + +static void uf_device_disconnect_all_ports(struct uf_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 uf_device_complete_gw_disconnect(struct uf_device *device) +{ + trace(); + uf_callback_success(device->gw_disconnect_callback); +} + +static int uf_device_resume_or_suspend(struct uf_device *device, int suspend) +{ + trace(); + if (suspend) + goto suspend_path; + uf_xb_channel_connect(&device->channel, device->dev); + return 0; + suspend_path: + uf_xb_channel_disconnect(&device->channel); + return 0; +} + +static int uf_device_init_or_exit(struct uf_device *device, +struct xenbus_device *dev, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + INIT_LIST_HEAD(&device->lookup_link); + device->dev = dev; + uf_device_add_to_lookup_list(device); + spin_lock_init(&device->lock); + device->state = uf_device_state_i; + uf_gw_tra_init(&device->probe, + uf_device_probe_all_ports_1); + uf_work_init(&device->probe_driver_1_work, + uf_device_probe_driver_1, device); + uf_work_init(&device->remove_driver_1_work, + uf_device_remove_driver_1, device); + return_value = uf_xb_channel_init(&device->channel); + if (return_value != 0) + goto exit_no_channel; + return_value = uf_gw_init(&device->gw, + uf_xb_channel_to_channel(&device->channel), + uf_device_gw_connect, + uf_device_gw_disconnect, + USBIF_QUOTA); + if (return_value != 0) + goto exit_no_gw; + return_value = uf_device_resume_or_suspend(device, 0); + if (return_value != 0) + goto exit_no_resume; + return 0; + exit_path: + (void)uf_device_resume_or_suspend(device, 1); + exit_no_resume: + uf_gw_exit(&device->gw); + exit_no_gw: + uf_xb_channel_exit(&device->channel); + exit_no_channel: + uf_device_del_from_lookup_list(device); + return return_value; +} + +static int uf_device_init(struct uf_device *device, struct xenbus_device *dev) +{ + trace(); + return uf_device_init_or_exit(device, dev, 0); +} + +static void uf_device_exit(struct uf_device *device) +{ + trace(); + (void)uf_device_init_or_exit(device, NULL, 1); +} + +static int uf_device_probe_or_remove(struct xenbus_device *dev, int remove) +{ + int return_value = 0; + struct uf_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; + } + return_value = uf_device_init(device, dev); + if (return_value != 0) + goto exit_init_failed; + return 0; + remove_path: + device = uf_device_xenbus_dev_to(dev); + uf_device_exit(device); + exit_init_failed: + kfree(device); + exit_no_device: + return return_value; +} + +static int +uf_device_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) +{ + trace(); + return uf_device_probe_or_remove(dev, 0); +} + +static int uf_device_remove(struct xenbus_device *dev) +{ + trace(); + return uf_device_probe_or_remove(dev, 1); +} + +static int uf_device_resume(struct xenbus_device *dev) +{ + struct uf_device *device = uf_device_xenbus_dev_to(dev); + trace(); + (void)uf_device_resume_or_suspend(device, 1); + return uf_device_resume_or_suspend(device, 0); +} + +static void +uf_device_backend_changed(struct xenbus_device *dev, XenbusState state) +{ + struct uf_device *device = uf_device_xenbus_dev_to(dev); + trace(); + uf_xb_channel_backend_changed(&device->channel, dev, state); +} + +static struct xenbus_device_id uf_device_ids[] = { + {"usbport"}, + {""} +}; + +static struct xenbus_driver uf_device_driver = { + .name = "usbport", + .owner = THIS_MODULE, + .ids = uf_device_ids, + .probe = uf_device_probe, + .remove = uf_device_remove, + .resume = uf_device_resume, + .otherend_changed = uf_device_backend_changed, +}; + +int uf_device_class_init(void) +{ + trace(); + return xenbus_register_frontend(&uf_device_driver); +} + +void uf_device_class_exit(void) +{ + trace(); + xenbus_unregister_driver(&uf_device_driver); +} + +static void uf_device_handle_stimulus(struct uf_device *device, +uf_device_stimulus stimulus) +{ + switch (device->state) { + case uf_device_state_i: + switch (stimulus) { + case uf_device_stimulus_cn: + device->state = uf_device_state_i_cn; + uf_device_probe_driver(device); + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn: + /* Probing driver. */ + switch (stimulus) { + case uf_device_stimulus_dn: + device->state = uf_device_state_i_cn_dn; + break; + case uf_device_stimulus_ps: + device->state = uf_device_state_i_cn_ps; + uf_device_probe_all_ports(device); + break; + case uf_device_stimulus_pf: + device->state = uf_device_state_i_cn_pf; + break; + case uf_device_stimulus_pt: + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_dn: + /* Probing driver. */ + /* Gateway disconnecting. */ + switch (stimulus) { + case uf_device_stimulus_ps: + device->state = uf_device_state_i_cn_dn_ps; + uf_device_remove_driver(device); + break; + case uf_device_stimulus_pf: + device->state = uf_device_state_i; + uf_device_disconnect_all_ports(device); + uf_device_complete_gw_disconnect(device); + break; + case uf_device_stimulus_pt: + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_ps: + /* Driver Probed. */ + /* Polling ports. */ + switch (stimulus) { + case uf_device_stimulus_dn: + device->state = uf_device_state_i_cn_ps_dn; + uf_device_remove_driver(device); + break; + case uf_device_stimulus_pt: + break; + case uf_device_stimulus_pc: + device->state = uf_device_state_i_cn_ps_pc; + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_pf: + /* Driver probe failed. */ + switch (stimulus) { + case uf_device_stimulus_dn: + device->state = uf_device_state_i; + uf_device_disconnect_all_ports(device); + uf_device_complete_gw_disconnect(device); + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_dn_ps: + /* Gateway disconnecting. */ + /* Removing driver. */ + switch (stimulus) { + case uf_device_stimulus_rc: + device->state = uf_device_state_i; + uf_device_disconnect_all_ports(device); + uf_device_complete_gw_disconnect(device); + break; + case uf_device_stimulus_pt: + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_ps_dn: + /* Gateway disconnecting. */ + /* Removing driver. */ + /* Polling ports. */ + switch (stimulus) { + case uf_device_stimulus_rc: + device->state = uf_device_state_i_cn_ps_dn_rc; + break; + case uf_device_stimulus_pt: + break; + case uf_device_stimulus_pc: + device->state = uf_device_state_i_cn_dn_ps; + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_ps_pc: + /* Driver Probed. */ + switch (stimulus) { + case uf_device_stimulus_dn: + device->state = uf_device_state_i_cn_dn_ps; + uf_device_remove_driver(device); + break; + case uf_device_stimulus_pt: + device->state = uf_device_state_i_cn_ps; + uf_device_probe_all_ports(device); + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + case uf_device_state_i_cn_ps_dn_rc: + /* Gateway disconnecting. */ + /* Polling ports. */ + switch (stimulus) { + case uf_device_stimulus_pc: + device->state = uf_device_state_i; + uf_device_disconnect_all_ports(device); + uf_device_complete_gw_disconnect(device); + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } + break; + default: + uf_device_invalid_stimulus(device, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_device.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_device.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,48 @@ +/*****************************************************************************/ +/* 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 UF_DEVICE_H +#define UF_DEVICE_H + +#include +#include +#include +#include "../../usb/core/hcd.h" +#include "uf_gw.h" + +int uf_device_class_init(void); +void uf_device_class_exit(void); + +struct uf_device; + +void uf_device_set_drvdata(struct uf_device *device, void *data); +void *uf_device_get_drvdata(struct uf_device *device); +struct device *uf_device_to_dev(struct uf_device *device); +struct uf_device *uf_device_dev_to(struct device *dev); +int uf_device_query_port_status_changed(struct uf_device *device); +struct usb_port_status uf_device_query_port_status(struct uf_device *device); +void uf_device_set_port_power(struct uf_device *device); +void uf_device_set_port_reset(struct uf_device *device); +void uf_device_clear_port_enable(struct uf_device *device); +void uf_device_clear_port_connection_change(struct uf_device *device); +void uf_device_clear_port_reset_change(struct uf_device *device); +domid_t uf_device_query_domain(struct uf_device *device); +void uf_device_submit_tra(struct uf_device *device, struct uf_gw_tra *tra); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_driver.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_driver.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,429 @@ +/*****************************************************************************/ +/* 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 "uf_driver.h" +#include "uf_ep.h" +#include "uf_trace.h" + +#define UF_DRIVER_EP_COUNT 32 + +struct uf_hcd { + struct uf_ep ep[UF_DRIVER_EP_COUNT]; +}; + +static struct uf_hcd *uf_hcd_hcd_to_uhcd(struct usb_hcd *hcd) +{ + return (struct uf_hcd *)(hcd->hcd_priv); +} + +static struct uf_device *uf_hcd_hcd_to_uf_device(struct usb_hcd *hcd) +{ + return uf_device_dev_to(hcd_to_bus(hcd)->controller); +} + + +static int uf_hcd_start_or_stop(struct usb_hcd *hcd, int stop) +{ + int return_value = 0; + struct uf_hcd *uhcd = uf_hcd_hcd_to_uhcd(hcd); + int i = UF_DRIVER_EP_COUNT; + trace(); + if (stop) + goto stop_path; + memset(uhcd, 0, sizeof(*uhcd)); + for (i = 0; i < UF_DRIVER_EP_COUNT; i++) { + struct uf_ep *ep = &uhcd->ep[i]; + return_value = uf_ep_init(ep, uf_hcd_hcd_to_uf_device(hcd)); + if (return_value != 0) + goto exit_no_ep; + } + hcd->state = HC_STATE_RUNNING; /* FIXME: breaks encapsulation */ + return 0; + stop_path: + exit_no_ep: + for( i--; i >= 0; i-- ) + uf_ep_exit(&uhcd->ep[i]); + return return_value; +} + +static int uf_hcd_start(struct usb_hcd *hcd) +{ + trace(); + return uf_hcd_start_or_stop(hcd, 0); +} + +static void uf_hcd_stop(struct usb_hcd *hcd) +{ + trace(); + (void)uf_hcd_start_or_stop(hcd, 1); +} + +static int uf_hcd_get_frame_number(struct usb_hcd *hcd) +{ + trace(); + return 0; +} + +static int +uf_hcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, +struct urb *urb, gfp_t mem_flags) +{ + struct uf_hcd *uhcd = uf_hcd_hcd_to_uhcd(hcd); + int ep_index = usb_pipeendpoint(urb->pipe) + + (usb_pipein(urb->pipe) ? 16 : 0); + trace(); + uf_ep_urb_enqueue(&uhcd->ep[ep_index], urb); + return 0; +} + +static int uf_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct uf_hcd *uhcd = uf_hcd_hcd_to_uhcd(hcd); + int ep_index = usb_pipeendpoint(urb->pipe) + + (usb_pipein(urb->pipe) ? 16 : 0); + trace(); + return uf_ep_urb_dequeue(&uhcd->ep[ep_index], urb); +} + +static void +uf_hcd_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +{ + trace(); + /* FIXME: need to call uf_ep_disable for the right endpoint */ +} + +static int uf_hcd_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + int changed = 0; + struct uf_device *device = uf_hcd_hcd_to_uf_device(hcd); + *buf = 0; + if (uf_device_query_port_status_changed(device)) { + *buf |= 2; + changed = 1; + } + return changed ? 1 : 0; +} + +static int uf_hcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, +u16 wIndex, char *buf, u16 wLength) +{ + struct uf_device *device = uf_hcd_hcd_to_uf_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 = + uf_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"); + uf_device_set_port_reset(device); + break; + case USB_PORT_FEAT_POWER: + trace_info("USB_PORT_FEAT_POWER"); + uf_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"); + uf_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"); + uf_device_clear_port_connection_change(device); + break; + case USB_PORT_FEAT_C_RESET: + trace_info("USB_PORT_C_RESET"); + uf_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 uf_hcd_start_port_reset(struct usb_hcd *hcd, unsigned port_num) +{ + trace(); + return 0; +} + +static struct hc_driver uf_hc_driver = { + .description = "uf_hc_driver", + .product_desc = "Xen USB Front-End Driver", + .hcd_priv_size = sizeof(struct uf_hcd), + /* .irq */ + .flags = HCD_USB2, + /* reset optional. */ + .start = uf_hcd_start, + /* suspend optional. */ + /* resume optional. */ + .stop = uf_hcd_stop, + .get_frame_number = uf_hcd_get_frame_number, + .urb_enqueue = uf_hcd_urb_enqueue, + .urb_dequeue = uf_hcd_urb_dequeue, + .endpoint_disable = uf_hcd_endpoint_disable, + .hub_status_data = uf_hcd_hub_status_data, + .hub_control = uf_hcd_hub_control, + /* hub_suspend optional. */ + /* hub_resume optional. */ + .start_port_reset = uf_hcd_start_port_reset, +}; + +static int uf_driver_probe_or_remove(struct uf_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; + } + trace_info( "Trying to allocate %d bytes for uf_hcd", sizeof(struct uf_hcd)); + if ((hcd = usb_create_hcd(&uf_hc_driver, + uf_device_to_dev(device), + uf_device_to_dev(device)->bus_id))== NULL) { + trace_info("usb_create_hcd failed"); + return_value = -ENOMEM; + goto exit_no_hcd; + } + uf_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 = uf_device_get_drvdata(device); + usb_remove_hcd(hcd); + exit_no_add: + usb_put_hcd(hcd); + uf_device_set_drvdata(device, NULL); + exit_no_hcd: + exit_no_usb: + return return_value; +} + +int uf_driver_probe(struct uf_device *device) +{ + trace(); + return uf_driver_probe_or_remove(device, 0); +} + +void uf_driver_remove(struct uf_device *device) +{ + trace(); + uf_driver_probe_or_remove(device, 1); +} + +int uf_driver_class_init(void) +{ + trace(); + return uf_ep_class_init(); +} + +void uf_driver_class_exit(void) +{ + trace(); + uf_ep_class_exit(); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_driver.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_driver.h Tue Oct 3 16:29:44 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 UF_DRIVER_H +#define UF_DRIVER_H + +#include "uf_device.h" + +int uf_driver_class_init(void); +void uf_driver_class_exit(void); +int uf_driver_probe(struct uf_device *device); +void uf_driver_remove(struct uf_device *device); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ep.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ep.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,452 @@ +/*****************************************************************************/ +/* 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 "uf_ep.h" +#include "uf_rsrc.h" +#include "uf_sll.h" +#include "uf_trace.h" + +/* When there is an error, we are supposed to guarantee that the URB queue */ +/* stalls until the completion handlers of the failing URB and all URBs */ +/* unlinked as a result have returned. This guarantee is satisfied as */ +/* follows: when an URB fails in the BE, the BE endpoint unlinks all */ +/* in-progress URBs and goes into a stalled state such that newly arriving */ +/* URBs will get returned as unlinked. */ +/* When the FE gets an URB returning with an unlinked or failure error, the */ +/* FE endpoint stalls and waits for all outstanding requests to the BE to */ +/* return with error or unlinked status. Once the FE endpoint is quiesced */ +/* and all URB completions are done, the FE endpoint restarts any stalled */ +/* URBs, the first of which gets the CLEAR_STALL flag set to unstall the BE */ +/* endpoint. */ +/* Explicit unlinks by the FE are dealt with by noting in the FE that the */ +/* URB is to be returned as unlinked rather than retried and then sending a */ +/* request to the BE to stall the queue and go through the same quiesce */ +/* process. */ + +typedef enum { + uf_ep_stimulus_ue, /* URB enqueue/rsrc became free */ + uf_ep_stimulus_ud, /* URB dequeue/error */ + uf_ep_stimulus_bz, /* Busy count drops to zero */ + uf_ep_stimulus_ed, /* endpoint disable */ +} uf_ep_stimulus; + +static void uf_ep_handle_stimulus(struct uf_ep *ep, uf_ep_stimulus stimulus); + +void uf_ep_urb_enqueue(struct uf_ep *ep, struct urb *urb) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + uf_slk_init((struct uf_slk *)&urb->hcpriv); + uf_sll_add_last(&ep->urb_sll, (struct uf_slk *)&urb->hcpriv); + uf_ep_handle_stimulus(ep, uf_ep_stimulus_ue); + spin_unlock_irqrestore(&ep->lock, flags); +} + +int uf_ep_urb_dequeue(struct uf_ep *ep, struct urb *urb) +{ + /* return -EINPROGRESS iff URB will be unlinked and complete with */ + /* -ECONNRESET. */ + int done, result = -EINPROGRESS; + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + ep->busy_count++; + uf_ep_handle_stimulus(ep, uf_ep_stimulus_ud); + done = uf_sll_remove_slk(&ep->urb_sll, (struct uf_slk *)&urb->hcpriv); + spin_unlock_irqrestore(&ep->lock, flags); + if (done) { + urb->hcpriv = 0; + urb->status = -ECONNRESET; + urb->actual_length = 0; + local_irq_save(flags); + usb_hcd_giveback_urb(uf_device_get_drvdata(ep->device),urb,0); + local_irq_restore(flags); + } else { + result = uf_rsrc_urb_dequeue(urb); + } + spin_lock_irqsave(&ep->lock, flags); + if (done || (result != -EINPROGRESS)) + ep->busy_count--; + if (ep->busy_count == 0) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_bz); + spin_unlock_irqrestore(&ep->lock, flags); + return result; +} + +void uf_ep_disable(struct uf_ep *ep) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + uf_ep_handle_stimulus(ep, uf_ep_stimulus_ed); + spin_unlock_irqrestore(&ep->lock, flags); + uf_work_until(ep->state == uf_ep_state_i); +} + +void uf_ep_rsrc_stalled(struct uf_ep *ep) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + if (--ep->busy_count == 0) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_bz); + spin_unlock_irqrestore(&ep->lock, flags); +} + +static void uf_ep_rsrc_completed(struct uf_callback * callback) +{ + struct uf_rsrc *rsrc = uf_rsrc_callback_to(callback); + struct uf_ep *ep = uf_rsrc_query_ep(rsrc); + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + list_del_init(uf_rsrc_to_link(rsrc)); + list_add(uf_rsrc_to_link(rsrc), &ep->free_rsrc_list); + uf_ep_handle_stimulus(ep, uf_ep_stimulus_ue); + if (uf_callback_query_error(callback) != USBIF_ERROR_SUCCESS) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_ud); + if (uf_callback_query_error(callback) == USBIF_ERROR_UNLINKED) + ep->busy_count--; + if (--ep->busy_count == 0) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_bz); + spin_unlock_irqrestore(&ep->lock, flags); +} + +static void uf_ep_invalid_stimulus(struct uf_ep *ep, uf_ep_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: ep %p in state %d" + "received invalid stimulus %d", ep, ep->state, stimulus); +} + +static void uf_ep_clear_stall(struct uf_ep *ep) +{ + trace(); + ep->clear_stall = 1; +} + +static void uf_ep_kick_urbs(struct uf_ep *ep) +{ + trace(); + while (!uf_sll_is_empty(&ep->urb_sll) && + !list_empty(&ep->free_rsrc_list)) { + struct urb *urb = container_of( + (void **)uf_sll_remove_first( + &ep->urb_sll), + struct urb, hcpriv); + struct uf_rsrc *rsrc = list_entry( + ep->free_rsrc_list.next, + struct uf_rsrc, + link); + list_del_init(uf_rsrc_to_link(rsrc)); + list_add_tail(uf_rsrc_to_link(rsrc), &ep->used_rsrc_list); + ep->busy_count++; + uf_rsrc_start_urb(rsrc, urb, ep->clear_stall); + ep->clear_stall = 0; + } + if (ep->busy_count == 0) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_bz); +} + +static void uf_ep_restart_urbs(struct uf_ep *ep) +{ + struct uf_rsrc *rsrc; + trace(); + list_for_each_entry(rsrc, &ep->used_rsrc_list, link) { + ep->busy_count++; + uf_rsrc_restart_urb(rsrc, ep->clear_stall); + ep->clear_stall = 0; + } +} + +static void uf_ep_submit_stall(struct uf_ep *ep) +{ + struct usbif_stall_tra_parameters *parameters = + &ep->stall_tra.parameters.stall; + trace(); + ep->busy_count++; + memset(parameters, 0, sizeof(*parameters)); + parameters->header.tra_type = USBIF_TRA_TYPE_STALL; + uf_device_submit_tra(ep->device, &ep->stall_tra); +} + +static void uf_ep_submit_stall_1(struct uf_callback *callback) +{ + struct uf_ep *ep = container_of(uf_gw_tra_callback_to(callback), + struct uf_ep, stall_tra); + unsigned long flags; + trace(); + spin_lock_irqsave(&ep->lock, flags); + if (--ep->busy_count == 0) + uf_ep_handle_stimulus(ep, uf_ep_stimulus_bz); + spin_unlock_irqrestore(&ep->lock, flags); +} + +static void uf_ep_complete_disable(struct uf_ep *ep) +{ + uf_work_wake_up(); +} + +static void uf_ep_handle_stimulus(struct uf_ep *ep, uf_ep_stimulus stimulus) +{ + trace_info("ep %p in state %d received stimulus %d", ep, ep->state, + stimulus); + switch (ep->state) { + /* Idle. */ + case uf_ep_state_i: + switch (stimulus) { + case uf_ep_stimulus_ue: + ep->state = uf_ep_state_i_ue; + uf_ep_kick_urbs(ep); + break; + case uf_ep_stimulus_ed: + uf_ep_complete_disable(ep); + break; + default: + uf_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + /* Busy */ + case uf_ep_state_i_ue: + switch (stimulus) { + case uf_ep_stimulus_ue: + uf_ep_kick_urbs(ep); + break; + case uf_ep_stimulus_ud: + ep->state = uf_ep_state_i_ue_ud; + uf_ep_submit_stall(ep); + break; + case uf_ep_stimulus_bz: + ep->state = uf_ep_state_i; + uf_ep_complete_disable(ep); + break; + case uf_ep_stimulus_ed: + /* We always call complete_disable when we go idle */ + /* so there's no need to remember that this is */ + /* outstanding. */ + break; + default: + uf_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + /* Quiescing for stall. */ + case uf_ep_state_i_ue_ud: + switch (stimulus) { + case uf_ep_stimulus_ue: + case uf_ep_stimulus_ud: + break; + case uf_ep_stimulus_bz: + ep->state = uf_ep_state_i_ue; + uf_ep_clear_stall(ep); + uf_ep_restart_urbs(ep); + uf_ep_kick_urbs(ep); + break; + case uf_ep_stimulus_ed: + break; + default: + uf_ep_invalid_stimulus(ep, stimulus); + break; + } + break; + default: + uf_ep_invalid_stimulus(ep, stimulus); + break; + } +} + +static int +uf_ep_init_or_exit(struct uf_ep *ep, struct uf_device *device, int exit) +{ + int return_value = 0; + int i = UF_EP_QUOTA; + trace(); + if (exit) + goto exit_path; + ep->device = device; + spin_lock_init(&ep->lock); + ep->state = uf_ep_state_i; + ep->busy_count = 0; + ep->clear_stall = 0; + uf_sll_init(&ep->urb_sll); + INIT_LIST_HEAD(&ep->free_rsrc_list); + INIT_LIST_HEAD(&ep->used_rsrc_list); + for (i = 0; i < UF_EP_QUOTA; i++) { + ep->rsrcs[i]=kzalloc(sizeof(struct uf_rsrc),GFP_KERNEL); + if (ep->rsrcs[i]==NULL) { + return_value = -ENOMEM; + goto exit_no_rsrc; + } + } + for (i = 0; i < UF_EP_QUOTA; i++) { + struct uf_rsrc *rsrc = ep->rsrcs[i]; + return_value = uf_rsrc_init(rsrc, ep, uf_ep_rsrc_completed); + if (return_value != 0) { + trace_info("uf_rsrc_init failed"); + goto exit_no_rsrc_init; + } + list_add(uf_rsrc_to_link(rsrc), &ep->free_rsrc_list); + } + uf_gw_tra_init(&ep->stall_tra, uf_ep_submit_stall_1); + return 0; + exit_path: + exit_no_rsrc_init: + while (!list_empty(&ep->free_rsrc_list)) { + struct uf_rsrc *rsrc = list_entry( + ep->free_rsrc_list.next, + struct uf_rsrc, + callback.work.link); + list_del_init(uf_rsrc_to_link(rsrc)); + uf_rsrc_exit(rsrc); + } + exit_no_rsrc: + for(i--;i>=0;i--) + kfree(ep->rsrcs[i]); + return return_value; +} + +int uf_ep_init(struct uf_ep *ep, struct uf_device *device) +{ + trace(); + return uf_ep_init_or_exit(ep, device, 0); +} + +void uf_ep_exit(struct uf_ep *ep) +{ + trace(); + (void)uf_ep_init_or_exit(ep, NULL, 1); +} + +int uf_ep_class_init(void) +{ + trace(); + return uf_rsrc_class_init(); +} + +void uf_ep_class_exit(void) +{ + trace(); + uf_rsrc_class_exit(); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ep.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ep.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* 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 UF_EP_H +#define UF_EP_H + +#include "uf_device.h" +#include "uf_rsrc.h" +#include "uf_sll.h" + +#define UF_EP_QUOTA 4 + +typedef enum { + uf_ep_state_i, + uf_ep_state_i_ue, + uf_ep_state_i_ue_ud +} uf_ep_state; + +struct uf_ep { + struct uf_device *device; + spinlock_t lock; + uf_ep_state state; + int busy_count; + int clear_stall; + int stalled; + struct uf_sll urb_sll; + struct list_head free_rsrc_list; + struct list_head used_rsrc_list; + struct uf_rsrc *rsrcs[UF_EP_QUOTA]; + struct uf_gw_tra stall_tra; +}; + +static inline struct uf_device *uf_ep_query_device(struct uf_ep *ep) +{ + return ep->device; +} + +void uf_ep_urb_enqueue(struct uf_ep *ep, struct urb *urb); +int uf_ep_urb_dequeue(struct uf_ep *ep, struct urb *urb); +void uf_ep_disable(struct uf_ep *ep); +void uf_ep_rsrc_stalled(struct uf_ep *ep); +int uf_ep_init(struct uf_ep *ep, struct uf_device *device); +void uf_ep_exit(struct uf_ep *ep); +int uf_ep_class_init(void); +void uf_ep_class_exit(void); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw.c Tue Oct 3 16:29:44 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 "uf_gw.h" +#include "uf_gw_rsrc.h" +#include "uf_trace.h" + +typedef enum { + uf_gw_stimulus_tq, /* Transaction queued. */ + uf_gw_stimulus_cc, /* Channel connect. */ + uf_gw_stimulus_cd, /* Channel disconnect. */ + uf_gw_stimulus_kt, /* Kick tras completed. */ + uf_gw_stimulus_ic, /* Initiator rsrc completed. */ + uf_gw_stimulus_ii, /* Initiator rsrcs idle. */ + uf_gw_stimulus_lc, /* Client connect completed. */ + uf_gw_stimulus_lg, /* Client disconnect called. */ + uf_gw_stimulus_ld, /* Client disconnect completed. */ +} uf_gw_stimulus; + +static void +uf_gw_handle_stimulus(struct uf_gw *gw, +uf_gw_stimulus stimulus); + +void +uf_gw_submit_tra(struct uf_gw *gw, struct uf_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(uf_gw_tra_to_link(tra), &gw->tra_list); + uf_gw_handle_stimulus(gw, uf_gw_stimulus_tq); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_channel_connect(void *context) +{ + struct uf_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + uf_gw_handle_stimulus(gw, uf_gw_stimulus_cc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_handle_channel_message(void *context, +struct uf_channel_ibmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct uf_gw *gw = context; + struct uf_gw_rsrc *rsrc; + usbif_error error = USBIF_ERROR_INVALID_PROTOCOL; + if (message->response.gw_status.id >= gw->initiator_quota) + goto complete_channel_message; + rsrc = &gw->initiator_rsrcs[message->response.gw_status.id]; + if (uf_gw_rsrc_handle_response(rsrc, &message->response) + != 0) + goto complete_channel_message; + error = USBIF_ERROR_SUCCESS; + complete_channel_message: + uf_callback_complete(uf_channel_ibmsg_to_callback(message), + error); +} + +static void uf_gw_channel_disconnect(void *context, +struct uf_callback *callback) +{ + struct uf_gw *gw = context; + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + gw->channel_disconnect_callback = callback; + uf_gw_handle_stimulus(gw, uf_gw_stimulus_cd); + spin_unlock_irqrestore(&gw->lock, flags); +} + +void uf_gw_submit_channel_message(struct uf_gw *gw, +struct uf_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + uf_channel_submit_message(gw->channel, message); +} + +static void uf_gw_invalid_stimulus(struct uf_gw *gw, +uf_gw_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: gw %p in state %d" + "received invalid stimulus %d", gw, gw->state, stimulus); +} + +static void uf_gw_kick_tras(struct uf_gw *gw) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + if (!gw->kick_tras_out) { + gw->kick_tras_out = 1; + (void)uf_work_schedule(&gw->kick_tras_1_work); + } +} + +static void uf_gw_kick_tras_1(void *data) +{ + struct uf_gw *gw = data; + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + while ((!list_empty(&gw->tra_list)) && + (!list_empty(&gw->initiator_rsrc_list))) { + struct uf_gw_tra *tra; + struct uf_gw_rsrc *rsrc; + tra = list_entry(gw->tra_list.next, + struct uf_gw_tra, + callback.work.link); + rsrc = list_entry(gw->initiator_rsrc_list.next, + struct uf_gw_rsrc, + callback.work.link); + list_del_init(uf_gw_tra_to_link(tra)); + list_del_init(uf_gw_rsrc_to_link(rsrc)); + gw->initiator_rsrcs_out++; + spin_unlock_irqrestore(&gw->lock, flags); + uf_gw_rsrc_start(rsrc, tra); + spin_lock_irqsave(&gw->lock, flags); + } + gw->kick_tras_out = 0; + uf_gw_handle_stimulus(gw, uf_gw_stimulus_kt); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_kick_tras_2(struct uf_callback *callback) +{ + struct uf_gw_rsrc *rsrc = + uf_gw_rsrc_callback_to(callback); + struct uf_gw *gw = uf_gw_rsrc_query_gw(rsrc); + unsigned long flags; + spin_lock_irqsave(&gw->lock, flags); + list_add_tail(uf_gw_rsrc_to_link(rsrc), + &gw->initiator_rsrc_list); + if (--gw->initiator_rsrcs_out != 0) { + uf_gw_handle_stimulus(gw, uf_gw_stimulus_ic); + } else { + uf_gw_handle_stimulus(gw, uf_gw_stimulus_ii); + } + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_fail_out_tras(struct uf_gw *gw) +{ + trace(); + while (!list_empty(&gw->tra_list)) { + struct uf_gw_tra *tra = list_entry(gw->tra_list.next, + struct uf_gw_tra, callback.work.link); + list_del_init(uf_gw_tra_to_link(tra)); + uf_callback_complete(uf_gw_tra_to_callback(tra), + USBIF_ERROR_DISCONNECT); + } +} + +static void uf_gw_connect_client(struct uf_gw *gw) +{ + trace(); + (void)uf_work_schedule(&gw->connect_client_1_work); +} + +static void uf_gw_connect_client_1(void *data) +{ + struct uf_gw *gw = data; + unsigned long flags; + trace(); + gw->connect(gw); + spin_lock_irqsave(&gw->lock, flags); + uf_gw_handle_stimulus(gw, uf_gw_stimulus_lc); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_disconnect_client(struct uf_gw *gw) +{ + trace(); + (void)uf_work_schedule(&gw->disconnect_client_1_work); +} + +static void uf_gw_disconnect_client_1(void *data) +{ + struct uf_gw *gw = data; + unsigned long flags; + trace(); + gw->disconnect(gw, &gw->disconnect_client_2_callback); + spin_lock_irqsave(&gw->lock, flags); + uf_gw_handle_stimulus(gw, uf_gw_stimulus_lg); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_disconnect_client_2(struct uf_callback *callback) +{ + struct uf_gw *gw = container_of(callback, struct uf_gw, + disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&gw->lock, flags); + uf_gw_handle_stimulus(gw, uf_gw_stimulus_ld); + spin_unlock_irqrestore(&gw->lock, flags); +} + +static void uf_gw_abort_initiator_rsrcs(struct uf_gw *gw) +{ + u32 i; + trace(); + for (i = 0; i < gw->initiator_quota; i++) + uf_gw_rsrc_abort(&gw->initiator_rsrcs[i], + USBIF_ERROR_DISCONNECT); +} + +static void uf_gw_test_initiator_rsrcs(struct uf_gw *gw) +{ + trace(); + if (gw->initiator_rsrcs_out == 0) + uf_gw_handle_stimulus(gw, uf_gw_stimulus_ii); +} + +static void uf_gw_complete_channel_disconnect(struct uf_gw *gw) +{ + trace(); + uf_callback_success(gw->channel_disconnect_callback); +} + +static int uf_gw_init_or_exit(struct uf_gw *gw, int exit) +{ + int return_value = 0; + u32 i; + trace(); + if (exit) + goto exit_path; + INIT_LIST_HEAD(&gw->initiator_rsrc_list); + if (gw->initiator_quota != 0) { + /*FIXME: kmalloc > page size*/ + gw->initiator_rsrcs = kmalloc(sizeof( + struct uf_gw_rsrc) * + gw->initiator_quota, GFP_KERNEL); + if (gw->initiator_rsrcs == NULL) { + return_value = -ENOMEM; + goto exit_no_initiator_rsrcs; + } + for (i = 0; i < gw->initiator_quota; i++) { + struct uf_gw_rsrc *rsrc = + &gw->initiator_rsrcs[i]; + uf_gw_rsrc_init(rsrc, + gw, uf_gw_kick_tras_2, i); + list_add_tail(uf_gw_rsrc_to_link(rsrc), + &gw->initiator_rsrc_list); + } + } + spin_lock_init(&gw->lock); + gw->state = uf_gw_state_i; + INIT_LIST_HEAD(&gw->tra_list); + uf_work_init(&gw->kick_tras_1_work, uf_gw_kick_tras_1, gw); + uf_work_init(&gw->connect_client_1_work, + uf_gw_connect_client_1, gw); + uf_work_init(&gw->disconnect_client_1_work, + uf_gw_disconnect_client_1, gw); + uf_callback_init(&gw->disconnect_client_2_callback, + uf_gw_disconnect_client_2); + gw->kick_tras_out = 0; + gw->initiator_rsrcs_out = 0; + return 0; + exit_path: + if (gw->initiator_quota != 0) + kfree(gw->initiator_rsrcs); + exit_no_initiator_rsrcs: + return return_value; +} + +int +uf_gw_init(struct uf_gw *gw, +struct uf_channel *channel, uf_gw_connect_function *connect, +uf_gw_disconnect_function *disconnect, u32 initiator_quota) +{ + trace(); + gw->channel = channel; + gw->connect = connect; + gw->disconnect = disconnect; + gw->initiator_quota = initiator_quota; + uf_channel_install_client(channel, gw, + uf_gw_channel_connect, + uf_gw_handle_channel_message, + uf_gw_channel_disconnect); + return uf_gw_init_or_exit(gw, 0); +} + +void uf_gw_exit(struct uf_gw *gw) +{ + trace(); + (void)uf_gw_init_or_exit(gw, 1); +} + +static void +uf_gw_handle_stimulus(struct uf_gw *gw, +uf_gw_stimulus stimulus) +{ + switch (gw->state) { + case uf_gw_state_i: + /* Channel disconnected. */ + /* Client disconnected. */ + /* No tras queued. */ + /* Initiator rsrcs idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_cc: + gw->state = uf_gw_state_i_cc; + uf_gw_connect_client(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc: + /* Channel connected. */ + /* Client connecting. */ + /* Maybe tras queued. */ + /* Initiator rsrcs idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + break; + case uf_gw_stimulus_cd: + gw->state = uf_gw_state_i_cc_cd; + break; + case uf_gw_stimulus_lc: + gw->state = uf_gw_state_i_cc_lc; + uf_gw_kick_tras(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_cd: + /* Channel disconnecting. */ + /* Client connecting. */ + /* Maybe tras queued. */ + /* Initiator rsrcs idle. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + break; + case uf_gw_stimulus_lc: + gw->state = uf_gw_state_i_cc_cd_lc; + uf_gw_disconnect_client(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_lc: + /* Channel connected. */ + /* Client connected. */ + /* Maybe tras queued. */ + /* Maybe initiator rsrcs busy. */ + /* Maybe kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + uf_gw_kick_tras(gw); + break; + case uf_gw_stimulus_cd: + gw->state = uf_gw_state_i_cc_lc_cd; + uf_gw_kick_tras(gw); + break; + case uf_gw_stimulus_kt: + break; + case uf_gw_stimulus_ic: + case uf_gw_stimulus_ii: + uf_gw_kick_tras(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_lc_cd: + /* Channel disconnecting. */ + /* Client connected. */ + /* Maybe tras queued. */ + /* Maybe initiator rsrcs busy. */ + /* Kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + break; + case uf_gw_stimulus_kt: + gw->state = uf_gw_state_i_cc_cd_lc; + uf_gw_disconnect_client(gw); + break; + case uf_gw_stimulus_ic: + case uf_gw_stimulus_ii: + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_cd_lc: + /* Channel disconnecting. */ + /* Calling client disconnect. */ + /* Maybe tras queued. */ + /* Maybe initiator rsrcs busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + break; + case uf_gw_stimulus_ic: + case uf_gw_stimulus_ii: + break; + case uf_gw_stimulus_lg: + gw->state = uf_gw_state_i_cc_cd_lc_lg; + uf_gw_fail_out_tras(gw); + uf_gw_abort_initiator_rsrcs(gw); + break; + case uf_gw_stimulus_ld: + gw->state = uf_gw_state_i_cc_cd_lc_ld; + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_cd_lc_lg: + /* Channel disconnecting. */ + /* Client disconnecting. */ + /* No tras queued. */ + /* Maybe initiator rsrcs busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_tq: + break; + case uf_gw_stimulus_ic: + case uf_gw_stimulus_ii: + break; + case uf_gw_stimulus_ld: + gw->state = + uf_gw_state_i_cc_cd_lc_lg_ld; + uf_gw_test_initiator_rsrcs(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_cd_lc_ld: + /* Channel disconnecting. */ + /* Client disconnected but call still in progress. */ + /* No tras queued. */ + /* Maybe initiator rsrcs busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_ic: + case uf_gw_stimulus_ii: + break; + case uf_gw_stimulus_lg: + gw->state = + uf_gw_state_i_cc_cd_lc_lg_ld; + uf_gw_test_initiator_rsrcs(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + case uf_gw_state_i_cc_cd_lc_lg_ld: + /* Channel disconnecting. */ + /* Client disconnected. */ + /* No tras queued. */ + /* Test initiator rsrcs or initiator rsrcs busy. */ + /* Not kicking tras. */ + switch (stimulus) { + case uf_gw_stimulus_ic: + break; + case uf_gw_stimulus_ii: + gw->state = uf_gw_state_i; + uf_gw_complete_channel_disconnect(gw); + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } + break; + default: + uf_gw_invalid_stimulus(gw, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw.h Tue Oct 3 16:29:44 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 UF_GW_H +#define UF_GW_H + +#include "uf_callback.h" +#include "uf_channel.h" + +struct uf_gw_rsrc; + +struct uf_gw_tra { + struct uf_callback callback; + union usbif_parameters parameters; + union usbif_status status; +}; + +static inline struct uf_callback * +uf_gw_tra_to_callback(struct uf_gw_tra *tra) +{ + return &tra->callback; +} + +static inline struct uf_gw_tra * +uf_gw_tra_callback_to(struct uf_callback *callback) +{ + return container_of(callback, struct uf_gw_tra, callback); +} + +static inline struct list_head * +uf_gw_tra_to_link(struct uf_gw_tra *tra) +{ + return uf_callback_to_link(uf_gw_tra_to_callback(tra)); +} + +static inline struct uf_gw_tra * +uf_gw_tra_link_to(struct list_head *link) +{ + return uf_gw_tra_callback_to(uf_callback_link_to(link)); +} + +static inline void uf_gw_tra_init(struct uf_gw_tra *tra, +uf_callback_function *callback) +{ + uf_callback_init(uf_gw_tra_to_callback(tra), callback); +} + +static inline void +uf_gw_tra_complete(struct uf_gw_tra *tra, usbif_error error) +{ + uf_callback_complete(uf_gw_tra_to_callback(tra), error); +} + +static inline +usbif_error uf_gw_tra_query_error(struct uf_gw_tra *tra) +{ + return uf_callback_query_error(uf_gw_tra_to_callback(tra)); +} + +typedef enum { + uf_gw_state_i, + uf_gw_state_i_cc, + uf_gw_state_i_cc_cd, + uf_gw_state_i_cc_lc, + uf_gw_state_i_cc_lc_cd, + uf_gw_state_i_cc_cd_lc, + uf_gw_state_i_cc_cd_lc_lg, + uf_gw_state_i_cc_cd_lc_ld, + uf_gw_state_i_cc_cd_lc_lg_ld +} uf_gw_state; + +struct uf_gw; + +typedef void uf_gw_connect_function(struct uf_gw *gw); + +typedef void uf_gw_disconnect_function(struct uf_gw *gw, +struct uf_callback *callback); + +struct uf_gw { + struct uf_channel *channel; + uf_gw_connect_function *connect; + uf_gw_disconnect_function *disconnect; + u32 initiator_quota; + struct list_head initiator_rsrc_list; + struct uf_gw_rsrc *initiator_rsrcs; + spinlock_t lock; + uf_gw_state state; + struct list_head tra_list; + struct uf_work kick_tras_1_work; + struct uf_work connect_client_1_work; + struct uf_work disconnect_client_1_work; + struct uf_callback disconnect_client_2_callback; + int kick_tras_out; + u32 initiator_rsrcs_out; + struct uf_callback *channel_disconnect_callback; +}; + +int uf_gw_init(struct uf_gw *gw, +struct uf_channel *channel, uf_gw_connect_function *connect, +uf_gw_disconnect_function *disconnect, u32 initiator_quota); + +extern void +uf_gw_submit_tra(struct uf_gw *gw, struct uf_gw_tra *tra); + +extern void uf_gw_exit(struct uf_gw *gw); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw_rsrc.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw_rsrc.c Tue Oct 3 16:29:44 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 "uf_gw_rsrc.h" +#include "uf_trace.h" + +void uf_gw_submit_channel_message(struct uf_gw *gw, +struct uf_channel_obmsg *message); + +static void +uf_gw_rsrc_handle_stimulus(struct uf_gw_rsrc *rsrc, +uf_gw_rsrc_stimulus stimulus); + +void uf_gw_rsrc_start(struct uf_gw_rsrc *rsrc, +struct uf_gw_tra *tra) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + unsigned long flags; + rsrc->tra = tra; + rsrc->error = USBIF_ERROR_SUCCESS; + rsrc->channel_message.request.usbif_parameters = + tra->parameters; + spin_lock_irqsave(&rsrc->lock, flags); + uf_gw_rsrc_handle_stimulus(rsrc, + uf_gw_rsrc_stimulus_st); + spin_unlock_irqrestore(&rsrc->lock, flags); + uf_gw_submit_channel_message(rsrc->gw, + &rsrc->channel_message); +} + +void uf_gw_rsrc_abort(struct uf_gw_rsrc *rsrc, +usbif_error error) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->aborted_error = error; + uf_gw_rsrc_handle_stimulus(rsrc, + uf_gw_rsrc_stimulus_ab); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void uf_gw_rsrc_channel_message_callback( +struct uf_callback *callback) +{ + struct uf_gw_rsrc *rsrc = container_of(callback, + struct uf_gw_rsrc, channel_message.callback); + unsigned long flags; + spin_lock_irqsave(&rsrc->lock, flags); + uf_gw_rsrc_handle_stimulus(rsrc, + uf_gw_rsrc_stimulus_sc); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +int uf_gw_rsrc_handle_response(struct uf_gw_rsrc *rsrc, +struct usbif_response *response) +{ + int return_value = -1; + unsigned long flags; + spin_lock_irqsave(&rsrc->lock, flags); + if ((rsrc->state == uf_gw_rsrc_state_i_st) + ||(rsrc->state == uf_gw_rsrc_state_i_st_sc)) { + rsrc->tra->status = response->usbif_status; + rsrc->error = response->gw_status.error; + uf_gw_rsrc_handle_stimulus(rsrc, + uf_gw_rsrc_stimulus_ts); + return_value = 0; + } + spin_unlock_irqrestore(&rsrc->lock, flags); + return return_value; +} + + +static void +uf_gw_rsrc_invalid_stimulus(struct uf_gw_rsrc *rsrc, +uf_gw_rsrc_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: gw initiator rsrc %p in state %d" + "received invalid stimulus %d", rsrc, rsrc->state, + stimulus); +} + +static void +uf_gw_rsrc_set_aborted(struct uf_gw_rsrc *rsrc) +{ + trace(); + rsrc->error = rsrc->aborted_error; +} + +static void +uf_gw_rsrc_complete(struct uf_gw_rsrc *rsrc) +{ + uf_callback_complete(&rsrc->tra->callback, rsrc->error); + uf_callback_success(&rsrc->callback); +} + +void uf_gw_rsrc_init(struct uf_gw_rsrc *rsrc, +struct uf_gw *gw, uf_callback_function callback, int id) +{ + trace(); + uf_callback_init(&rsrc->callback, callback); + rsrc->gw = gw; + rsrc->channel_message.request.gw_parameters.id = id; + spin_lock_init(&rsrc->lock); + rsrc->state = uf_gw_rsrc_state_i; + uf_callback_init(uf_channel_obmsg_to_callback( + &rsrc->channel_message), + uf_gw_rsrc_channel_message_callback); +} + +static void +uf_gw_rsrc_handle_stimulus(struct uf_gw_rsrc *rsrc, +uf_gw_rsrc_stimulus stimulus) +{ + switch (rsrc->state) { + case uf_gw_rsrc_state_i: + switch (stimulus) { + case uf_gw_rsrc_stimulus_st: + rsrc->state = uf_gw_rsrc_state_i_st; + break; + case uf_gw_rsrc_stimulus_ab: + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, + stimulus); + break; + } + break; + case uf_gw_rsrc_state_i_st: + switch (stimulus) { + case uf_gw_rsrc_stimulus_ab: + rsrc->state = uf_gw_rsrc_state_i_st_ab; + break; + case uf_gw_rsrc_stimulus_sc: + rsrc->state = uf_gw_rsrc_state_i_st_sc; + break; + case uf_gw_rsrc_stimulus_ts: + rsrc->state = uf_gw_rsrc_state_i_st_ts; + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, + stimulus); + break; + } + break; + case uf_gw_rsrc_state_i_st_ab: + switch (stimulus) { + case uf_gw_rsrc_stimulus_sc: + rsrc->state = uf_gw_rsrc_state_i; + uf_gw_rsrc_set_aborted(rsrc); + uf_gw_rsrc_complete(rsrc); + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, + stimulus); + break; + } + break; + case uf_gw_rsrc_state_i_st_sc: + switch (stimulus) { + case uf_gw_rsrc_stimulus_ab: + rsrc->state = uf_gw_rsrc_state_i; + uf_gw_rsrc_set_aborted(rsrc); + uf_gw_rsrc_complete(rsrc); + break; + case uf_gw_rsrc_stimulus_ts: + rsrc->state = uf_gw_rsrc_state_i; + uf_gw_rsrc_complete(rsrc); + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, + stimulus); + break; + } + break; + case uf_gw_rsrc_state_i_st_ts: + switch (stimulus) { + case uf_gw_rsrc_stimulus_ab: + break; + case uf_gw_rsrc_stimulus_sc: + rsrc->state = uf_gw_rsrc_state_i; + uf_gw_rsrc_complete(rsrc); + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, + stimulus); + break; + } + break; + default: + uf_gw_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw_rsrc.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_gw_rsrc.h Tue Oct 3 16:29:44 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 UF_GW_RSRC_H +#define UF_GW_RSRC_H + +#include +#include +#include "uf_channel.h" +#include "uf_gw.h" + +typedef enum { + uf_gw_rsrc_state_i, + uf_gw_rsrc_state_i_st, + uf_gw_rsrc_state_i_st_ab, + uf_gw_rsrc_state_i_st_sc, + uf_gw_rsrc_state_i_st_ts +} uf_gw_rsrc_state; + +typedef enum { + uf_gw_rsrc_stimulus_st, + uf_gw_rsrc_stimulus_ab, + uf_gw_rsrc_stimulus_sc, + uf_gw_rsrc_stimulus_ts, +} uf_gw_rsrc_stimulus; + +struct uf_gw_rsrc { + struct uf_callback callback; + struct uf_gw *gw; + spinlock_t lock; + uf_gw_rsrc_state state; + struct uf_gw_tra *tra; + usbif_error aborted_error; + usbif_error error; + struct uf_channel_obmsg channel_message; +}; + +static inline struct list_head * +uf_gw_rsrc_to_link(struct uf_gw_rsrc *rsrc) +{ + return &rsrc->callback.work.link; +} + +static inline struct uf_gw_rsrc * +uf_gw_rsrc_callback_to(struct uf_callback *callback) +{ + return container_of(callback, struct uf_gw_rsrc, callback); +} + +static inline struct uf_gw * +uf_gw_rsrc_query_gw(struct uf_gw_rsrc *rsrc) +{ + return rsrc->gw; +} + +void uf_gw_rsrc_init(struct uf_gw_rsrc *rsrc, +struct uf_gw *gw, uf_callback_function callback, int id); + +void uf_gw_rsrc_start(struct uf_gw_rsrc *rsrc, +struct uf_gw_tra *tra); + +void uf_gw_rsrc_abort(struct uf_gw_rsrc *rsrc, +usbif_error error); + +int uf_gw_rsrc_handle_response(struct uf_gw_rsrc *rsrc, +struct usbif_response *response); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_module.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_module.c Tue Oct 3 16:29:44 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 "uf_device.h" +#include "uf_driver.h" +#include "uf_trace.h" + +static int uf_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 = uf_driver_class_init()) != 0) + goto exit_no_driver_class; + if ((return_value = uf_device_class_init()) != 0) + goto exit_no_device_class; + return 0; + exit_path: + uf_device_class_exit(); + exit_no_device_class: + uf_driver_class_exit(); + exit_no_driver_class: + exit_no_usb: + return return_value; +} + +static int __init uf_module_init(void) +{ + trace(); + return uf_module_init_or_exit(0); +} + +static void __exit uf_module_exit(void) +{ + trace(); + (void)uf_module_init_or_exit(1); +} + +module_init(uf_module_init); +module_exit(uf_module_exit); + +MODULE_LICENSE("GPL"); diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rbr_provider_pool.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rbr_provider_pool.c Tue Oct 3 16:29:44 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 "uf_rbr_provider_pool.h" +#include "uf_trace.h" + +static UF_CALLBACK_SERIALISER(callback_serialiser); + +struct uf_rbr_provider_pool { + struct uf_buffer_rsrc_list dedicated_rsrcs; + struct uf_buffer_rsrc_provider *provider; + spinlock_t lock; + struct uf_buffer_rsrc_list head_request_req; + struct list_head request_list; + int kicking_requests; + int head_request_req_calculated; +}; + +static int +uf_rbr_provider_pool_init_or_exit( +struct uf_rbr_provider_pool *pool, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + pool->provider = uf_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_req_calculated = 0; + return 0; + exit_path: + uf_free_buffer_rsrc_provider(pool->provider); + exit_no_provider: + return return_value; +} + +struct uf_rbr_provider_pool *uf_rbr_provider_pool_allocate(void) +{ + struct uf_rbr_provider_pool *pool; + trace(); + pool = (struct uf_rbr_provider_pool *)kmalloc(sizeof( + struct uf_rbr_provider_pool), GFP_KERNEL); + if (pool != NULL) { + pool->dedicated_rsrcs.grant_references = USBIF_QUOTA * + USBIF_GRANT_TABLE_REFERENCE_COUNT; + if (uf_rbr_provider_pool_init_or_exit(pool, 0) != 0) { + kfree(pool); + pool = NULL; + } + } + return pool; +} + +void +uf_rbr_provider_pool_free(struct uf_rbr_provider_pool *pool) +{ + trace(); + (void)uf_rbr_provider_pool_init_or_exit(pool, 1); + kfree(pool); +} + +static void +uf_rbr_provider_pool_kick_requests( +struct uf_rbr_provider_pool *pool); + +void +uf_rbr_provider_pool_reserve_and_create_rbrs( +struct uf_rbr_provider_pool *pool, +struct uf_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(uf_rbr_provider_request_to_link(request), + &pool->request_list); + spin_unlock_irqrestore(&pool->lock, flags); + uf_rbr_provider_pool_kick_requests(pool); +} + +static int +uf_rbr_provider_pool_calculate_rbr_rsrcs(void *buffer, +usbif_buffer_byte_count length, struct uf_buffer_rsrc_list *list) +{ + unsigned long first_page, final_page; + trace(); + *list = uf_buffer_rsrc_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 uf_rbr_provider_pool_calc_req( +struct uf_rbr_provider_request *request, +struct uf_buffer_rsrc_list *list) +{ + struct uf_create_rbr_request_element *element; + trace(); + *list = uf_buffer_rsrc_list_null(); + list_for_each_entry(element, &request->request_elements, link) { + struct uf_buffer_rsrc_list element_list; + if (uf_rbr_provider_pool_calculate_rbr_rsrcs( + element->buffer, element->length, &element_list) != 0) + return 1; + uf_buffer_rsrc_list_plus_equals(list, &element_list); + } + return 0; +} + +static void +uf_rbr_provider_pool_create_rbr(void *buffer, +usbif_buffer_byte_count length, domid_t domain, +struct uf_buffer_rsrc_provider *provider, struct usbif_rbr *rbr, +int access_flags, struct uf_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] = + uf_buffer_rsrc_provider_allocate_grant_reference( + provider); + gnttab_grant_foreign_access_ref(context->reference[i], + context->domain_id, virt_to_mfn(page), ((access_flags & + UF_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 +uf_rbr_provider_pool_service_request( +struct uf_rbr_provider_pool *pool, +struct uf_rbr_provider_request *request) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct uf_create_rbr_request_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + uf_rbr_provider_pool_create_rbr( + element->buffer, element->length, + request->domain, pool->provider, &element->rbr, + element->access_flags, &element->context); + element->created = 1; + } + uf_callback_serialiser_complete_callback(&callback_serialiser, + uf_rbr_provider_request_to_create_callback(request), + USBIF_ERROR_SUCCESS); +} + +static void uf_rbr_provider_pool_kick_requests( +struct uf_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 uf_buffer_rsrc_list free_rsrcs; + struct uf_rbr_provider_request *request = + uf_rbr_provider_request_link_to( + pool->request_list.next); + if (pool->head_request_req_calculated) + goto skip_calc; + if ((uf_rbr_provider_pool_calc_req(request, + &pool->head_request_req) != 0) || + !uf_buffer_rsrc_list_subset_of( + &pool->head_request_req, + &pool->dedicated_rsrcs)) { + list_del_init(uf_rbr_provider_request_to_link( + request)); + uf_callback_serialiser_complete_callback( + &callback_serialiser, + uf_rbr_provider_request_to_create_callback( + request), + USBIF_ERROR_TOO_BIG); + continue; + } + pool->head_request_req_calculated = 1; + skip_calc: + free_rsrcs = + uf_buffer_rsrc_provider_query_free_rsrcs( + pool->provider); + if (!uf_buffer_rsrc_list_subset_of( + &pool->head_request_req, &free_rsrcs)) + break; + list_del_init(uf_rbr_provider_request_to_link(request)); + pool->head_request_req_calculated = 0; + pool->kicking_requests = 1; + spin_unlock_irqrestore(&pool->lock, flags); + uf_rbr_provider_pool_service_request(pool, request); + spin_lock_irqsave(&pool->lock, flags); + pool->kicking_requests = 0; + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +void +uf_rbr_provider_pool_abort_reserve_and_create_rbrs( +struct uf_rbr_provider_request *request, usbif_error error) +{ + struct uf_rbr_provider_pool *pool = request->pool; + unsigned long flags; + struct uf_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(uf_rbr_provider_request_to_link( + request)); + pool->head_request_req_calculated = 0; + uf_callback_serialiser_complete_callback( + &callback_serialiser, + uf_rbr_provider_request_to_create_callback( + request), error); + break; + } + } + spin_unlock_irqrestore(&pool->lock, flags); +} + +static void +uf_rbr_provider_pool_retry_revoke_rbr( +struct uf_rbr_context *context) +{ + int i = 0,j; + trace(); + while (i != context->count) { + if (gnttab_end_foreign_access_ref(context->reference[i], 0)) { + uf_buffer_rsrc_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) { + uf_callback_success(context->callback); + } else { + printk(KERN_WARNING + "uf 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)) + uf_rbr_provider_pool_retry_revoke_rbr; + add_timer(&context->timer); + } +} + +static void +uf_rbr_provider_pool_revoke_rbr(struct uf_rbr_context *context, +struct uf_callback *callback) +{ + trace(); + context->callback = callback; + uf_rbr_provider_pool_retry_revoke_rbr(context); +} + +static void +uf_rbr_provider_pool_revoke_and_unreserve_rbrs_1( +struct uf_callback *callback) +{ + struct uf_rbr_provider_request *request = container_of(callback, + struct uf_rbr_provider_request, + reserved_callback); + struct uf_create_rbr_request_element *element; + trace(); + list_for_each_entry(element, &request->request_elements, link) { + if (element->created) { + element->created = 0; + uf_rbr_provider_pool_revoke_rbr( + &element->context, callback); + return; + } + } + uf_rbr_provider_pool_kick_requests(request->pool); + uf_callback_serialiser_complete_callback(&callback_serialiser, + uf_rbr_provider_request_to_revoke_callback(request), + USBIF_ERROR_SUCCESS); +} + +void uf_rbr_provider_pool_revoke_and_unreserve_rbrs( +struct uf_rbr_provider_request *request) +{ + trace(); + uf_callback_init(&request->reserved_callback, + uf_rbr_provider_pool_revoke_and_unreserve_rbrs_1); + uf_rbr_provider_pool_revoke_and_unreserve_rbrs_1( + &request->reserved_callback); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rbr_provider_pool.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rbr_provider_pool.h Tue Oct 3 16:29:44 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 UF_RBR_PROVIDER_POOL +#define UF_RBR_PROVIDER_POOL + +#include +#include "uf_buffer_rsrc_provider.h" +#include "uf_callback.h" + +struct uf_rbr_provider_pool; + +struct uf_rbr_provider_pool *uf_rbr_provider_pool_allocate(void); + +void uf_rbr_provider_pool_free(struct uf_rbr_provider_pool *pool); + +#define UF_ACCESS_FLAGS_READ 1 +#define UF_ACCESS_FLAGS_WRITE 2 + +struct uf_rbr_context { + struct uf_buffer_rsrc_provider *provider; + struct timer_list timer; + struct uf_callback *callback; + int domain_id; + int count; + grant_ref_t reference[USBIF_GRANT_TABLE_REFERENCE_COUNT]; +}; + +struct uf_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 uf_rbr_context context; +}; + +static inline void uf_create_rbr_request_element_init( +struct uf_create_rbr_request_element *element) +{ + memset(element, 0, sizeof(*element)); + INIT_LIST_HEAD(&element->link); +} + +static inline void +uf_create_rbr_request_element_set_buffer( +struct uf_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 +uf_create_rbr_request_element_query_rbr( +struct uf_create_rbr_request_element *element) +{ + return element->rbr; +} + +static inline void +uf_create_rbr_request_element_ensure_removed( +struct uf_create_rbr_request_element *element) +{ + list_del_init(&element->link); +} + +struct uf_rbr_provider_request { + struct uf_callback create_callback; + struct uf_callback revoke_callback; + struct uf_rbr_provider_pool *pool; + domid_t domain; + struct list_head request_elements; + struct uf_callback reserved_callback; +}; + +static inline struct uf_callback * +uf_rbr_provider_request_to_create_callback( +struct uf_rbr_provider_request *request) +{ + return &request->create_callback; +} + +static inline struct uf_rbr_provider_request * +uf_rbr_provider_request_create_callback_to( +struct uf_callback * callback) +{ + return container_of(callback, struct uf_rbr_provider_request, + create_callback); +} + +static inline struct uf_callback * +uf_rbr_provider_request_to_revoke_callback( +struct uf_rbr_provider_request *request) +{ + return &request->revoke_callback; +} + +static inline struct uf_rbr_provider_request * +uf_rbr_provider_request_revoke_callback_to( +struct uf_callback * callback) +{ + return container_of(callback, struct uf_rbr_provider_request, + revoke_callback); +} + +static inline struct list_head * uf_rbr_provider_request_to_link( +struct uf_rbr_provider_request *request) +{ + return uf_callback_to_link( + uf_rbr_provider_request_to_create_callback(request)); +} + +static inline struct uf_rbr_provider_request * +uf_rbr_provider_request_link_to(struct list_head *link) +{ + return uf_rbr_provider_request_create_callback_to( + uf_callback_link_to(link)); +} + +static inline void uf_rbr_provider_request_init( +struct uf_rbr_provider_request *request, +uf_callback_function *create_callback, +uf_callback_function *revoke_callback) +{ + uf_callback_init(&request->create_callback, create_callback); + uf_callback_init(&request->revoke_callback, revoke_callback); + INIT_LIST_HEAD(&request->request_elements); +} + +static inline void uf_rbr_provider_request_add_element( +struct uf_rbr_provider_request *request, +struct uf_create_rbr_request_element *element) +{ + list_add(&element->link, &request->request_elements); +} + +void +uf_rbr_provider_pool_reserve_and_create_rbrs( +struct uf_rbr_provider_pool *pool, +struct uf_rbr_provider_request *request, domid_t domain); + +void +uf_rbr_provider_pool_abort_reserve_and_create_rbrs( +struct uf_rbr_provider_request *request, usbif_error error); + +void +uf_rbr_provider_pool_revoke_and_unreserve_rbrs( +struct uf_rbr_provider_request *request); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ring_channel.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ring_channel.c Tue Oct 3 16:29:44 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 "uf_ring_channel.h" +#include "uf_trace.h" + +static inline void +uf_ring_channel_rsrc_init(struct uf_ring_channel_rsrc *rsrc, +struct uf_ring_channel *channel, uf_callback_function *callback) +{ + trace(); + uf_channel_ibmsg_init(&rsrc->message, callback); + rsrc->channel = channel; +} + +static inline struct list_head * +uf_ring_channel_rsrc_to_link(struct uf_ring_channel_rsrc *rsrc) +{ + return uf_channel_ibmsg_to_link(&rsrc->message); +} + +static inline struct uf_ring_channel_rsrc * +uf_ring_channel_rsrc_callback_to(struct uf_callback *callback) +{ + return container_of(uf_channel_ibmsg_callback_to(callback), + struct uf_ring_channel_rsrc, message); +} + +typedef enum { + uf_ring_channel_stimulus_cn,/* connect request */ + uf_ring_channel_stimulus_mq,/* message queued */ + uf_ring_channel_stimulus_dn,/* disconnect request */ + uf_ring_channel_stimulus_ri,/* ring interrupt */ + uf_ring_channel_stimulus_cs,/* connect successful */ + uf_ring_channel_stimulus_cf,/* connect failed */ + uf_ring_channel_stimulus_cc,/* connect client completed */ + uf_ring_channel_stimulus_dc,/* disconnect client completed */ + uf_ring_channel_stimulus_ds,/* disconnect successful */ + uf_ring_channel_stimulus_ks,/* kick send ring completed */ + uf_ring_channel_stimulus_kr,/* kick recv ring completed */ + uf_ring_channel_stimulus_rc,/* rsrc completed */ + uf_ring_channel_stimulus_rf /* rsrc final completion */ +} uf_ring_channel_stimulus; + +static void +uf_ring_channel_handle_stimulus(struct uf_ring_channel *channel, +uf_ring_channel_stimulus stimulus); + +void +uf_ring_channel_connect(struct uf_ring_channel *channel, +struct uf_ring_channel_connect_request *request) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = &request->callback; + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_submit_message(struct uf_channel *base_channel, +struct uf_channel_obmsg *message) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct uf_ring_channel *channel = + uf_ring_channel_channel_to(base_channel); + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(uf_channel_obmsg_to_link(message), + &channel->message_list); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_mq); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void +uf_ring_channel_disconnect(struct uf_ring_channel *channel, +struct uf_callback *callback) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->current_callback = callback; + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static irqreturn_t +uf_ring_channel_interrupt(int irq, void *context, struct pt_regs *ptregs) +{ + struct uf_ring_channel *channel = context; + unsigned long flags; + spin_lock_irqsave(&channel->lock, flags); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_ri); + spin_unlock_irqrestore(&channel->lock, flags); + return IRQ_HANDLED; +} + +static void +uf_ring_channel_invalid_stimulus(struct uf_ring_channel *channel, +uf_ring_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: channel %p in state %d" + "received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void +uf_ring_channel_do_connect(struct uf_ring_channel *channel) +{ + trace(); + (void)uf_work_schedule(&channel->do_connect_1_work); +} + +static void uf_ring_channel_do_connect_1(void *data) +{ + struct uf_ring_channel *channel = data; + struct uf_ring_channel_connect_request *request = + uf_ring_channel_connect_request_callback_to( + channel->current_callback); + struct evtchn_alloc_unbound alloc_unbound = { + .dom = DOMID_SELF, + .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(EVTCHNOP_alloc_unbound, + &alloc_unbound) != 0); + channel->event_channel = alloc_unbound.port; + error = bind_evtchn_to_irqhandler(channel->event_channel, + uf_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); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_cs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_connect_client(struct uf_ring_channel *channel) +{ + trace(); + (void)uf_work_schedule(&channel->connect_client_1_work); +} + +static void uf_ring_channel_connect_client_1(void *data) +{ + struct uf_ring_channel *channel = data; + unsigned long flags; + trace(); + uf_channel_connect(&channel->channel); + spin_lock_irqsave(&channel->lock, flags); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_cc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_disconnect_client(struct uf_ring_channel *channel) +{ + trace(); + (void)uf_work_schedule(&channel->disconnect_client_1_work); +} + +static void uf_ring_channel_disconnect_client_1(void *data) +{ + struct uf_ring_channel *channel = data; + trace(); + uf_channel_disconnect(&channel->channel, + &channel->disconnect_client_2_callback); +} + +static void +uf_ring_channel_disconnect_client_2(struct uf_callback *callback) +{ + struct uf_ring_channel *channel = container_of(callback, + struct uf_ring_channel, disconnect_client_2_callback); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_dc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_do_disconnect(struct uf_ring_channel *channel) +{ + trace(); + (void)uf_work_schedule(&channel->do_disconnect_1_work); +} + +static void uf_ring_channel_do_disconnect_1(void *data) +{ + struct uf_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); + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_ds); + spin_unlock_irqrestore(&channel->lock, flags); + } else { + printk(KERN_WARNING + "uf_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)) + uf_ring_channel_do_disconnect; + add_timer(&channel->timer); + } +} + +static void +uf_ring_channel_kick_send_ring(struct uf_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)uf_work_schedule(&channel->kick_send_ring_1_work); + } +} + +static void uf_ring_channel_kick_send_ring_1(void *data) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct uf_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 uf_channel_obmsg *message; + struct usbif_request *request; + message = list_entry(channel->message_list.next, + struct uf_channel_obmsg, + callback.work.link); + list_del_init(uf_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; + uf_callback_success( + uf_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; + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_ks); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_kick_recv_ring(struct uf_ring_channel *channel) +{ + if (!channel->recv_ring_kick_out) { + channel->recv_ring_kick_out = 1; + (void)uf_work_schedule(&channel->kick_recv_ring_1_work); + } +} + +static void uf_ring_channel_kick_recv_ring_1(void *data) +{ + struct uf_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 uf_ring_channel_rsrc *rsrc; + struct usbif_response *response; + rsrc = list_entry(channel->rsrc_list.next, + struct uf_ring_channel_rsrc, + message.callback.work.link); + list_del_init(uf_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); + uf_channel_handle_message(&channel->channel, + &rsrc->message); + progress = 1; + spin_lock_irqsave(&channel->lock, flags); + } + if (!progress) + { + channel->recv_ring_kick_out = 0; + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_kr); + break; + } + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_kick_recv_ring_2(struct uf_callback *callback) +{ + struct uf_ring_channel_rsrc *rsrc = + uf_ring_channel_rsrc_callback_to(callback); + struct uf_ring_channel *channel = rsrc->channel; + unsigned long flags; + if ((uf_callback_query_error(callback) != USBIF_ERROR_SUCCESS) + && (channel->protocol_error != NULL)) + channel->protocol_error(channel); + spin_lock_irqsave(&channel->lock, flags); + list_add_tail(uf_ring_channel_rsrc_to_link(rsrc), + &channel->rsrc_list); + if (--channel->rsrcs_out == 0) { + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_rf); + } else { + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_rc); + } + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void +uf_ring_channel_complete_current_callback( +struct uf_ring_channel *channel) +{ + trace(); + uf_callback_success(channel->current_callback); +} + +static void +uf_ring_channel_fail_current_callback( +struct uf_ring_channel *channel) +{ + trace(); + uf_callback_complete(channel->current_callback, + USBIF_ERROR_FAILURE); +} + +static void +uf_ring_channel_fail_out_messages(struct uf_ring_channel *channel) +{ + trace(); + while (!list_empty(&channel->message_list)) { + struct uf_channel_obmsg *message = list_entry( + channel->message_list.next, + struct uf_channel_obmsg, + callback.work.link); + list_del_init(uf_channel_obmsg_to_link(message)); + uf_callback_success(uf_channel_obmsg_to_callback( + message)); + } +} + +static void +uf_ring_channel_test_rsrcs(struct uf_ring_channel *channel) +{ + trace(); + if (channel->rsrcs_out == 0) { + uf_ring_channel_handle_stimulus(channel, + uf_ring_channel_stimulus_rf); + } +} + +static int +uf_ring_channel_init_or_exit(struct uf_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 = uf_ring_channel_state_i; + INIT_LIST_HEAD(&channel->message_list); + uf_work_init(&channel->do_connect_1_work, + uf_ring_channel_do_connect_1, channel); + uf_work_init(&channel->connect_client_1_work, + uf_ring_channel_connect_client_1, channel); + uf_work_init(&channel->disconnect_client_1_work, + uf_ring_channel_disconnect_client_1, channel); + uf_callback_init(&channel->disconnect_client_2_callback, + uf_ring_channel_disconnect_client_2); + uf_work_init(&channel->do_disconnect_1_work, + uf_ring_channel_do_disconnect_1, channel); + uf_work_init(&channel->kick_send_ring_1_work, + uf_ring_channel_kick_send_ring_1, channel); + uf_work_init(&channel->kick_recv_ring_1_work, + uf_ring_channel_kick_recv_ring_1, channel); + INIT_LIST_HEAD(&channel->rsrc_list); + for (i = 0; i < USBIF_QUOTA; i++) { + struct uf_ring_channel_rsrc *rsrc = &channel->rsrcs[i]; + uf_ring_channel_rsrc_init(rsrc, channel, + uf_ring_channel_kick_recv_ring_2); + list_add_tail(uf_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 +uf_ring_channel_init(struct uf_ring_channel *channel, +void (*protocol_error) (struct uf_ring_channel *channel)) +{ + trace(); + uf_channel_init(&channel->channel, + uf_ring_channel_submit_message); + channel->protocol_error = protocol_error; + return uf_ring_channel_init_or_exit(channel, 0); +} + +void uf_ring_channel_exit(struct uf_ring_channel *channel) +{ + trace(); + (void)uf_ring_channel_init_or_exit(channel, 1); +} + +static void uf_ring_channel_handle_stimulus( +struct uf_ring_channel *channel, +uf_ring_channel_stimulus stimulus) +{ + switch (channel->state) { + case uf_ring_channel_state_i: + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target rsrcs idle. */ + switch (stimulus) { + case uf_ring_channel_stimulus_cn: + channel->state = uf_ring_channel_state_i_cn; + uf_ring_channel_do_connect(channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_cs: + channel->state = uf_ring_channel_state_i_cn_cs; + uf_ring_channel_connect_client(channel); + break; + case uf_ring_channel_stimulus_cf: + channel->state = uf_ring_channel_state_i; + uf_ring_channel_fail_current_callback(channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_mq: + case uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_cc: + channel->state = + uf_ring_channel_state_i_cn_cs_cc; + uf_ring_channel_complete_current_callback( + channel); + uf_ring_channel_kick_send_ring(channel); + uf_ring_channel_kick_recv_ring(channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_mq: + uf_ring_channel_kick_send_ring(channel); + break; + case uf_ring_channel_stimulus_dn: + channel->state = + uf_ring_channel_state_i_cn_cs_cc_dn; + uf_ring_channel_kick_send_ring(channel); + uf_ring_channel_kick_recv_ring(channel); + break; + case uf_ring_channel_stimulus_ri: + uf_ring_channel_kick_recv_ring(channel); + break; + case uf_ring_channel_stimulus_ks: + case uf_ring_channel_stimulus_kr: + break; + case uf_ring_channel_stimulus_rc: + case uf_ring_channel_stimulus_rf: + uf_ring_channel_kick_recv_ring(channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_mq: + case uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_ks: + case uf_ring_channel_stimulus_kr: + channel->state = + uf_ring_channel_state_i_cn_cs_cc_dn_ks; + break; + case uf_ring_channel_stimulus_rc: + case uf_ring_channel_stimulus_rf: + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_mq: + case uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_ks: + case uf_ring_channel_stimulus_kr: + channel->state = + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr; + uf_ring_channel_disconnect_client(channel); + uf_ring_channel_fail_out_messages(channel); + break; + case uf_ring_channel_stimulus_rc: + case uf_ring_channel_stimulus_rf: + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_mq: + uf_ring_channel_fail_out_messages(channel); + break; + case uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_dc: + channel->state = + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc; + uf_ring_channel_test_rsrcs(channel); + break; + case uf_ring_channel_stimulus_rc: + case uf_ring_channel_stimulus_rf: + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_ri: + case uf_ring_channel_stimulus_rc: + break; + case uf_ring_channel_stimulus_rf: + channel->state = + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf; + uf_ring_channel_do_disconnect(channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + case uf_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 uf_ring_channel_stimulus_ri: + break; + case uf_ring_channel_stimulus_ds: + channel->state = uf_ring_channel_state_i; + uf_ring_channel_complete_current_callback( + channel); + break; + default: + uf_ring_channel_invalid_stimulus(channel, + stimulus); + break; + } + break; + default: + uf_ring_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ring_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_ring_channel.h Tue Oct 3 16:29:44 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 UF_RING_CHANNEL_H +#define UF_RING_CHANNEL_H + +#include +#include +#include "uf_channel.h" + +struct uf_ring_channel; + +struct uf_ring_channel_rsrc { + struct uf_channel_ibmsg message; + struct uf_ring_channel *channel; +}; + +typedef enum { + uf_ring_channel_state_i, + uf_ring_channel_state_i_cn, + uf_ring_channel_state_i_cn_cs, + uf_ring_channel_state_i_cn_cs_cc, + uf_ring_channel_state_i_cn_cs_cc_dn, + uf_ring_channel_state_i_cn_cs_cc_dn_ks, + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr, + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc, + uf_ring_channel_state_i_cn_cs_cc_dn_ks_kr_dc_rf +} uf_ring_channel_state; + +struct uf_ring_channel { + struct uf_channel channel; + void (*protocol_error) (struct uf_ring_channel *channel); + void *ring; + grant_ref_t grant_ref_pool; + spinlock_t lock; + uf_ring_channel_state state; + struct list_head message_list; + struct uf_work do_connect_1_work; + struct uf_work connect_client_1_work; + struct uf_work disconnect_client_1_work; + struct uf_callback disconnect_client_2_callback; + struct uf_work do_disconnect_1_work; + struct uf_work kick_send_ring_1_work; + struct uf_work kick_recv_ring_1_work; + struct list_head rsrc_list; + struct uf_ring_channel_rsrc rsrcs[USBIF_QUOTA]; + int send_ring_kick_out; + int recv_ring_kick_out; + int rsrcs_out; + struct uf_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 uf_channel * +uf_ring_channel_to_channel(struct uf_ring_channel *channel) +{ + return &channel->channel; +} + +static inline struct uf_ring_channel * +uf_ring_channel_channel_to(struct uf_channel *channel) +{ + return container_of(channel, struct uf_ring_channel, channel); +} + +int uf_ring_channel_init(struct uf_ring_channel *channel, +void (*protocol_error) (struct uf_ring_channel *channel)); + +struct uf_ring_channel_connect_request { + struct uf_callback callback; + domid_t domain_id; + grant_ref_t ring_ref; + unsigned int event_channel; +}; + +static inline struct uf_ring_channel_connect_request * +uf_ring_channel_connect_request_callback_to( +struct uf_callback *callback) +{ + return container_of(callback, + struct uf_ring_channel_connect_request, callback); +} + +void uf_ring_channel_connect(struct uf_ring_channel *channel, +struct uf_ring_channel_connect_request *request); + +void +uf_ring_channel_disconnect(struct uf_ring_channel *channel, +struct uf_callback *callback); + +void uf_ring_channel_exit(struct uf_ring_channel *channel); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rsrc.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rsrc.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,597 @@ +/*****************************************************************************/ +/* 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 "uf_driver.h" +#include "uf_ep.h" +#include "uf_rsrc.h" +#include "uf_rbr_provider_pool.h" +#include "uf_trace.h" + +struct uf_rbr_provider_pool *rbr_provider_pool; + +typedef enum { + uf_rsrc_stimulus_st, /* Start URB */ + uf_rsrc_stimulus_dq, /* Dequeue URB */ + uf_rsrc_stimulus_cs, /* Create RBRs success */ + uf_rsrc_stimulus_cf, /* Create RBRs failure */ + uf_rsrc_stimulus_tc, /* Transaction completed (success/failure) */ + uf_rsrc_stimulus_tu, /* Transaction completed (BE unlinked) */ + uf_rsrc_stimulus_rc, /* Revoke RBRs complete */ +} uf_rsrc_stimulus; + +static void +uf_rsrc_handle_stimulus(struct uf_rsrc *rsrc, uf_rsrc_stimulus stimulus); + +void uf_rsrc_start_urb(struct uf_rsrc *rsrc, struct urb *urb, int clear_stall) +{ + unsigned long flags; + trace(); + urb->hcpriv = rsrc; + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->urb = urb; + rsrc->clear_stall = clear_stall; + rsrc->stalled = 0; + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_st); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +void uf_rsrc_restart_urb(struct uf_rsrc *rsrc, int clear_stall) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + rsrc->clear_stall = clear_stall; + rsrc->stalled = 0; + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_st); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static DEFINE_RWLOCK(dequeue_lock); + +int uf_rsrc_urb_dequeue(struct urb *urb) +{ + int result = 0; + unsigned long flags; + trace(); + write_lock_irqsave(&dequeue_lock, flags); + if (urb->hcpriv != NULL) { + struct uf_rsrc *rsrc = (struct uf_rsrc *)urb->hcpriv; + unsigned long flags2; + spin_lock_irqsave(&rsrc->lock, flags2); + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_dq); + spin_unlock_irqrestore(&rsrc->lock, flags2); + result = -EINPROGRESS; + } + write_unlock_irqrestore(&dequeue_lock, flags); + return result; +} + +static void +uf_rsrc_invalid_stimulus(struct uf_rsrc *rsrc, uf_rsrc_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: hcd rsrc %p in state %d" + "received invalid stimulus %d", + rsrc, rsrc->state, stimulus); +} + +static void uf_rsrc_create_rbrs(struct uf_rsrc *rsrc) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + trace(); + uf_create_rbr_request_element_ensure_removed( + &rsrc->rbr_request_element[0]); + uf_create_rbr_request_element_ensure_removed( + &rsrc->rbr_request_element[1]); + uf_create_rbr_request_element_set_buffer( + &rsrc->rbr_request_element[0], + rsrc->urb->transfer_buffer, + rsrc->urb->transfer_buffer_length, + usb_pipein(rsrc->urb->pipe) ? + UF_ACCESS_FLAGS_WRITE : + UF_ACCESS_FLAGS_READ); + uf_rbr_provider_request_add_element(&rsrc->rbr_request, + &rsrc->rbr_request_element[0]); + if (usb_pipetype(rsrc->urb->pipe) == PIPE_ISOCHRONOUS) { + struct usbif_isochronous_io_schedule_element *element; + int i; + if (rsrc->urb->number_of_packets > (PAGE_SIZE / + sizeof(struct usbif_isochronous_io_schedule_element))) + goto schedule_too_big; + element = rsrc->schedule; + for (i = 0; i < rsrc->urb->number_of_packets; i++) { + memset(&element[i], 0, sizeof(element[i])); + element[i].offset = rsrc->urb->iso_frame_desc[i]. + offset; + element[i].length = rsrc->urb->iso_frame_desc[i]. + length; + } + uf_create_rbr_request_element_set_buffer( + &rsrc->rbr_request_element[1], + rsrc->schedule, + sizeof(struct usbif_isochronous_io_schedule_element) * + rsrc->urb->number_of_packets, + UF_ACCESS_FLAGS_WRITE | UF_ACCESS_FLAGS_READ); + uf_rbr_provider_request_add_element( + &rsrc->rbr_request, + &rsrc->rbr_request_element[1]); + } + uf_rbr_provider_pool_reserve_and_create_rbrs(rbr_provider_pool, + &rsrc->rbr_request, + uf_device_query_domain(uf_ep_query_device(rsrc->ep))); + return; + schedule_too_big: + uf_callback_complete( + uf_rbr_provider_request_to_create_callback( + &rsrc->rbr_request), USBIF_ERROR_TOO_BIG); +} + +static void uf_rsrc_create_rbrs_1(struct uf_callback * callback) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct uf_rsrc *rsrc = container_of( + uf_rbr_provider_request_create_callback_to(callback), + struct uf_rsrc, rbr_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + if (uf_callback_query_error(callback) == USBIF_ERROR_SUCCESS) { + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_cs); + } else { + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_cf); + } + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void uf_rsrc_abort_create_rbrs(struct uf_rsrc *rsrc) +{ + trace(); + uf_rbr_provider_pool_abort_reserve_and_create_rbrs( + &rsrc->rbr_request, USBIF_ERROR_UNLINKED); +} + +static void uf_rsrc_set_unlinked_error(struct uf_rsrc *rsrc) +{ + trace(); + uf_callback_set_error( + uf_rbr_provider_request_to_create_callback( + &rsrc->rbr_request), USBIF_ERROR_UNLINKED); +} + +static void uf_rsrc_submit_tra(struct uf_rsrc *rsrc) +{ + /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */ + struct urb *urb = rsrc->urb; + union usbif_io_tra_parameters *parameters = + &rsrc->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; + 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; + if (rsrc->clear_stall) + parameters->header.flags |= USBIF_IO_FLAGS_CLEAR_STALL; + parameters->header.rbr = uf_create_rbr_request_element_query_rbr( + &rsrc->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 = + uf_create_rbr_request_element_query_rbr( + &rsrc->rbr_request_element[1]); + parameters->isochronous.packet_count = urb->number_of_packets; + } + + uf_device_submit_tra(uf_ep_query_device(rsrc->ep), &rsrc->io_tra); +} + +static void uf_rsrc_submit_tra_1(struct uf_callback *callback) +{ + struct uf_rsrc *rsrc = container_of( + uf_gw_tra_callback_to(callback), + struct uf_rsrc, io_tra); + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + if (uf_callback_query_error(callback) != USBIF_ERROR_UNLINKED) + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_tc); + else + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_tu); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void uf_rsrc_revoke_rbrs(struct uf_rsrc *rsrc) +{ + trace(); + uf_rbr_provider_pool_revoke_and_unreserve_rbrs(&rsrc->rbr_request); +} + +static void uf_rsrc_revoke_rbrs_1(struct uf_callback *callback) +{ + struct uf_rsrc *rsrc = container_of( + uf_rbr_provider_request_revoke_callback_to(callback), + struct uf_rsrc, rbr_request); + unsigned long flags; + trace(); + spin_lock_irqsave(&rsrc->lock, flags); + uf_rsrc_handle_stimulus(rsrc, uf_rsrc_stimulus_rc); + spin_unlock_irqrestore(&rsrc->lock, flags); +} + +static void uf_rsrc_complete(struct uf_rsrc *rsrc) +{ + trace(); + /* Upcall so schedule work to drop the lock to avoid deadlock. */ + (void)uf_work_schedule(&rsrc->complete_1_work); +} + +static void uf_rsrc_complete_1(void *context) +{ + struct uf_rsrc *rsrc = context; + struct urb *urb = rsrc->urb; + usbif_error error; + unsigned long flags; + trace(); + read_lock_irqsave(&dequeue_lock, flags); + urb->hcpriv = 0; + read_unlock_irqrestore(&dequeue_lock, flags); + if (((error = uf_callback_query_error( + uf_rbr_provider_request_to_create_callback( + &rsrc->rbr_request))) == USBIF_ERROR_SUCCESS) && ((error = + uf_callback_query_error( + uf_rbr_provider_request_to_revoke_callback( + &rsrc->rbr_request))) == USBIF_ERROR_SUCCESS)) { + urb->status = usbif_error_map_to_local( + uf_gw_tra_query_error(&rsrc->io_tra)); + urb->actual_length = rsrc->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 = &rsrc->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; + } + local_irq_save(flags); + usb_hcd_giveback_urb( + uf_device_get_drvdata(uf_ep_query_device(rsrc->ep)), + urb, NULL); + local_irq_restore(flags); + /* uf_ep uses the rsrc callback error value to get the busy count */ + /* right. */ + /* There are 3 cases to consider for the error value: */ + /* 1) Normal completion success or failure: use !UNLINKED. */ + /* 2) Unlinked without intermediate stall: use UNLINKED. */ + /* 3) Unlinked after intermediate stall: use !UNLINKED. */ + /* */ + /* In 1 and 3, ep needs to decrement the busy count by 1 on */ + /* completion (in case 1 because there is only the rsrc outstanding */ + /* and in case 3 because there is only a dequeue outstanding, the */ + /* rsrc having previously completed with the stall upcall). */ + /* In case 2, the ep needs to decrement the busy count by 2 since */ + /* both the rsrc and the unlink are outstanding. This corresponds to */ + /* an unlink where the URB happened to fail anyway. */ + if(rsrc->stalled) + error = USBIF_ERROR_SUCCESS; + uf_callback_complete(&rsrc->callback, error); +} + +static void uf_rsrc_stall(struct uf_rsrc *rsrc) +{ + trace(); + rsrc->stalled = 1; + /* Upcall so schedule work to drop the lock to avoid deadlock. */ + (void)uf_work_schedule(&rsrc->stall_1_work); +} + +static void uf_rsrc_stall_1(void *context) +{ + struct uf_rsrc *rsrc = context; + trace(); + uf_ep_rsrc_stalled(rsrc->ep); +} + +static void +uf_rsrc_handle_stimulus(struct uf_rsrc *rsrc, uf_rsrc_stimulus stimulus) +{ + trace(); + switch (rsrc->state) { + case uf_rsrc_state_i: + /* Initialised. */ + /* Maybe completing previous URB. */ + switch (stimulus) { + case uf_rsrc_stimulus_st: + rsrc->state = uf_rsrc_state_i_st; + uf_rsrc_create_rbrs(rsrc); + break; + /* A dequeue is possible whilst we are making the */ + /* complete response after we have transitioned back */ + /* to the i state. */ + case uf_rsrc_stimulus_dq: + uf_rsrc_set_unlinked_error(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st: + /* Creating RBRs. */ + switch (stimulus) { + case uf_rsrc_stimulus_dq: + rsrc->state = uf_rsrc_state_i_st_dq; + uf_rsrc_abort_create_rbrs(rsrc); + break; + case uf_rsrc_stimulus_cs: + rsrc->state = uf_rsrc_state_i_st_cs; + uf_rsrc_submit_tra(rsrc); + break; + case uf_rsrc_stimulus_cf: + rsrc->state = uf_rsrc_state_i; + uf_rsrc_complete(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_dq: + /* Creating RBRs. */ + /* Dequeue URB. */ + switch (stimulus) { + case uf_rsrc_stimulus_cs: + rsrc->state = uf_rsrc_state_i_st_dq_cs; + uf_rsrc_set_unlinked_error(rsrc); + uf_rsrc_revoke_rbrs(rsrc); + break; + case uf_rsrc_stimulus_cf: + rsrc->state = uf_rsrc_state_i; + uf_rsrc_set_unlinked_error(rsrc); + uf_rsrc_complete(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_cs: + /* Transaction submitted. */ + switch (stimulus) { + case uf_rsrc_stimulus_dq: + rsrc->state = uf_rsrc_state_i_st_cs_dq; + uf_rsrc_set_unlinked_error(rsrc); + break; + case uf_rsrc_stimulus_tc: + rsrc->state = uf_rsrc_state_i_st_dq_cs; + uf_rsrc_revoke_rbrs(rsrc); + break; + case uf_rsrc_stimulus_tu: + rsrc->state = uf_rsrc_state_i_st_cs_tu; + uf_rsrc_revoke_rbrs(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_dq_cs: + /* Revoking RBRs. */ + /* Dequeue URB/URB success/failure */ + switch (stimulus) { + case uf_rsrc_stimulus_dq: + uf_rsrc_set_unlinked_error(rsrc); + break; + case uf_rsrc_stimulus_rc: + rsrc->state = uf_rsrc_state_i; + uf_rsrc_complete(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_cs_dq: + /* Transaction submitted */ + /* Dequeue URB. */ + switch (stimulus) { + case uf_rsrc_stimulus_tc: + case uf_rsrc_stimulus_tu: + rsrc->state = uf_rsrc_state_i_st_dq_cs; + uf_rsrc_revoke_rbrs(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_cs_tu: + /* Revoking RBRs. */ + /* BE unlinked tra */ + switch (stimulus) { + case uf_rsrc_stimulus_dq: + rsrc->state = uf_rsrc_state_i_st_dq_cs; + uf_rsrc_set_unlinked_error(rsrc); + break; + case uf_rsrc_stimulus_rc: + rsrc->state = uf_rsrc_state_i_st_cs_tu_rc; + uf_rsrc_stall(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + case uf_rsrc_state_i_st_cs_tu_rc: + /* Stalled. */ + switch (stimulus) { + case uf_rsrc_stimulus_st: + rsrc->state = uf_rsrc_state_i_st; + uf_rsrc_create_rbrs(rsrc); + break; + case uf_rsrc_stimulus_dq: + rsrc->state = uf_rsrc_state_i; + uf_rsrc_set_unlinked_error(rsrc); + uf_rsrc_complete(rsrc); + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } + break; + default: + uf_rsrc_invalid_stimulus(rsrc, stimulus); + break; + } +} + +static int uf_rsrc_init_or_exit(struct uf_rsrc *rsrc, struct uf_ep *ep, +uf_callback_function *callback, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + INIT_LIST_HEAD(&rsrc->link); + uf_callback_init(&rsrc->callback, callback); + rsrc->ep = ep; + rsrc->schedule = (struct usbif_isochronous_io_schedule_element *) + __get_free_page(GFP_KERNEL); + if (rsrc->schedule == NULL) { + return_value = -ENOMEM; + goto exit_no_schedule; + } + spin_lock_init(&rsrc->lock); + rsrc->state = uf_rsrc_state_i; + uf_create_rbr_request_element_init(&rsrc->rbr_request_element[0]); + uf_create_rbr_request_element_init(&rsrc->rbr_request_element[1]); + uf_rbr_provider_request_init(&rsrc->rbr_request, + uf_rsrc_create_rbrs_1, + uf_rsrc_revoke_rbrs_1); + uf_gw_tra_init(&rsrc->io_tra, uf_rsrc_submit_tra_1); + uf_work_init(&rsrc->complete_1_work, uf_rsrc_complete_1, rsrc); + uf_work_init(&rsrc->stall_1_work, uf_rsrc_stall_1, rsrc); + return 0; + exit_path: + free_page((unsigned long)rsrc->schedule); + exit_no_schedule: + return return_value; +} + +int uf_rsrc_init(struct uf_rsrc *rsrc, struct uf_ep *ep, +uf_callback_function *callback) +{ + trace(); + return uf_rsrc_init_or_exit(rsrc, ep, callback, 0); +} + +void uf_rsrc_exit(struct uf_rsrc *rsrc) +{ + trace(); + uf_rsrc_init_or_exit(rsrc, NULL, NULL, 1); +} + +int uf_rsrc_class_init(void) +{ + trace(); + rbr_provider_pool = uf_rbr_provider_pool_allocate(); + return (rbr_provider_pool != NULL) ? 0 : -ENOMEM; +} + +void uf_rsrc_class_exit(void) +{ + trace(); + uf_rbr_provider_pool_free(rbr_provider_pool); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rsrc.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_rsrc.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,81 @@ +/*****************************************************************************/ +/* 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 UF_RSRC_H +#define UF_RSRC_H + +#include +#include +#include +#include "uf_device.h" +#include "uf_rbr_provider_pool.h" + +typedef enum { + uf_rsrc_state_i, + uf_rsrc_state_i_st, + uf_rsrc_state_i_st_dq, + uf_rsrc_state_i_st_cs, + uf_rsrc_state_i_st_dq_cs, + uf_rsrc_state_i_st_cs_dq, + uf_rsrc_state_i_st_cs_tu, + uf_rsrc_state_i_st_cs_tu_rc +} uf_rsrc_state; + +struct uf_rsrc { + struct list_head link; + struct uf_callback callback; + struct uf_ep *ep; + struct usbif_isochronous_io_schedule_element *schedule; + spinlock_t lock; + uf_rsrc_state state; + struct urb *urb; + int clear_stall; + int stalled; + struct uf_create_rbr_request_element rbr_request_element[2]; + struct uf_rbr_provider_request rbr_request; + struct uf_gw_tra io_tra; + struct uf_work complete_1_work; + struct uf_work stall_1_work; +}; + +static inline struct list_head *uf_rsrc_to_link(struct uf_rsrc *rsrc) +{ + return &rsrc->link; +} + +static inline struct uf_rsrc *uf_rsrc_callback_to(struct uf_callback *callback) +{ + return container_of(callback, struct uf_rsrc, callback); +} + +static inline struct uf_ep *uf_rsrc_query_ep(struct uf_rsrc *rsrc) +{ + return rsrc->ep; +} + +void uf_rsrc_start_urb(struct uf_rsrc *rsrc, struct urb *urb, int clear_stall); +void uf_rsrc_restart_urb(struct uf_rsrc *rsrc, int clear_stall); +int uf_rsrc_urb_dequeue(struct urb *urb); +int uf_rsrc_init(struct uf_rsrc *rsrc, struct uf_ep *ep, +uf_callback_function *callback); +void uf_rsrc_exit(struct uf_rsrc *rsrc); +int uf_rsrc_class_init(void); +void uf_rsrc_class_exit(void); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_sll.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_sll.h Tue Oct 3 16:29:44 2006 @@ -0,0 +1,97 @@ +/*****************************************************************************/ +/* 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 UF_SLL_H +#define UF_SLL_H + +struct uf_slk { + struct uf_slk *next; +}; + +struct uf_sll { + struct uf_slk *first; + struct uf_slk *last; +}; + +static inline void uf_slk_init(struct uf_slk *slk) +{ + slk->next = slk; +} + +static inline int uf_slk_is_singular(struct uf_slk *slk) +{ + return (slk->next == slk); +} + +static inline void uf_sll_init(struct uf_sll *sll) +{ + sll->first = 0; +} + +static inline int uf_sll_is_empty(struct uf_sll *sll) +{ + return (sll->first == 0); +} + +static inline void uf_sll_add_last(struct uf_sll *sll, struct uf_slk *slk) +{ + slk->next = 0; + if (sll->first != 0) { + sll->last->next = slk; + } else { + sll->first = slk; + } + sll->last = slk; +} + +static inline struct uf_slk *uf_sll_remove_first(struct uf_sll *sll) +{ + struct uf_slk *slk = sll->first; + if (slk != 0) { + sll->first = slk->next; + slk->next = slk; + } + return slk; +} + +static inline int uf_sll_remove_slk(struct uf_sll *sll, struct uf_slk *slk) +{ + struct uf_slk *prev_slk = NULL; + struct uf_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 f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_trace.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_trace.h Tue Oct 3 16:29:44 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 UF_TRACE_H +#define UF_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_FRONTEND_TRACE + +#define trace_info(format, ...) \ +printk(KERN_INFO "uf %s: " format "\n", __FUNCTION__, ## __VA_ARGS__) +#define trace() trace_info("") +#else +#define trace_info(format, ...) +#define trace() +#endif + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_work.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_work.c Tue Oct 3 16:29:44 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 "uf_work.h" + +static void uf_work_function(void *ignored); + +DEFINE_SPINLOCK(uf_work_list_lock); +LIST_HEAD(uf_work_list); +DECLARE_WORK(uf_work_work, uf_work_function, NULL); +DECLARE_WAIT_QUEUE_HEAD(uf_work_waitqueue); +LIST_HEAD(uf_work_condition); + +void uf_work_wake_up(void) +{ + unsigned long flags; + spin_lock_irqsave(&uf_work_list_lock, flags); + while (!list_empty(&uf_work_condition)) + list_del_init(uf_work_condition.next); + spin_unlock_irqrestore(&uf_work_list_lock, flags); + wake_up(&uf_work_waitqueue); +} + +int uf_work_schedule(struct uf_work *work) +{ + int scheduled = 0; + unsigned long flags; + spin_lock_irqsave(&uf_work_list_lock, flags); + if (list_empty(&work->link)) { + list_add_tail(&work->link, &uf_work_list); + scheduled = 1; + } + spin_unlock_irqrestore(&uf_work_list_lock, flags); + if (scheduled) { + uf_work_wake_up(); + schedule_work(&uf_work_work); + } + return scheduled; +} + +static void uf_work_function(void *ignored) +{ + unsigned long flags; + spin_lock_irqsave(&uf_work_list_lock, flags); + while (!list_empty(&uf_work_list)) { + struct uf_work *work = list_entry( + uf_work_list.next, + struct uf_work, + link); + list_del_init(&work->link); + spin_unlock_irqrestore(&uf_work_list_lock, flags); + uf_work_perform_synchronously(work); + spin_lock_irqsave(&uf_work_list_lock, flags); + } + spin_unlock_irqrestore(&uf_work_list_lock, flags); +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_work.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_work.h Tue Oct 3 16:29:44 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 UF_WORK_H +#define UF_WORK_H + +#include +#include +#include +#include +#include + +struct uf_work { + struct list_head link; + void (*function) (void *context); + void *context; +}; + +#define UF_WORK_INIT( name, fn, ctx ) \ +{ .link = LIST_HEAD_INIT( name.link ), .function = fn, .context = ctx } + +#define UF_WORK( name, fn, ctx ) \ +struct uf_work name = UF_WORK_INIT( name, fn, ctx ) + +static inline void +uf_work_init(struct uf_work *work, void (*function)(void *context), +void *context) +{ + INIT_LIST_HEAD(&work->link); + work->function = function; + work->context = context; +} + +static inline +struct list_head *uf_work_to_link(struct uf_work *work) +{ + return &work->link; +} + +static inline struct uf_work * +uf_work_link_to(struct list_head *link) +{ + return container_of(link, struct uf_work, link); +} + +int uf_work_schedule(struct uf_work *work); + +static inline void +uf_work_perform_synchronously(struct uf_work *work) +{ + work->function(work->context); +} + +extern spinlock_t uf_work_list_lock; +extern struct list_head uf_work_list; +extern wait_queue_head_t uf_work_waitqueue; +extern struct list_head uf_work_condition; + +#define uf_work_until( condition ) \ +do { \ + unsigned long flags; \ + spin_lock_irqsave(&uf_work_list_lock, flags); \ + for (;;) { \ + struct list_head link; \ + while (!list_empty(&uf_work_list) && !(condition)) { \ + struct uf_work *work; \ + work = list_entry(uf_work_list.next, \ + struct uf_work, link); \ + list_del_init(&work->link); \ + spin_unlock_irqrestore(&uf_work_list_lock, \ + flags); \ + uf_work_perform_synchronously(work); \ + spin_lock_irqsave(&uf_work_list_lock, flags); \ + } \ + if (condition) \ + break; \ + INIT_LIST_HEAD(&link); \ + list_add_tail(&link, &uf_work_condition); \ + spin_unlock_irqrestore(&uf_work_list_lock, flags); \ + wait_event(uf_work_waitqueue, list_empty(&link)); \ + spin_lock_irqsave(&uf_work_list_lock, flags); \ + } \ + spin_unlock_irqrestore(&uf_work_list_lock, flags); \ +} \ +while (0) + +void uf_work_wake_up(void); + +#endif diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_xb_channel.c --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_xb_channel.c Tue Oct 3 16:29:44 2006 @@ -0,0 +1,399 @@ +/*****************************************************************************/ +/* 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 "uf_xb_channel.h" +#include "uf_trace.h" + +typedef enum { + uf_xb_channel_stimulus_cn, + uf_xb_channel_stimulus_dn, + uf_xb_channel_stimulus_bc, + uf_xb_channel_stimulus_pe, + uf_xb_channel_stimulus_rs, + uf_xb_channel_stimulus_rf +} uf_xb_channel_stimulus; + +static void uf_xb_channel_handle_stimulus(struct uf_xb_channel *channel, +uf_xb_channel_stimulus stimulus); + +void uf_xb_channel_connect(struct uf_xb_channel *channel, +struct xenbus_device *device) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->device = device; + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_cn); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void uf_xb_channel_backend_closing(struct uf_xb_channel *channel, +struct xenbus_device *dev) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_bc); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void uf_xb_channel_backend_changed(struct uf_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 XenbusStateClosing: + uf_xb_channel_backend_closing(channel, dev); + break; + default: + break; + } +} + +static void uf_xb_channel_protocol_error(struct uf_ring_channel *ring_channel) +{ + struct uf_xb_channel *channel = + uf_xb_channel_ring_channel_to(ring_channel); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_pe); + spin_unlock_irqrestore(&channel->lock, flags); +} + +void uf_xb_channel_disconnect(struct uf_xb_channel *channel) +{ + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + channel->disconnected = 0; + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_dn); + spin_unlock_irqrestore(&channel->lock, flags); + uf_work_until(channel->disconnected); +} + +static void uf_xb_channel_invalid_stimulus(struct uf_xb_channel *channel, +uf_xb_channel_stimulus stimulus) +{ + trace(); + printk(KERN_ERR "uf: xb channel %p in state %d" + " received invalid stimulus %d", channel, channel->state, + stimulus); +} + +static void uf_xb_channel_connect_ring(struct uf_xb_channel *channel) +{ + trace(); + channel->ring_connect.domain_id = channel->device->otherend_id; + uf_ring_channel_connect(&channel->channel, &channel->ring_connect); +} + +static void uf_xb_channel_connect_ring_1(struct uf_callback *callback) +{ + struct uf_xb_channel *channel = container_of( + uf_ring_channel_connect_request_callback_to(callback), + struct uf_xb_channel, ring_connect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void uf_xb_channel_publish_details(struct uf_xb_channel *channel) +{ + trace(); + uf_work_schedule(&channel->publish_details_1_work); +} + +static void uf_xb_channel_publish_details_1(void *data) +{ + struct uf_xb_channel *channel = data; + struct xenbus_transaction transaction; + int error; + unsigned long flags; + trace(); + trace_info("%s", channel->device->nodename); + 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_transaction_end(transaction, 0); + if (error) { + if (error == -EAGAIN) { + goto again; + } else { + trace_info("error committing transaction"); + goto transaction_error; + } + } + xenbus_switch_state(channel->device, XenbusStateConnected); + spin_lock_irqsave(&channel->lock, flags); + uf_xb_channel_handle_stimulus(channel, + uf_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); + uf_xb_channel_handle_stimulus(channel, + uf_xb_channel_stimulus_rf); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void uf_xb_channel_disconnect_ring(struct uf_xb_channel *channel) +{ + trace(); + uf_ring_channel_disconnect(&channel->channel, + &channel->ring_disconnect); +} + +static void uf_xb_channel_disconnect_ring_1(struct uf_callback *callback) +{ + struct uf_xb_channel *channel = container_of(callback, + struct uf_xb_channel, ring_disconnect); + unsigned long flags; + trace(); + spin_lock_irqsave(&channel->lock, flags); + uf_xb_channel_handle_stimulus(channel, uf_xb_channel_stimulus_rs); + spin_unlock_irqrestore(&channel->lock, flags); +} + +static void uf_xb_channel_complete_disconnect(struct uf_xb_channel *channel) +{ + trace(); + channel->disconnected = 1; + uf_work_wake_up(); +} + +static int uf_xb_channel_init_or_exit(struct uf_xb_channel *channel, int exit) +{ + int return_value = 0; + trace(); + if (exit) + goto exit_path; + if ((return_value = uf_ring_channel_init(&channel->channel, + uf_xb_channel_protocol_error)) != 0) { + goto exit_no_ring_channel; + } + spin_lock_init(&channel->lock); + channel->state = uf_xb_channel_state_i; + uf_callback_init(&channel->ring_connect.callback, + uf_xb_channel_connect_ring_1); + uf_work_init(&channel->publish_details_1_work, + uf_xb_channel_publish_details_1, + channel); + uf_callback_init(&channel->ring_disconnect, + uf_xb_channel_disconnect_ring_1); + return 0; + exit_path: + uf_ring_channel_exit(&channel->channel); + exit_no_ring_channel: + return return_value; +} + +int uf_xb_channel_init(struct uf_xb_channel *channel) +{ + return uf_xb_channel_init_or_exit(channel, 0); +} + +void uf_xb_channel_exit(struct uf_xb_channel *channel) +{ + trace(); + (void)uf_xb_channel_init_or_exit(channel, 1); +} + +static void uf_xb_channel_handle_stimulus(struct uf_xb_channel *channel, +uf_xb_channel_stimulus stimulus) { + trace_info("xb channel %p in state %d received stimulus %d", channel, + channel->state, stimulus); + switch (channel->state) { + case uf_xb_channel_state_i: + switch (stimulus) { + case uf_xb_channel_stimulus_cn: + channel->state = uf_xb_channel_state_i_cn; + uf_xb_channel_connect_ring(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + channel->state = uf_xb_channel_state_i_cn_dn; + break; + case uf_xb_channel_stimulus_rs: + channel->state = uf_xb_channel_state_i_cn_rs; + uf_xb_channel_publish_details(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_dn: + switch (stimulus) { + case uf_xb_channel_stimulus_rs: + channel->state = uf_xb_channel_state_i_cn_dn_rs; + uf_xb_channel_disconnect_ring(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + channel->state = uf_xb_channel_state_i_cn_rs_dn; + break; + case uf_xb_channel_stimulus_rs: + channel->state = uf_xb_channel_state_i_cn_rs_rs; + break; + case uf_xb_channel_stimulus_rf: + channel->state = uf_xb_channel_state_i_cn_rs_rf; + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_dn_rs: + switch (stimulus) { + case uf_xb_channel_stimulus_rs: + channel->state = uf_xb_channel_state_i; + uf_xb_channel_complete_disconnect(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs_dn: + switch (stimulus) { + case uf_xb_channel_stimulus_rs: + case uf_xb_channel_stimulus_rf: + uf_xb_channel_disconnect_ring(channel); + channel->state = uf_xb_channel_state_i_cn_dn_rs; + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs_rs: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + uf_xb_channel_disconnect_ring(channel); + channel->state = uf_xb_channel_state_i_cn_dn_rs; + break; + case uf_xb_channel_stimulus_bc: + channel->state = uf_xb_channel_state_i_cn_rs_rs_bc; + uf_xb_channel_disconnect_ring(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs_rf: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + uf_xb_channel_disconnect_ring(channel); + channel->state = uf_xb_channel_state_i_cn_dn_rs; + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs_rs_bc: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + channel->state = uf_xb_channel_state_i_cn_dn_rs; + break; + case uf_xb_channel_stimulus_rs: + channel->state = uf_xb_channel_state_i_cn_rs_rs_bc_rs; + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + case uf_xb_channel_state_i_cn_rs_rs_bc_rs: + switch (stimulus) { + case uf_xb_channel_stimulus_dn: + channel->state = uf_xb_channel_state_i; + uf_xb_channel_complete_disconnect(channel); + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } + break; + default: + uf_xb_channel_invalid_stimulus(channel, stimulus); + break; + } +} diff -r f426f6e646eb -r 404b4ec94253 linux-2.6-xen-sparse/drivers/xen/usbfront/uf_xb_channel.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/uf_xb_channel.h Tue Oct 3 16:29:44 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 UF_XB_CHANNEL_H +#define UF_XB_CHANNEL_H + +#include +#include "uf_ring_channel.h" + +typedef enum { + uf_xb_channel_state_i, + uf_xb_channel_state_i_cn, + uf_xb_channel_state_i_cn_dn, + uf_xb_channel_state_i_cn_rs, + uf_xb_channel_state_i_cn_dn_rs, + uf_xb_channel_state_i_cn_rs_dn, + uf_xb_channel_state_i_cn_rs_rs, + uf_xb_channel_state_i_cn_rs_rf, + uf_xb_channel_state_i_cn_rs_rs_bc, + uf_xb_channel_state_i_cn_rs_rs_bc_rs +} uf_xb_channel_state; + +struct uf_xb_channel { + struct uf_ring_channel channel; + spinlock_t lock; + uf_xb_channel_state state; + struct xenbus_device *device; + struct uf_ring_channel_connect_request ring_connect; + struct uf_work publish_details_1_work; + struct uf_callback ring_disconnect; + int disconnected; +}; + +static inline struct uf_channel * +uf_xb_channel_to_channel(struct uf_xb_channel *channel) +{ + return uf_ring_channel_to_channel(&channel->channel); +} + +static inline struct uf_xb_channel * +uf_xb_channel_ring_channel_to(struct uf_ring_channel *ring_channel) +{ + return container_of(ring_channel, struct uf_xb_channel, channel); +} + +static inline struct uf_xb_channel * +uf_xb_channel_channel_to(struct uf_channel *channel) +{ + return uf_xb_channel_ring_channel_to( + uf_ring_channel_channel_to(channel)); +} + +void uf_xb_channel_connect(struct uf_xb_channel *channel, +struct xenbus_device *device); +void uf_xb_channel_backend_changed(struct uf_xb_channel *channel, +struct xenbus_device *dev, XenbusState state); +void uf_xb_channel_disconnect(struct uf_xb_channel *channel); +int uf_xb_channel_init(struct uf_xb_channel *channel); +void uf_xb_channel_exit(struct uf_xb_channel *channel); + +#endif diff -r f426f6e646eb -r 404b4ec94253 tools/examples/usb --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/tools/examples/usb Tue Oct 3 16:29:44 2006 @@ -0,0 +1,6 @@ +#!/bin/sh + +dir=$(dirname "$0") +. "$dir/xen-hotplug-common.sh" + +success diff -r f426f6e646eb -r 404b4ec94253 xen/include/public/io/usbif.h --- /dev/null Mon Oct 2 17:04:56 2006 +++ b/xen/include/public/io/usbif.h Tue Oct 3 16:29:44 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_STALL 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 flags; + 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 +#define USBIF_IO_FLAGS_CLEAR_STALL 0x80000000 + +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_stall_tra_parameters { + struct usbif_tra_parameters_header header; + uint8_t endpoint; + uint8_t direction; + uint8_t reserved[6]; +}; + +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_stall_tra_parameters stall; +}; + +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