diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/arch/xen/Kconfig --- a/linux-2.6-xen-sparse/arch/xen/Kconfig Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/arch/xen/Kconfig Sun Oct 30 16:03:34 2005 @@ -38,6 +38,14 @@ (e.g., hard drives, network cards). This allows you to configure such devices and also includes some low-level support that is otherwise not compiled into the kernel. + +config XEN_IDC_TRACE + bool "Inter-domain communication code tracing" + default y + help + This option causes the IDC code to output a continual trace of its + activity. + Say N here unless you are trying to debug this code. config XEN_BLKDEV_BACKEND bool "Block-device backend driver" @@ -69,6 +77,29 @@ The network-device backend driver allows the kernel to export its network devices to other guests via a high-performance shared-memory interface. + +config XEN_USBDEV_BACKEND + tristate "USB-device backend driver" + depends on XEN_PHYSDEV_ACCESS + select USB + default m + 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 or M 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 y + 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_FRONTEND bool "TPM-device frontend driver" @@ -128,6 +159,30 @@ are unsure; or if you experience network hangs when this option is enabled; then you must say N here. +config XEN_USBDEV_FRONTEND + tristate "USB-device frontend driver" + select USB + default m + 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 or M 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 y + help + This option causes the driver to output a continual trace of its + activity. + Say N here unless you are trying to debug the driver. + config XEN_BLKDEV_TAP bool "Block device tap driver" default n diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Sun Oct 30 16:03:34 2005 @@ -1,4 +1,3 @@ - obj-y += util.o obj-y += console/ @@ -6,12 +5,14 @@ obj-y += balloon/ obj-y += privcmd/ obj-y += xenbus/ +obj-y += xenidc/ obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/ obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/ +obj-$(CONFIG_XEN_USBDEV_BACKEND) += usbback/ obj-$(CONFIG_XEN_TPMDEV_BACKEND) += tpmback/ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += usbfront/ obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/ obj-$(CONFIG_XEN_TPMDEV_FRONTEND) += tpmfront/ - diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.c Sun Oct 30 16:03:34 2005 @@ -264,6 +264,7 @@ { return xenbus_register_driver_common(drv, &xenbus_backend); } +EXPORT_SYMBOL(xenbus_register_backend); void xenbus_unregister_driver(struct xenbus_driver *drv) { diff -r b5903c9aeda5 -r e93b9c54edb3 tools/ioemu/target-i386-dm/Makefile --- a/tools/ioemu/target-i386-dm/Makefile Sun Oct 30 09:45:49 2005 +++ b/tools/ioemu/target-i386-dm/Makefile Sun Oct 30 16:03:34 2005 @@ -188,7 +188,7 @@ ######################################################### DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DAPIC_SUPPORT -LIBS+=-lm -L../../libxc -lxenctrl +LIBS+=-lm -L../../libxc -lxenctrl -lpthread ifndef CONFIG_USER_ONLY LIBS+=-lz endif diff -r b5903c9aeda5 -r e93b9c54edb3 tools/python/xen/xend/server/usbif.py --- a/tools/python/xen/xend/server/usbif.py Sun Oct 30 09:45:49 2005 +++ b/tools/python/xen/xend/server/usbif.py Sun Oct 30 16:03:34 2005 @@ -22,8 +22,9 @@ """Support for virtual USB hubs. """ +from xen.xend import sxp + from xen.xend.server.DevController import DevController - class UsbifController(DevController): """USB device interface controller. Handles all USB devices @@ -35,8 +36,16 @@ """ 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) diff -r b5903c9aeda5 -r e93b9c54edb3 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Sun Oct 30 09:45:49 2005 +++ b/tools/python/xen/xm/create.py Sun Oct 30 16:03:34 2005 @@ -222,6 +222,10 @@ fn=set_bool, default=0, use="Make the domain a network interface backend.") +gopts.var('usbif', val='no|yes', + fn=set_bool, default=0, + use="Make the domain a USB device backend.") + gopts.var('tpmif', val='frontend=DOM', fn=append_value, default=[], use="""Make the domain a TPM interface backend. If frontend is given, @@ -242,10 +246,12 @@ For example '-pci c0,02,1a'. The option may be repeated to add more than one pci device.""") -gopts.var('usb', val='PATH', +gopts.var('usb', val="port=PORT,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="""Add a USB device, as specified by the path to that device in + the backend domain, to the specified virtual hub port of a 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('ipaddr', val="IPADDR", fn=append_value, default=[], @@ -427,8 +433,13 @@ config_devs.append(['device', config_pci]) def configure_usb(opts, config_devs, vals): - for path in vals.usb: - config_usb = ['usb', ['path', path]] + for d in vals.usb: + path = d.get('path') + backend = d.get('backend') + config_usb = ['usb'] + config_usb.append(['path', path]) + if backend: + config_usb.append(['backend', backend]) config_devs.append(['device', config_usb]) def configure_vtpm(opts, config_devs, vals): @@ -578,6 +589,8 @@ config.append(['backend', ['blkif']]) if vals.netif: config.append(['backend', ['netif']]) + if vals.usbif: + config.append(['backend', ['usbif']]) if vals.tpmif: config.append(['backend', ['tpmif']]) if vals.restart: @@ -649,6 +662,22 @@ d[k] = v vifs.append(d) vals.vif = vifs + +def preprocess_usb(opts, vals): + if not vals.usb: return + usb = [] + for port in vals.usb: + d = {} + a = port.split(',') + for b in a: + (k, v) = b.strip().split('=', 1) + k = k.strip() + v = v.strip() + if k not in ['path', 'backend']: + opts.err('Invalid usb port specifier: ' + port) + d[k] = v + usb.append(d) + vals.usb = usb def preprocess_vtpm(opts, vals): if not vals.vtpm: return @@ -770,9 +799,10 @@ preprocess_ip(opts, vals) preprocess_nfs(opts, vals) preprocess_vnc(opts, vals) + preprocess_usb(opts, vals) preprocess_vtpm(opts, vals) preprocess_tpmif(opts, vals) - + def make_domain(opts, config): """Create, build and start a domain. diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/Makefile --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/Makefile Sun Oct 30 16:03:34 2005 @@ -0,0 +1,9 @@ +obj-$(CONFIG_XEN_USBDEV_BACKEND) += usbback.o + +usbback-objs := \ +usbback_device.o \ +usbback_driver_backend.o \ +usbback_driver.o \ +usbback_driver_port.o \ +usbback_driver_port_resource.o \ +usbback_module.o diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_assert.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_assert.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,43 @@ +/*****************************************************************************/ +/* Implementation of the ASSERT macro */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_ASSERT_H +#define USBBACK_ASSERT_H + +#include + +static inline void assert_failed + ( const char * function, int line, const char * statement ) +{ + printk + ( + KERN_ERR "usbback assert failed: %s line %d, statement %s\n", + function, + line, + statement + ); + + BUG(); +} + +#define ASSERT( S ) \ +( ( S ) ? ( (void)0 ) : assert_failed( __PRETTY_FUNCTION__, __LINE__, #S ) ) + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,401 @@ +/*****************************************************************************/ +/* A device object representing the low-level connection to a single */ +/* front-end. */ +/* */ +/* 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 "usbback_assert.h" +#include "usbback_device.h" +#include "usbback_driver.h" +#include "usbback_trace.h" + +struct usbback_device +{ + struct xenbus_device * dev; + void * drvdata; + long int frontend_id; + char * frontend; + xenidc_endpoint endpoint; +}; + +static inline struct usbback_device * usbback_device_endpoint_to + ( xenidc_endpoint * endpoint ) +{ + trace(); + + return container_of( endpoint, struct usbback_device, endpoint ); +} + +void usbback_device_set_drvdata( struct usbback_device * device, void * data ) +{ + trace(); + + device->drvdata = data; +} + +void * usbback_device_get_drvdata( struct usbback_device * device ) +{ + trace(); + + return device->drvdata; +} + +static void usbback_device_endpoint_connect( xenidc_endpoint * endpoint ) +{ + trace(); + + /* Transactions and messages received between connect and disconnect are */ + /* processed normally. */ + /* Between connect and completion of the disconnect callback we are */ + /* allowed to issue messages and transactions. We don't actually need */ + /* to issue messages or transactions from the backend to the frontend. */ + + usbback_driver_connect( usbback_device_endpoint_to( endpoint ) ); +} + +static void usbback_device_endpoint_disconnect + ( xenidc_endpoint * endpoint, xenidc_callback * callback ) +{ + trace(); + + /* We must stop issuing messages and transactions and complete the */ + /* callback once all of the messages and transactions we are issuing */ + /* have completed or failed back to us. */ + + /* After the disconnect, any messages or transactions we receive must be */ + /* failed back and we must flush through any that are in progress. */ + + usbback_driver_disconnect( usbback_device_endpoint_to( endpoint ) ); + + /* We can complete the callback immediately since we are not issuing any */ + /* messages or transactions from the backend to the frontend. */ + + xenidc_callback_success( callback ); +} + +static void usbback_device_endpoint_message + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ) +{ + trace(); + + usbback_driver_message_handler + ( usbback_device_endpoint_to( endpoint ), message ); +} + +static void usbback_device_endpoint_transaction + ( xenidc_endpoint * endpoint, xenidc_endpoint_transaction * transaction ) +{ + trace(); + + usbback_driver_transaction_handler + ( usbback_device_endpoint_to( endpoint ), transaction ); +} + +static int usbback_device_init_or_exit + ( struct usbback_device * device, struct xenbus_device * dev, int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + memset( device, 0, sizeof( *device ) ); + + device->dev = dev; + + return_value = usbback_driver_probe_usbback_device( device ); + + if( return_value != 0 ) + { + goto EXIT_NO_DRIVER; + } + + device->frontend = NULL; + + return_value = xenbus_gather + ( + NULL, /* FIXME? */ + dev->nodename, + "frontend-id", "%li", &device->frontend_id, + "frontend", NULL, &device->frontend, + NULL + ); + + if( XENBUS_EXIST_ERR( return_value ) ) + { + goto EXIT_NO_FRONTEND; + } + + if( return_value < 0 ) + { + xenbus_dev_error + ( + dev, + return_value, + "reading %s/frontend", + dev->nodename + ); + + goto EXIT_NO_FRONTEND; + } + + return_value = xenidc_endpoint_init + ( + &device->endpoint, + usbback_device_endpoint_connect, + usbback_device_endpoint_message, + usbback_device_endpoint_transaction, + usbback_device_endpoint_disconnect, + USBIF_BE_INITIATOR_QUOTA, + USBIF_BE_INITIATOR_MAXIMUM_BYTE_COUNT, + USBIF_BE_TARGET_QUOTA, + USBIF_BE_TARGET_MAXIMUM_BYTE_COUNT + ); + + if( return_value != 0 ) + { + goto EXIT_NO_ENDPOINT; + } + + { + xenidc_address address; + + xenidc_address_init + ( + &address, + dev->nodename, + device->frontend, + device->frontend_id + ); + + xenidc_endpoint_create( &device->endpoint, address ); + } + + return 0; + + EXIT: + + xenidc_endpoint_destroy( &device->endpoint ); + + xenidc_endpoint_exit( &device->endpoint ); + + EXIT_NO_ENDPOINT: + + kfree( device->frontend ); + + EXIT_NO_FRONTEND: + + usbback_driver_remove_usbback_device( device ); + + EXIT_NO_DRIVER: + + return return_value; + } +} + +static int usbback_device_init + ( struct usbback_device * device, struct xenbus_device * dev ) +{ + trace(); + + return usbback_device_init_or_exit( device, dev, 0 ); +} + +static void usbback_device_exit( struct usbback_device * device ) +{ + trace(); + + (void)usbback_device_init_or_exit( device, NULL, 1 ); +} + +static int usbback_device_probe_or_remove + ( struct xenbus_device * dev, int remove ) +{ + trace(); + + { + int return_value = 0; + + struct usbback_device * device; + + if( remove ) + { + goto REMOVE; + } + + device = kmalloc( sizeof( *device ), GFP_KERNEL ); + + if( device == NULL ) + { + xenbus_dev_error( dev, -ENOMEM, "allocating backend structure" ); + + return_value = -ENOMEM; + + goto EXIT_NO_DEVICE; + } + + dev->data = device; + + return_value = usbback_device_init( device, dev ); + + if( return_value != 0 ) + { + goto EXIT_INIT_FAILED; + } + + return 0; + + REMOVE: + + device = dev->data; + + usbback_device_exit( device ); + + EXIT_INIT_FAILED: + + dev->data = NULL; + + kfree( device ); + + EXIT_NO_DEVICE: + + return return_value; + } +} + +static int usbback_device_probe + ( struct xenbus_device * dev, const struct xenbus_device_id * id ) +{ + trace(); + + return usbback_device_probe_or_remove( dev, 0 ); +} + +static int usbback_device_remove( struct xenbus_device * dev ) +{ + trace(); + + return usbback_device_probe_or_remove( dev, 1 ); +} + +static struct xenbus_device_id usbback_device_ids[] = +{ + { "usb" }, + { "" } +}; + +static struct xenbus_driver usbback_device_driver = +{ + .name = "usb", + .owner = THIS_MODULE, + .ids = usbback_device_ids, + .probe = usbback_device_probe, + .remove = usbback_device_remove, +}; + +int usbback_device_class_init( void ) +{ + trace(); + + xenbus_register_backend( &usbback_device_driver ); + + return 0; +} + +void usbback_device_class_exit( void ) +{ + trace(); + + xenbus_unregister_driver( &usbback_device_driver ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,37 @@ +/*****************************************************************************/ +/* A device object representing the low-level connection to a single */ +/* front-end. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DEVICE_H +#define USBBACK_DEVICE_H + +extern int usbback_device_class_init( void ); + +extern void usbback_device_class_exit( void ); + +struct usbback_device; + +extern void usbback_device_set_drvdata + ( struct usbback_device * device, void * data ); + +extern void * usbback_device_get_drvdata( struct usbback_device * device ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,249 @@ +/*****************************************************************************/ +/* Device driver which drives usbback_device devices. */ +/* */ +/* 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 "usbback_assert.h" +#include "usbback_device.h" +#include "usbback_driver.h" +#include "usbback_driver_backend.h" +#include "usbback_driver_port.h" +#include "usbback_trace.h" + +static int usbback_driver_probe_usb + ( struct usb_interface * intf, const struct usb_device_id * id ) +{ + trace(); + + return usbback_driver_port_probe_usb( intf ); +} + +static void usbback_driver_disconnect_usb( struct usb_interface * intf ) +{ + trace(); + + usbback_driver_port_disconnect_usb( intf ); +} + +static int usbback_driver_probe_or_remove_usbback_device + ( struct usbback_device * device, int remove ) +{ + trace(); + + { + struct usbback_driver_backend * backend; + + int return_value = 0; + + if( remove ) + { + goto REMOVE; + } + + backend = usbback_driver_backend_allocate( device ); + + if( backend == NULL ) + { + return_value = -ENOMEM; + + goto EXIT_NO_BACKEND; + } + + usbback_device_set_drvdata( device, backend ); + + return 0; + + REMOVE: + + backend = usbback_device_get_drvdata( device ); + + usbback_driver_backend_shutdown( backend ); + + usbback_device_set_drvdata( device, NULL ); + + EXIT_NO_BACKEND: + + return return_value; + } +} + +int usbback_driver_probe_usbback_device( struct usbback_device * device ) +{ + trace(); + + return usbback_driver_probe_or_remove_usbback_device( device, 0 ); +} + +void usbback_driver_connect( struct usbback_device * device ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_connect( backend ); + } +} + +void usbback_driver_message_handler + ( struct usbback_device * device, xenidc_endpoint_message * message ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_message_handler( backend, message ); + } +} + +void usbback_driver_transaction_handler + ( struct usbback_device * device, xenidc_endpoint_transaction * transaction ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_transaction_handler( backend, transaction ); + } +} + +void usbback_driver_disconnect( struct usbback_device * device ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_disconnect( backend ); + } +} + +void usbback_driver_claim_port + ( struct usbback_device * device, u32 port, char * path ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_claim_port( backend, port, path ); + } +} + +void usbback_driver_release_port + ( struct usbback_device * device, u32 port ) +{ + trace(); + + { + struct usbback_driver_backend * backend = + usbback_device_get_drvdata( device ); + + usbback_driver_backend_release_port( backend, port ); + } +} + +void usbback_driver_remove_usbback_device( struct usbback_device * device ) +{ + trace(); + + (void)usbback_driver_probe_or_remove_usbback_device( device, 1 ); +} + +static struct usb_device_id usbback_driver_usb_id_table[] = +{ + { .driver_info = 1 }, /* Matches all devices. */ + {} +}; + +MODULE_DEVICE_TABLE( usb, usbback_driver_usb_id_table ); + +struct usb_driver usbback_driver_usb_driver = +{ + .owner = THIS_MODULE, + .name = "usbback", + .probe = usbback_driver_probe_usb, + .disconnect = usbback_driver_disconnect_usb, + .id_table = usbback_driver_usb_id_table, +}; + +static int usbback_driver_init_or_exit( int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + return_value = usbback_driver_port_class_init(); + + if( return_value != 0 ) + { + goto EXIT_NO_PORT_CLASS; + } + + return_value = usb_register( &usbback_driver_usb_driver ); + + if( return_value != 0 ) + { + goto EXIT_NO_USB_REGISTER; + } + + return 0; + + EXIT: + + usb_deregister( &usbback_driver_usb_driver ); + + EXIT_NO_USB_REGISTER: + + usbback_driver_port_class_exit(); + + EXIT_NO_PORT_CLASS: + + return return_value; + } +} + +int usbback_driver_init( void ) +{ + trace(); + + return usbback_driver_init_or_exit( 0 ); +} + +void usbback_driver_exit( void ) +{ + trace(); + + (void)usbback_driver_init_or_exit( 1 ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,57 @@ +/*****************************************************************************/ +/* Device driver which drives usbback_device devices. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DRIVER_H +#define USBBACK_DRIVER_H + +#include "asm-xen/xenidc.h" +#include "usbback_device.h" + +extern int usbback_driver_init( void ); + +extern int usbback_driver_probe_usbback_device + ( struct usbback_device * device ); + +extern void usbback_driver_connect( struct usbback_device * device ); + +extern void usbback_driver_message_handler + ( struct usbback_device * device, xenidc_endpoint_message * message ); + +extern void usbback_driver_transaction_handler +( + struct usbback_device * device, + xenidc_endpoint_transaction * transaction +); + +extern void usbback_driver_disconnect( struct usbback_device * device ); + +extern void usbback_driver_claim_port + ( struct usbback_device * device, u32 port, char * path ); + +extern void usbback_driver_release_port + ( struct usbback_device * device, u32 port ); + +extern void usbback_driver_remove_usbback_device + ( struct usbback_device * device ); + +extern void usbback_driver_exit( void ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,603 @@ +/*****************************************************************************/ +/* usbback_driver_backend is the representation in the driver of the */ +/* back-end side of a single front-end to back-end connection. */ +/* One of these objects is used by the usbback_driver to manage each */ +/* usbback_device. */ +/* */ +/* 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 "usbback_assert.h" +#include "usbback_device.h" +#include "usbback_driver_backend.h" +#include "usbback_driver_port.h" +#include "usbback_trace.h" + +#define USBBACK_DRIVER_PORT_COUNT 7 + +struct usbback_driver_backend +{ + struct usbback_device * device; + struct usbback_driver_port port[ USBBACK_DRIVER_PORT_COUNT ]; +}; + +static int usbback_driver_backend_init_or_exit +( + struct usbback_driver_backend * backend, + struct usbback_device * device, + int exit +) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + memset( backend, 0, sizeof( *backend ) ); + + backend->device = device; + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++ ) + { + return_value = + usbback_driver_port_init( &backend->port[ i ], backend ); + + if( return_value != 0 ) + { + goto EXIT_NO_PORT; + } + } + + return 0; + + EXIT: + + i = USBBACK_DRIVER_PORT_COUNT; + + EXIT_NO_PORT: + + i--; + + for( ; i >= 0; i-- ) + { + usbback_driver_port_shutdown( &backend->port[ i ] ); + } + } + + return return_value; + } +} + +struct usbback_driver_backend * usbback_driver_backend_allocate + ( struct usbback_device * device ) +{ + struct usbback_driver_backend * backend = + (struct usbback_driver_backend *)kmalloc + ( sizeof( struct usbback_driver_backend ), GFP_KERNEL ); + + if + ( + ( backend != NULL ) + && + ( usbback_driver_backend_init_or_exit( backend, device, 0 ) != 0 ) + ) + { + kfree( backend ); + + backend = NULL; + } + + return backend; +} + +void usbback_driver_backend_connect( struct usbback_driver_backend * backend ) +{ + trace(); + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++ ) + { + usbback_driver_port_connect( &backend->port[ i ] ); + } + } +} + +static void usbback_driver_backend_handle_probe +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +); + +static void usbback_driver_backend_handle_reset +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +); + +static void usbback_driver_backend_handle_io +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +); + +void usbback_driver_backend_transaction_handler +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +) +{ + /* trace(); */ + + usbif_transaction_parameters_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr( transaction ), + &header, + sizeof( header ) + ) + == + sizeof( header ) + ) + { + switch( header.transaction_type ) + { + case USBIF_TRANSACTION_TYPE_PROBE: + usbback_driver_backend_handle_probe( backend, transaction ); + break; + case USBIF_TRANSACTION_TYPE_RESET: + usbback_driver_backend_handle_reset( backend, transaction ); + break; + case USBIF_TRANSACTION_TYPE_IO: + usbback_driver_backend_handle_io( backend, transaction ); + break; + default: + xenidc_endpoint_transaction_complete + ( transaction, XENIDC_ERROR_INVALID_PARAMETER ); + break; + } + } + else + { + /* Parameters were underlength. */ + + xenidc_endpoint_transaction_complete + ( transaction, XENIDC_ERROR_INVALID_FORMAT ); + } +} + +static void usbback_driver_backend_handle_probe +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +) +{ + /* trace(); */ + + usbif_probe_transaction_parameters parameters; + + xenidc_error error; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr( transaction ), + ¶meters, + sizeof( parameters ) + ) + != + sizeof( parameters ) + ) + { + error = XENIDC_ERROR_INVALID_FORMAT; + + goto COMPLETE; + } + + if + ( + ( parameters.port <= 0 ) + || + ( parameters.port > USBBACK_DRIVER_PORT_COUNT ) + ) + { + error = XENIDC_ERROR_INVALID_PARAMETER; + + goto COMPLETE; + } + + { + usbif_probe_transaction_status status; + + memset( &status, 0, sizeof( status ) ); + + status.result = usbback_driver_port_usbdev_is_connected + ( &backend->port[ parameters.port - 1 ] ) ? + USBIF_PROBE_RESULT_DEVICE_PRESENT : + USBIF_PROBE_RESULT_NO_DEVICE; + + if + ( + xenidc_local_buffer_reference_copy_in + ( + xenidc_endpoint_transaction_to_status_lbr( transaction ), + &status, + sizeof( status ) + ) + != + sizeof( status ) + ) + { + error = XENIDC_ERROR_INVALID_FORMAT; + + goto COMPLETE; + } + } + + error = XENIDC_ERROR_SUCCESS; + + COMPLETE: + + xenidc_endpoint_transaction_complete( transaction, error ); +} + +static void usbback_driver_backend_handle_reset +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +) +{ + trace(); + + { + usbif_reset_transaction_parameters parameters; + + xenidc_error error; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr( transaction ), + ¶meters, + sizeof( parameters ) + ) + != + sizeof( parameters ) + ) + { + error = XENIDC_ERROR_INVALID_FORMAT; + + goto COMPLETE; + } + + if + ( + ( parameters.port <= 0 ) + || + ( parameters.port > USBBACK_DRIVER_PORT_COUNT ) + ) + { + error = XENIDC_ERROR_INVALID_PARAMETER; + + goto COMPLETE; + } + + { + int result = usbback_driver_port_reset + ( &backend->port[ parameters.port - 1 ] ); + + if( result < 0 ) + { + error = USBIF_XENIDC_ERROR_NO_DEVICE; + + goto COMPLETE; + } + + { + usbif_reset_transaction_status status; + + memset( &status, 0, sizeof( status ) ); + + status.result = result; + + if + ( + xenidc_local_buffer_reference_copy_in + ( + xenidc_endpoint_transaction_to_status_lbr + ( transaction ), + &status, + sizeof( status ) + ) + != + sizeof( status ) + ) + { + error = XENIDC_ERROR_INVALID_FORMAT; + + goto COMPLETE; + } + } + } + + error = XENIDC_ERROR_SUCCESS; + + COMPLETE: + + xenidc_endpoint_transaction_complete( transaction, error ); + } +} + +static struct usbback_driver_port * + usbback_driver_backend_find_port_by_guest_address +( + struct usbback_driver_backend * backend, + unsigned long guest_address +) +{ + trace(); + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++ ) + { + struct usbback_driver_port * port = &backend->port[ i ]; + + if + ( + usbback_driver_port_match_guest_address( port, guest_address ) + ) + { + return port; + } + } + } + + return NULL; +} + +static void usbback_driver_backend_handle_io +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +) +{ + trace(); + + { + usbif_io_transaction_parameters_header header; + + xenidc_error error; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr( transaction ), + &header, + sizeof( header ) + ) + != + sizeof( header ) + ) + { + error = XENIDC_ERROR_INVALID_FORMAT; + + goto COMPLETE; + } + + { + struct usbback_driver_port * port = + usbback_driver_backend_find_port_by_guest_address + ( backend, header.device_number ); + + if( port == NULL ) + { + error = USBIF_XENIDC_ERROR_NO_DEVICE; + + goto COMPLETE; + } + + usbback_driver_port_handle_io( port, transaction ); + } + + return; + + COMPLETE: + + xenidc_endpoint_transaction_complete( transaction, error ); + } +} + +static void usbback_driver_backend_handle_unlink +( + struct usbback_driver_backend * backend, + xenidc_endpoint_message * message +); + +void usbback_driver_backend_message_handler +( + struct usbback_driver_backend * backend, + xenidc_endpoint_message * message +) +{ + trace(); + + { + usbif_message_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_message_to_message_lbr( message ), + &header, + sizeof( header ) + ) + == + sizeof( header ) + ) + { + switch( header.message_type ) + { + case USBIF_MESSAGE_TYPE_UNLINK: + usbback_driver_backend_handle_unlink( backend, message ); + break; + } + } + + xenidc_callback_success + ( xenidc_endpoint_message_to_callback( message ) ); + } +} + +static void usbback_driver_backend_handle_unlink +( + struct usbback_driver_backend * backend, + xenidc_endpoint_message * message +) +{ + trace(); + + { + usbif_unlink_message_body unlink; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_message_to_message_lbr( message ), + &unlink, + sizeof( unlink ) + ) + == + sizeof( unlink ) + ) + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++ ) + { + struct usbback_driver_port * port = &backend->port[ i ]; + + if + ( + usbback_driver_port_try_unlink + ( port, unlink.unlink_id ) + ) + { + break; + } + } + } + } +} + +void usbback_driver_backend_disconnect + ( struct usbback_driver_backend * backend ) +{ + trace(); + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++ ) + { + usbback_driver_port_disconnect( &backend->port[ i ] ); + } + } +} + +void usbback_driver_backend_claim_port + ( struct usbback_driver_backend * backend, u32 port, char * path ) +{ + trace2( "port: %d, path: %s", port, path ); + + ASSERT + ( + ( port > 0 ) + && + ( port <= USBBACK_DRIVER_PORT_COUNT ) + ); + + usbback_driver_port_claim( &backend->port[ port - 1 ], path ); + + bus_rescan_devices( &usb_bus_type ); +} + +void usbback_driver_backend_release_port + ( struct usbback_driver_backend * backend, u32 port ) +{ + trace1( "port: %d", port ); + + ASSERT + ( + ( port > 0 ) + && + ( port <= USBBACK_DRIVER_PORT_COUNT ) + ); + + usbback_driver_port_release( &backend->port[ port - 1 ] ); + + bus_rescan_devices( &usb_bus_type ); +} + +void usbback_driver_backend_shutdown + ( struct usbback_driver_backend * backend ) +{ + trace(); + + (void)usbback_driver_backend_init_or_exit( backend, NULL, 1 ); + + kfree( backend ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* usbback_driver_backend is the representation in the driver of the */ +/* back-end side of a single front-end to back-end connection. */ +/* One of these objects is used by the usbback_driver to manage each */ +/* usbback_device. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DRIVER_BACKEND_H +#define USBBACK_DRIVER_BACKEND_H + +#include "asm-xen/xenidc.h" +#include "usbback_device.h" + +struct usbback_driver_backend; + +extern struct usbback_driver_backend * usbback_driver_backend_allocate + ( struct usbback_device * device ); + +extern void usbback_driver_backend_connect + ( struct usbback_driver_backend * backend ); + +extern void usbback_driver_backend_transaction_handler +( + struct usbback_driver_backend * backend, + xenidc_endpoint_transaction * transaction +); + +extern void usbback_driver_backend_message_handler +( + struct usbback_driver_backend * backend, + xenidc_endpoint_message * message +); + +extern void usbback_driver_backend_disconnect + ( struct usbback_driver_backend * backend ); + +extern void usbback_driver_backend_claim_port + ( struct usbback_driver_backend * backend, u32 port, char * path ); + +extern void usbback_driver_backend_release_port + ( struct usbback_driver_backend * backend, u32 port ); + +extern void usbback_driver_backend_shutdown + ( struct usbback_driver_backend * backend ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1163 @@ +/*****************************************************************************/ +/* Object which manages a single USB device and handles all the IO */ +/* transactions for that device by assigning usbback_driver_port_resources */ +/* to process them. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on arch/xen/drivers/usbif/backend/main.c, original copyright notice */ +/* follows... */ +/*****************************************************************************/ + +/****************************************************************************** + * arch/xen/drivers/usbif/backend/main.c + * + * Backend for the Xen virtual USB driver - provides an abstraction of a + * USB host controller to the corresponding frontend driver. + * + * by Mark Williamson + * Copyright (c) 2004 Intel Research Cambridge + * Copyright (c) 2004, 2005 Mark Williamson + * + * Based on arch/xen/drivers/blkif/backend/main.c + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + */ + +#include +#include +#include "usbback_assert.h" +#include "usbback_driver_port.h" +#include "usbback_trace.h" + +int usbback_driver_port_class_init( void ) +{ + trace(); + + return usbback_driver_port_resource_class_init(); +} + +void usbback_driver_port_class_exit( void ) +{ + trace(); + + usbback_driver_port_resource_class_exit(); +} + +extern struct usb_driver usbback_driver_usb_driver; + +static DECLARE_RWSEM( usbback_driver_port_list_sem ); + +static LIST_HEAD( usbback_driver_port_list ); + +typedef enum +{ + usbback_driver_port_stimulus_pc, /* Port claim */ + usbback_driver_port_stimulus_pr, /* Port release */ + usbback_driver_port_stimulus_up, /* USB probe */ + usbback_driver_port_stimulus_ud, /* USB disconnect */ + usbback_driver_port_stimulus_cn, /* endpoint connect */ + usbback_driver_port_stimulus_dn, /* endpoint disconnect */ + usbback_driver_port_stimulus_rq, /* Transaction queued/resource freed */ + usbback_driver_port_stimulus_sd, /* Shutdown */ + usbback_driver_port_stimulus_ri, /* test_io transactions idle (reent) */ + usbback_driver_port_stimulus_rr, /* test_io transactions running (reent) */ + usbback_driver_port_stimulus_rp, /* release performed */ +} +usbback_driver_port_stimulus; + +static void usbback_driver_port_handle_stimulus +( + struct usbback_driver_port * port, + usbback_driver_port_stimulus stimulus +); + +static void usbback_driver_port_purge_io_1( void * data ); + +static void usbback_driver_port_perform_release_1( void * data ); + +int usbback_driver_port_init +( + struct usbback_driver_port * port, + struct usbback_driver_backend * backend +) +{ + trace(); + + memset( port, 0, sizeof( *port ) ); + + port->backend = backend; + + INIT_LIST_HEAD( &port->link ); + + spin_lock_init( &port->lock ); + + port->state = usbback_driver_port_state_i; + + INIT_LIST_HEAD( &port->transaction_list ); + INIT_LIST_HEAD( &port->purge_list ); + INIT_LIST_HEAD( &port->free_resource_list ); + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_RESOURCE_COUNT; i++ ) + { + struct usbback_driver_port_resource * resource = + &port->resources[ i ]; + + usbback_driver_port_resource_init( resource, port ); + + list_add( &resource->link, &port->free_resource_list ); + } + } + + xenidc_work_init + ( &port->purge_io_1_work, usbback_driver_port_purge_io_1, port ); + + xenidc_work_init + ( + &port->perform_release_1_work, + usbback_driver_port_perform_release_1, + port + ); + + return 0; +} + +void usbback_driver_port_claim + ( struct usbback_driver_port * port, char * path ) +{ + trace(); + + down_write( &usbback_driver_port_list_sem ); + + strncpy( port->path, path, sizeof( port->path ) ); + + list_add_tail( &port->link, &usbback_driver_port_list ); + + spin_lock_irq( &port->lock ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_pc ); + + spin_unlock_irq( &port->lock ); + + up_write( &usbback_driver_port_list_sem ); +} + +void usbback_driver_port_release( struct usbback_driver_port * port ) +{ + trace(); + + down_write( &usbback_driver_port_list_sem ); + + list_del_init( &port->link ); + + up_write( &usbback_driver_port_list_sem ); + + port->release = 0; + + spin_lock_irq( &port->lock ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_pr ); + + spin_unlock_irq( &port->lock ); + + xenidc_work_until( port->release ); +} + +int usbback_driver_port_probe_usb( struct usb_interface * intf ) +{ + trace(); + + { + int return_value = 0; + + struct usb_device * usbdev = interface_to_usbdev( intf ); + + struct usbback_driver_port * port = NULL; + + printk + ( KERN_INFO "usbback: Probe for usb device %s\n", usbdev->devpath ); + + down_read( &usbback_driver_port_list_sem ); + + { + struct usbback_driver_port * cur = NULL; + + list_for_each_entry( cur, &usbback_driver_port_list, link ) + { + printk + ( + KERN_INFO + "usbback: Testing against configured path %s:", + cur->path + ); + + if + ( + strncmp( cur->path, usbdev->devpath, sizeof( cur->path ) ) + == + 0 + ) + { + printk( " matches.\n" ); + + port = cur; + + break; + } + else + { + printk( " doesn't match.\n" ); + } + } + } + + if( port == NULL ) + { + printk( KERN_INFO "usbback: No match found.\n" ); + + return_value = -ENODEV; + + goto EXIT_NO_PORT; + } + + { + int i; + + 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 ) ) + { + printk + ( + KERN_INFO + "usbback: An interface of the matching device is " + "already in use by another driver.\n" + ); + + return_value = -EBUSY; + + goto EXIT_INTERFACE_ALREADY_CLAIMED; + } + } + } + } + + { + int i; + + for( i = 0; i < usbdev->actconfig->desc.bNumInterfaces; i++ ) + { + struct usb_interface * other_intf = + usb_ifnum_to_if( usbdev, i ); + + if( other_intf != intf ) + { + int error = usb_driver_claim_interface + ( &usbback_driver_usb_driver, other_intf, port ); + + /* We already checked all the interfaces were available. */ + + ASSERT( error == 0 ); + } + } + } + + usbdev = usb_get_dev( usbdev ); + + usb_set_intfdata( intf, port ); + + spin_lock_irq( &port->lock ); + + port->usbdev_is_connected = 1; + + port->usbdev = usbdev; + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_up ); + + spin_unlock_irq( &port->lock ); + + EXIT_INTERFACE_ALREADY_CLAIMED: + + EXIT_NO_PORT: + + up_read( &usbback_driver_port_list_sem ); + + return return_value; + } +} + +void usbback_driver_port_disconnect_usb( struct usb_interface * intf ) +{ + trace(); + + { + struct usbback_driver_port * port = usb_get_intfdata( intf ); + + /* Protect against reentrant call when we release other interfaces. */ + + if( port != NULL ) + { + ASSERT( port->usbdev_is_connected ); + + spin_lock_irq( &port->lock ); + + port->usbdev_is_connected = 0; + + port->enabled = 0; + + port->usb_disconnect = 0; + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_ud ); + + spin_unlock_irq( &port->lock ); + + xenidc_work_until( port->usb_disconnect ); + + { + struct usb_device * usbdev = port->usbdev; + + { + int i; + + for + ( + i = 0; + i < usbdev->actconfig->desc.bNumInterfaces; + i++ + ) + { + struct usb_interface * other_intf = + usb_ifnum_to_if( usbdev, i ); + + if( other_intf != intf ) + { + /* Protect against reentrant call when we */ + /* release other interfaces. */ + + usb_set_intfdata( other_intf, NULL ); + + usb_driver_release_interface + ( &usbback_driver_usb_driver, other_intf ); + } + } + } + + usb_set_intfdata( intf, NULL ); + + usb_put_dev( usbdev ); + } + } + } +} + +int usbback_driver_port_reset( struct usbback_driver_port * port ) +{ + trace(); + + { + int status = -1; + + spin_lock_irq( &port->lock ); + + port->guest_address = 0; + + if( usbback_driver_port_usbdev_is_connected( port ) ) + { + if( port->usbdev->speed == USB_SPEED_LOW ) + { + status = USBIF_RESET_RESULT_LOW_SPEED; + } + else if( port->usbdev->speed == USB_SPEED_HIGH ) + { + status = USBIF_RESET_RESULT_HIGH_SPEED; + } + else + { + status = USBIF_RESET_RESULT_FULL_SPEED; + } + + port->enabled = 1; + } + else + { + port->enabled = 0; + } + + spin_unlock_irq( &port->lock ); + + return status; + } +} + +void usbback_driver_port_set_guest_address + ( struct usbback_driver_port * port, unsigned long guest_address ) +{ + trace(); + + port->guest_address = guest_address; +} + +int usbback_driver_port_match_guest_address + ( struct usbback_driver_port * port, unsigned long guest_address ) +{ + trace(); + + return ( port->enabled && ( guest_address == port->guest_address ) ); +} + +void usbback_driver_port_connect( struct usbback_driver_port * port ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &port->lock, flags ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_cn ); + + spin_unlock_irqrestore( &port->lock, flags ); + } +} + +void usbback_driver_port_handle_io +( + struct usbback_driver_port * port, + xenidc_endpoint_transaction * transaction +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &port->lock, flags ); + + list_add_tail + ( + xenidc_endpoint_transaction_to_link( transaction ), + &port->transaction_list + ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_rq ); + + spin_unlock_irqrestore( &port->lock, flags ); + } +} + +void usbback_driver_port_disconnect( struct usbback_driver_port * port ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &port->lock, flags ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_dn ); + + spin_unlock_irqrestore( &port->lock, flags ); + } +} + +int usbback_driver_port_try_unlink + ( struct usbback_driver_port * port, int unlink_id ) +{ + trace(); + + { + int return_value = 0; + + xenidc_endpoint_transaction * transaction = NULL; + + unsigned long flags; + + spin_lock_irqsave( &port->lock, flags ); + + { + xenidc_endpoint_transaction * temp; + + list_for_each_entry + ( + temp, + &port->transaction_list, + XENIDC_ENDPOINT_TRANSACTION_LINK + ) + { + usbif_io_transaction_parameters_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr + ( transaction ), + &header, + sizeof( header ) + ) + == + sizeof( header ) + ) + { + if( header.unlink_id == unlink_id ) + { + transaction = temp; + + list_del_init + ( + xenidc_endpoint_transaction_to_link( transaction ) + ); + + break; + } + } + } + } + + if( transaction == NULL ) + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_RESOURCE_COUNT; i++ ) + { + struct usbback_driver_port_resource * resource = + &port->resources[ i ]; + + if + ( + usbback_driver_port_resource_try_unlink + ( resource, unlink_id ) + ) + { + return_value = 1; + + break; + } + } + } + + spin_unlock_irqrestore( &port->lock, flags ); + + if( transaction ) + { + xenidc_endpoint_transaction_complete + ( transaction, USBIF_XENIDC_ERROR_UNLINKED ); + + return_value = 1; + } + + return return_value; + } +} + +void usbback_driver_port_shutdown( struct usbback_driver_port * port ) +{ + trace(); + + down_write( &usbback_driver_port_list_sem ); + + list_del_init( &port->link ); + + up_write( &usbback_driver_port_list_sem ); + + port->shutdown = 0; + + spin_lock_irq( &port->lock ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_sd ); + + spin_unlock_irq( &port->lock ); + + xenidc_work_until( port->shutdown ); + + flush_scheduled_work(); +} + +static void usbback_driver_port_invalid_stimulus + ( struct usbback_driver_port * port, usbback_driver_port_stimulus stimulus ); + +static void usbback_driver_port_purge_io( struct usbback_driver_port * port ); + +static void usbback_driver_port_test_io( struct usbback_driver_port * port ); + +static void usbback_driver_port_kick_io( struct usbback_driver_port * port ); + +static void usbback_driver_port_perform_release + ( struct usbback_driver_port * port ); + +static void usbback_driver_port_complete_release + ( struct usbback_driver_port * port ); + +static void usbback_driver_port_complete_usb_disconnect + ( struct usbback_driver_port * port ); + +static void usbback_driver_port_complete_shutdown + ( struct usbback_driver_port * port ); + +/* FIXME: update to add cn and dn */ + +static void usbback_driver_port_handle_stimulus +( + struct usbback_driver_port * port, + usbback_driver_port_stimulus stimulus +) +{ + trace3 + ( + "port %p in state %d received stimulus %d", + port, + port->state, + stimulus + ); + + switch( port->state ) + { + case usbback_driver_port_state_i: + /* Released */ + /* Disconnected */ + /* Idle */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_pc: + port->state = usbback_driver_port_state_i_pc; + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + case usbback_driver_port_stimulus_sd: + usbback_driver_port_complete_shutdown( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc: + /* Claimed */ + /* Disconnected */ + /* Idle */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_pr: + port->state = usbback_driver_port_state_i; + usbback_driver_port_complete_release( port ); + break; + case usbback_driver_port_stimulus_up: + port->state = usbback_driver_port_state_i_pc_up; + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + case usbback_driver_port_stimulus_sd: + port->state = usbback_driver_port_state_i; + usbback_driver_port_complete_shutdown( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up: + /* Claimed */ + /* Probed */ + /* Maybe Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_pr: + port->state = usbback_driver_port_state_i_pc_up_pr; + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_ud: + port->state = usbback_driver_port_state_i_pc_up_ud; + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_kick_io( port ); + break; + case usbback_driver_port_stimulus_sd: + port->state = usbback_driver_port_state_i_pc_up_sd; + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_pr: + /* Releasing */ + /* Probed */ + /* Testing/Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_ud: + port->state = usbback_driver_port_state_i_pc_up_pr_ud; + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_ri: + port->state = usbback_driver_port_state_i_pc_up_pr_ri; + usbback_driver_port_perform_release( port ); + break; + case usbback_driver_port_stimulus_rr: + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_ud: + /* Claimed */ + /* USB Disconnecting */ + /* Testing/Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_pr: + port->state = usbback_driver_port_state_i_pc_up_pr_ud; + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_sd: + port->state = usbback_driver_port_state_i_pc_up_ud_sd; + break; + case usbback_driver_port_stimulus_ri: + port->state = usbback_driver_port_state_i_pc; + usbback_driver_port_complete_usb_disconnect( port ); + break; + case usbback_driver_port_stimulus_rr: + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_sd: + /* Released */ + /* Probed */ + /* Shutting down */ + /* Testing/Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_ud: + port->state = usbback_driver_port_state_i_pc_up_ud_sd; + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_ri: + port->state = usbback_driver_port_state_i_pc_up_sd_ri; + usbback_driver_port_perform_release( port ); + break; + case usbback_driver_port_stimulus_rr: + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_pr_ud: + /* Releasing */ + /* USB Disconnecting */ + /* Testing/Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_ri: + port->state = usbback_driver_port_state_i_pc_up_pr_ud_ri; + usbback_driver_port_perform_release( port ); + usbback_driver_port_complete_usb_disconnect( port ); + break; + case usbback_driver_port_stimulus_rr: + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_pr_ri: + /* Releasing */ + /* Probed */ + /* Idle */ + /* Performing Release */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_ud: + port->state = usbback_driver_port_state_i_pc_up_pr_ud_ri; + usbback_driver_port_complete_usb_disconnect( port ); + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_ud_sd: + /* Released */ + /* USB Disconnecting */ + /* Shutting down */ + /* Testing/Running */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + usbback_driver_port_test_io( port ); + break; + case usbback_driver_port_stimulus_ri: + port->state = usbback_driver_port_state_i_pc_up_ud_sd_ri; + usbback_driver_port_perform_release( port ); + usbback_driver_port_complete_usb_disconnect( port ); + break; + case usbback_driver_port_stimulus_rr: + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_sd_ri: + /* Released */ + /* Probed */ + /* Shutting down */ + /* Idle */ + /* Performing release */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_ud: + port->state = usbback_driver_port_state_i_pc_up_ud_sd_ri; + usbback_driver_port_complete_usb_disconnect( port ); + break; + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_pr_ud_ri: + /* Releasing */ + /* Disconnected */ + /* Idle */ + /* Performing release */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + case usbback_driver_port_stimulus_rp: + port->state = usbback_driver_port_state_i; + usbback_driver_port_complete_release( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + case usbback_driver_port_state_i_pc_up_ud_sd_ri: + /* Released */ + /* Disconnected */ + /* Shutting down */ + /* Idle */ + /* Performing release */ + switch( stimulus ) + { + case usbback_driver_port_stimulus_rq: + usbback_driver_port_purge_io( port ); + break; + case usbback_driver_port_stimulus_rp: + port->state = usbback_driver_port_state_i; + usbback_driver_port_complete_shutdown( port ); + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } + break; + default: + usbback_driver_port_invalid_stimulus( port, stimulus ); + break; + } +} + +static void usbback_driver_port_invalid_stimulus + ( struct usbback_driver_port * port, usbback_driver_port_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "usbback: port %p in state %d" + "received invalid stimulus %d", + port, + port->state, + stimulus + ); +} + +static void usbback_driver_port_purge_io( struct usbback_driver_port * port ) +{ + trace(); + + while( !list_empty( &port->transaction_list ) ) + { + xenidc_endpoint_transaction * transaction = list_entry + ( + port->transaction_list.next, + xenidc_endpoint_transaction, + XENIDC_ENDPOINT_TRANSACTION_LINK + ); + + list_del_init( xenidc_endpoint_transaction_to_link( transaction ) ); + + list_add_tail + ( + xenidc_endpoint_transaction_to_link( transaction ), + &port->purge_list + ); + } + + { + int i; + + for( i = 0; i < USBBACK_DRIVER_PORT_RESOURCE_COUNT; i++ ) + { + struct usbback_driver_port_resource * resource = + &port->resources[ i ]; + + usbback_driver_port_resource_flush_transaction( resource ); + } + } + + (void)xenidc_work_schedule( &port->purge_io_1_work ); +} + +static void usbback_driver_port_purge_io_1( void * data ) +{ + trace(); + + { + struct usbback_driver_port * port = (struct usbback_driver_port *)data; + + spin_lock_irq( &port->lock ); + + while( !list_empty( &port->purge_list ) ) + { + xenidc_endpoint_transaction * transaction = list_entry + ( + port->purge_list.next, + xenidc_endpoint_transaction, + XENIDC_ENDPOINT_TRANSACTION_LINK + ); + + list_del_init + ( xenidc_endpoint_transaction_to_link( transaction ) ); + + spin_unlock_irq( &port->lock ); + + xenidc_endpoint_transaction_complete + ( transaction, USBIF_XENIDC_ERROR_UNLINKED ); + + spin_lock_irq( &port->lock ); + } + + spin_unlock_irq( &port->lock ); + } +} + +static void usbback_driver_port_test_io( struct usbback_driver_port * port ) +{ + trace(); + + if( port->current_transactions == 0 ) + { + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_ri ); + } + else + { + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_rr ); + } +} + +static void usbback_driver_port_kick_io( struct usbback_driver_port * port ) +{ + trace(); + + while + ( + ( !list_empty( &port->transaction_list ) ) + && + ( !list_empty( &port->free_resource_list ) ) + ) + { + xenidc_endpoint_transaction * transaction = list_entry + ( + port->transaction_list.next, + xenidc_endpoint_transaction, + XENIDC_ENDPOINT_TRANSACTION_LINK + ); + + struct usbback_driver_port_resource * resource = list_entry + ( + port->free_resource_list.next, + struct usbback_driver_port_resource, + link + ); + + list_del_init( xenidc_endpoint_transaction_to_link( transaction ) ); + + list_del_init( &resource->link ); + + port->current_transactions++; + + usbback_driver_port_resource_start_io( resource, transaction ); + } +} + +void usbback_driver_port_resource_completed_io +( + struct usbback_driver_port * port, + struct usbback_driver_port_resource * resource +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &port->lock, flags ); + + list_add( &resource->link, &port->free_resource_list ); + + port->current_transactions--; + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_rq ); + + spin_unlock_irqrestore( &port->lock, flags ); + } +} + +static void usbback_driver_port_perform_release + ( struct usbback_driver_port * port ) +{ + trace(); + + usb_get_dev( port->usbdev ); + + { + int scheduled = xenidc_work_schedule( &port->perform_release_1_work ); + + ASSERT( scheduled ); + } +} + +static void usbback_driver_port_perform_release_1( void * data ) +{ + trace(); + + { + struct usbback_driver_port * port = (struct usbback_driver_port *)data; + + usb_lock_device( port->usbdev ); + + down_write( &usb_bus_type.subsys.rwsem ); + + if( port->usbdev_is_connected ) + { + usb_driver_release_interface + ( + &usbback_driver_usb_driver, + usb_ifnum_to_if( port->usbdev, 0 ) + ); + } + + up_write( &usb_bus_type.subsys.rwsem ); + + usb_unlock_device( port->usbdev ); + + usb_put_dev( port->usbdev ); + + spin_lock_irq( &port->lock ); + + usbback_driver_port_handle_stimulus + ( port, usbback_driver_port_stimulus_rp ); + + spin_unlock_irq( &port->lock ); + } +} + +static void usbback_driver_port_complete_release + ( struct usbback_driver_port * port ) +{ + trace(); + + port->release = 1; + + xenidc_work_wake_up(); +} + +static void usbback_driver_port_complete_usb_disconnect + ( struct usbback_driver_port * port ) +{ + trace(); + + port->usb_disconnect = 1; + + xenidc_work_wake_up(); +} + +static void usbback_driver_port_complete_shutdown + ( struct usbback_driver_port * port ) +{ + trace(); + + port->shutdown = 1; + + xenidc_work_wake_up(); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,149 @@ +/*****************************************************************************/ +/* Object which manages a single USB device and handles all the */ +/* io transactions for that device by assigning */ +/* usbback_driver_port_resources to process them. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DRIVER_PORT_H +#define USBBACK_DRIVER_PORT_H + +#include +#include +#include "usbback_assert.h" +#include "usbback_driver_backend.h" +#include "usbback_driver_port_resource.h" +#include "usbback_trace.h" + +int usbback_driver_port_class_init( void ); + +void usbback_driver_port_class_exit( void ); + +int usbback_driver_port_init +( + struct usbback_driver_port * port, + struct usbback_driver_backend * backend +); + +void usbback_driver_port_claim + ( struct usbback_driver_port * port, char * path ); + +void usbback_driver_port_release + ( struct usbback_driver_port * port ); + +int usbback_driver_port_probe_usb( struct usb_interface * intf ); + +void usbback_driver_port_disconnect_usb( struct usb_interface * intf ); + +/* On success, usbback_driver_port_reset returns */ +/* USBIF_RESET_RESULT_FULL/LOW/HIGH_SPEED */ + +int usbback_driver_port_reset( struct usbback_driver_port * port ); + +void usbback_driver_port_set_guest_address + ( struct usbback_driver_port * port, unsigned long guest_address ); + +int usbback_driver_port_match_guest_address + ( struct usbback_driver_port * port, unsigned long guest_address ); + +void usbback_driver_port_connect + ( struct usbback_driver_port * port ); + +void usbback_driver_port_handle_io +( + struct usbback_driver_port * port, + xenidc_endpoint_transaction * transaction +); + +void usbback_driver_port_disconnect + ( struct usbback_driver_port * port ); + +int usbback_driver_port_try_unlink + ( struct usbback_driver_port * port, int unlink_id ); + +void usbback_driver_port_shutdown( struct usbback_driver_port * port ); + +typedef enum +{ + usbback_driver_port_state_i, + usbback_driver_port_state_i_pc, + usbback_driver_port_state_i_pc_up, + usbback_driver_port_state_i_pc_up_pr, + usbback_driver_port_state_i_pc_up_ud, + usbback_driver_port_state_i_pc_up_sd, + usbback_driver_port_state_i_pc_up_pr_ud, + usbback_driver_port_state_i_pc_up_pr_ri, + usbback_driver_port_state_i_pc_up_ud_sd, + usbback_driver_port_state_i_pc_up_sd_ri, + usbback_driver_port_state_i_pc_up_pr_ud_ri, + usbback_driver_port_state_i_pc_up_ud_sd_ri +} +usbback_driver_port_state; + +#define USBBACK_DRIVER_PORT_RESOURCE_COUNT 4 + +struct usbback_driver_port +{ + struct usbback_driver_backend * backend; + struct list_head link; + char path[ 16 ]; /* FIXME: Magic number. */ + struct usb_device * usbdev; + spinlock_t lock; + usbback_driver_port_state state; + int usbdev_is_connected; + int enabled; + unsigned long guest_address; + struct list_head transaction_list; + struct list_head purge_list; + struct list_head free_resource_list; + unsigned long current_transactions; + struct usbback_driver_port_resource resources + [ USBBACK_DRIVER_PORT_RESOURCE_COUNT ]; + xenidc_work purge_io_1_work; + xenidc_work perform_release_1_work; + int release; + int usb_disconnect; + int shutdown; +}; + +static inline struct usb_device * usbback_driver_port_query_usbdev + ( struct usbback_driver_port * port ) +{ + trace(); + + ASSERT( port->usbdev != NULL ); + + return port->usbdev; +} + +static inline int usbback_driver_port_usbdev_is_connected + ( struct usbback_driver_port * port ) +{ + /* trace(); */ + + return port->usbdev_is_connected; +} + +void usbback_driver_port_resource_completed_io +( + struct usbback_driver_port * port, + struct usbback_driver_port_resource * resource +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1228 @@ +/*****************************************************************************/ +/* Resource which processes a usbback_transaction by translating it into */ +/* an URB and submitting it to the USB stack. */ +/* */ +/* 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 "usbback_assert.h" +#include "usbback_driver_port.h" +#include "usbback_driver_port_resource.h" +#include "usbback_trace.h" + +static XENIDC_CALLBACK_SERIALISER + ( usbback_driver_port_resource_submit_urb_serialiser ); + +static xenidc_rbr_mapper_pool * usbback_driver_port_resource_rbr_mapper_pool; + +int usbback_driver_port_resource_class_init( void ) +{ + trace(); + + usbback_driver_port_resource_rbr_mapper_pool = + xenidc_allocate_rbr_mapper_pool( USBIF_MAX_PAGES_PER_REQUEST ); + + return ( usbback_driver_port_resource_rbr_mapper_pool != NULL ) ? + 0 : -ENOMEM; +} + +void usbback_driver_port_resource_class_exit( void ) +{ + trace(); + + xenidc_free_rbr_mapper_pool + ( usbback_driver_port_resource_rbr_mapper_pool ); +} + +typedef enum +{ + usbback_driver_port_resource_stimulus_st, /* Start IO */ + usbback_driver_port_resource_stimulus_tu, /* Try unlink */ + usbback_driver_port_resource_stimulus_ms, /* Map success */ + usbback_driver_port_resource_stimulus_mf, /* Map failure */ + usbback_driver_port_resource_stimulus_us, /* URB submitted */ + usbback_driver_port_resource_stimulus_un, /* URB not submitted */ + usbback_driver_port_resource_stimulus_uc, /* URB completed */ + usbback_driver_port_resource_stimulus_ul, /* Unlink completed */ +} +usbback_driver_port_resource_stimulus; + +static void usbback_driver_port_resource_handle_stimulus +( + struct usbback_driver_port_resource * resource, + usbback_driver_port_resource_stimulus stimulus +); + +static void usbback_driver_port_resource_submit_urb_1 + ( xenidc_callback * callback ); + +static void usbback_driver_port_resource_unlink_urb_1( void * data ); + +static void usbback_driver_port_resource_map_callback + ( xenidc_callback * callback ); + +static void usbback_driver_port_resource_unmap_callback + ( xenidc_callback * callback ); + +void usbback_driver_port_resource_init +( + struct usbback_driver_port_resource * resource, + struct usbback_driver_port * port +) +{ + trace(); + + resource->port = port; + + INIT_LIST_HEAD( &resource->link ); + + spin_lock_init( &resource->lock ); + + resource->state = usbback_driver_port_resource_state_i; + + resource->transaction = NULL; + + xenidc_map_rbr_request_element_init( &resource->request_element[ 0 ] ); + xenidc_map_rbr_request_element_init( &resource->request_element[ 1 ] ); + + xenidc_reserve_and_map_rbr_request_init + ( + &resource->reserve_and_map_rbr_request, + usbback_driver_port_resource_map_callback, + usbback_driver_port_resource_unmap_callback + ); + + resource->rbrs_mapped = 0; + + xenidc_callback_init + ( + &resource->submit_urb_1_callback, + usbback_driver_port_resource_submit_urb_1 + ); + + xenidc_work_init + ( + &resource->unlink_urb_1_work, + usbback_driver_port_resource_unlink_urb_1, + resource + ); +} + +void usbback_driver_port_resource_start_io +( + struct usbback_driver_port_resource * resource, + xenidc_endpoint_transaction * transaction +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + resource->transaction_error = XENIDC_ERROR_SUCCESS; + resource->transaction_status_length = 0; + + resource->transaction = transaction; + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_st ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +int usbback_driver_port_resource_try_unlink + ( struct usbback_driver_port_resource * resource, int unlink_id ) +{ + trace(); + + { + int return_value = 0; + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + if( resource->transaction != NULL ) + { + usbif_io_transaction_parameters_header header; + + if + ( + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr + ( resource->transaction ), + &header, + sizeof( header ) + ) + == + sizeof( header ) + ) + && + ( header.unlink_id == unlink_id ) + ) + { + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_tu ); + + return_value = 1; + } + } + + spin_unlock_irqrestore( &resource->lock, flags ); + + return return_value; + } +} + +void usbback_driver_port_resource_flush_transaction + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + if( resource->transaction != NULL ) + { + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_tu ); + } + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void usbback_driver_port_resource_invalid_stimulus +( + struct usbback_driver_port_resource * resource, + usbback_driver_port_resource_stimulus stimulus +); + +static void usbback_driver_port_resource_map_buffer + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_abort_map + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_submit_urb + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_unlink_urb + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_set_error + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_complete + ( struct usbback_driver_port_resource * resource ); + +static void usbback_driver_port_resource_handle_stimulus +( + struct usbback_driver_port_resource * resource, + usbback_driver_port_resource_stimulus stimulus +) +{ + trace3 + ( + "port resource %p in state %d received stimulus %d", + resource, + resource->state, + stimulus + ); + + switch( resource->state ) + { + case usbback_driver_port_resource_state_i: + /* Idle */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_st: + resource->state = usbback_driver_port_resource_state_i_st; + usbback_driver_port_resource_map_buffer( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st: + /* Handling request */ + /* map_buffer outstanding */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + resource->state = usbback_driver_port_resource_state_i_st_tu; + usbback_driver_port_resource_abort_map( resource ); + break; + case usbback_driver_port_resource_stimulus_ms: + resource->state = usbback_driver_port_resource_state_i_st_ms; + usbback_driver_port_resource_submit_urb( resource ); + break; + case usbback_driver_port_resource_stimulus_mf: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_tu: + /* Handling request */ + /* map_buffer outstanding */ + /* unlinking */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + break; + case usbback_driver_port_resource_stimulus_ms: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_set_error( resource ); + usbback_driver_port_resource_complete( resource ); + break; + case usbback_driver_port_resource_stimulus_mf: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms: + /* Handling request */ + /* submit_urb outstanding */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + resource->state = usbback_driver_port_resource_state_i_st_ms_tu; + break; + case usbback_driver_port_resource_stimulus_us: + resource->state = usbback_driver_port_resource_state_i_st_ms_us; + break; + case usbback_driver_port_resource_stimulus_un: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + case usbback_driver_port_resource_stimulus_uc: + resource->state = usbback_driver_port_resource_state_i_st_ms_uc; + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms_tu: + /* Handling request */ + /* submit_urb outstanding */ + /* unlinking */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + break; + case usbback_driver_port_resource_stimulus_us: + resource->state = usbback_driver_port_resource_state_i_st_ms_tu_us; + usbback_driver_port_resource_unlink_urb( resource ); + break; + case usbback_driver_port_resource_stimulus_un: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + case usbback_driver_port_resource_stimulus_uc: + resource->state = usbback_driver_port_resource_state_i_st_ms_uc; + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms_us: + /* Handling request */ + /* URB submitted */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + resource->state = usbback_driver_port_resource_state_i_st_ms_tu_us; + usbback_driver_port_resource_unlink_urb( resource ); + break; + case usbback_driver_port_resource_stimulus_uc: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms_uc: + /* Handling request */ + /* submit_urb outstanding */ + /* URB completed */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + break; + case usbback_driver_port_resource_stimulus_us: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms_tu_us: + /* Handling request */ + /* URB submitted */ + /* unlink_urb outstanding */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + break; + case usbback_driver_port_resource_stimulus_uc: + case usbback_driver_port_resource_stimulus_ul: + resource->state = + usbback_driver_port_resource_state_i_st_ms_tu_us_uc; + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case usbback_driver_port_resource_state_i_st_ms_tu_us_uc: + /* Handling request */ + /* URB submitted or unlink_urb outstanding */ + switch( stimulus ) + { + case usbback_driver_port_resource_stimulus_tu: + break; + case usbback_driver_port_resource_stimulus_uc: + case usbback_driver_port_resource_stimulus_ul: + resource->state = usbback_driver_port_resource_state_i; + usbback_driver_port_resource_complete( resource ); + break; + default: + usbback_driver_port_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + default: + usbback_driver_port_resource_invalid_stimulus( resource, stimulus ); + break; + } +} + +static void usbback_driver_port_resource_invalid_stimulus +( + struct usbback_driver_port_resource * resource, + usbback_driver_port_resource_stimulus stimulus +) +{ + trace(); + + printk + ( + KERN_ERR "usbback: port resource %p in state %d" + "received invalid stimulus %d", + resource, + resource->state, + stimulus + ); +} + +static void usbback_driver_port_resource_map_buffer + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + { + usbif_io_transaction_parameters_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr + ( resource->transaction ), + &header, + sizeof( header ) + ) + != + sizeof( header ) + ) + { + goto FORMAT_ERROR; + } + + xenidc_map_rbr_request_element_ensure_removed + ( &resource->request_element[ 0 ] ); + + xenidc_map_rbr_request_element_ensure_removed + ( &resource->request_element[ 1 ] ); + + xenidc_reserve_and_map_rbr_request_add_element + ( + &resource->reserve_and_map_rbr_request, + &resource->request_element[ 0 ] + ); + + xenidc_map_rbr_request_element_set_rbr + ( + &resource->request_element[ 0 ], + header.rbr + ); + + if + ( + header.io_transaction_type + == + USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS + ) + { + usbif_isochronous_io_transaction_parameters parameters; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr + ( resource->transaction ), + ¶meters, + sizeof( parameters ) + ) + != + sizeof( parameters ) + ) + { + goto FORMAT_ERROR; + } + + xenidc_reserve_and_map_rbr_request_add_element + ( + &resource->reserve_and_map_rbr_request, + &resource->request_element[ 1 ] + ); + + xenidc_map_rbr_request_element_set_rbr + ( + &resource->request_element[ 1 ], + parameters.schedule_rbr + ); + } + + xenidc_rbr_mapper_pool_reserve_and_map_rbrs + ( + usbback_driver_port_resource_rbr_mapper_pool, + &resource->reserve_and_map_rbr_request + ); + } + + return; + + FORMAT_ERROR: + + resource->transaction_error = XENIDC_ERROR_INVALID_FORMAT; + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_mf ); +} + +static void usbback_driver_port_resource_abort_map + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + xenidc_rbr_mapper_pool_abort_reserve_and_map_rbrs + ( + usbback_driver_port_resource_rbr_mapper_pool, + &resource->reserve_and_map_rbr_request + ); +} + +static void usbback_driver_port_resource_map_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_reserve_and_map_rbr_request * request = + xenidc_reserve_and_map_rbr_request_map_callback_to( callback ); + + struct usbback_driver_port_resource * resource = container_of + ( + request, + struct usbback_driver_port_resource, + reserve_and_map_rbr_request + ); + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + resource->transaction_error = + xenidc_callback_query_error( callback ); + + if + ( + resource->transaction_error + == + XENIDC_ERROR_SUCCESS + ) + { + resource->rbrs_mapped = 1; + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_ms ); + } + else + { + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_mf ); + } + + spin_unlock_irqrestore( &resource->lock, flags ); + } + } +} + +static void usbback_driver_port_resource_submit_urb + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + xenidc_callback_serialiser_complete_callback + ( + &usbback_driver_port_resource_submit_urb_serialiser, + &resource->submit_urb_1_callback, + 0 + ); +} + +static void usbback_driver_port_resource_end_io + ( struct urb * urb, struct pt_regs * regs ); + +static int check_iso_schedule( struct urb * urb ) +{ + int i; + + unsigned long total_length = 0; + + for( i = 0; i < urb->number_of_packets; i++ ) + { + struct usb_iso_packet_descriptor * desc = &urb->iso_frame_desc[ i ]; + + total_length += desc->length; + + if + ( + ( desc->offset > urb->transfer_buffer_length ) + || + ( desc->length > ( urb->transfer_buffer_length - desc->offset ) ) + || + ( total_length > urb->transfer_buffer_length ) + ) + { + return -EINVAL; + } + } + + return 0; +} + +static void usbback_driver_port_resource_submit_urb_1 + ( xenidc_callback * callback ) +{ + trace(); + + { + struct usbback_driver_port_resource * resource = container_of + ( + callback, + struct usbback_driver_port_resource, + submit_urb_1_callback + ); + + usbif_io_transaction_parameters io_parameters; + + { + xenidc_buffer_byte_count io_parameters_byte_count = + xenidc_local_buffer_reference_copy_out + ( + xenidc_endpoint_transaction_to_parameters_lbr + ( resource->transaction ), + &io_parameters, + sizeof( io_parameters ) + ); + + if( io_parameters_byte_count < sizeof( io_parameters.header ) ) + { + resource->transaction_error = XENIDC_ERROR_INVALID_FORMAT; + + goto FORMAT_ERROR; + } + + if + ( + io_parameters.header.io_transaction_type + >= + USBIF_IO_TRANSACTION_TYPE_LIMIT + ) + { + resource->transaction_error = XENIDC_ERROR_INVALID_PARAMETER; + + goto FORMAT_ERROR; + } + + if + ( + io_parameters_byte_count + < + usbif_io_parameters_byte_count + [ io_parameters.header.io_transaction_type ] + ) + { + resource->transaction_error = XENIDC_ERROR_INVALID_FORMAT; + + goto FORMAT_ERROR; + } + } + + if + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_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 ) + { + usbback_driver_port_set_guest_address + ( resource->port, le16_to_cpu( ctrl->wValue ) ); + + goto HANDLED_SPECIAL_CASE; + } + else if( ctrl->bRequest == USB_REQ_SET_CONFIGURATION ) + { + /* FIXME: what to do for set configuration? */ + + goto HANDLED_SPECIAL_CASE; + } + } + } + + { + struct urb * urb = resource->urb = usb_alloc_urb + ( + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS + ) + ? io_parameters.isochronous.packet_count : 0, + GFP_KERNEL + ); + + static const unsigned int pipe_type + [ USBIF_IO_TRANSACTION_TYPE_LIMIT ] = + { + [ USBIF_IO_TRANSACTION_TYPE_CONTROL ] = PIPE_CONTROL, + [ USBIF_IO_TRANSACTION_TYPE_BULK ] = PIPE_BULK, + [ USBIF_IO_TRANSACTION_TYPE_INTERRUPT ] = PIPE_INTERRUPT, + [ USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS ] = PIPE_ISOCHRONOUS + }; + + unsigned int pipe = + ( + ( + ( + ( + io_parameters.header.direction + == + USBIF_IO_TRANSACTION_DIRECTION_OUT + ) + ? USB_DIR_OUT : USB_DIR_IN + ) + << 7 + ) + | + ( + (unsigned int)usbback_driver_port_query_usbdev + ( resource->port )->devnum + << + 8 + ) + | + ( (unsigned int)io_parameters.header.endpoint << 15 ) + | + ( pipe_type[ io_parameters.header.io_transaction_type ] << 30 ) + ); + + if( urb == NULL ) + { + /* FIXME: better URB resource management should let us queue */ + /* for an URB and ISO resources from a reserved pool which */ + /* would eliminate the possibility of failure at this point. */ + + resource->transaction_error = + XENIDC_ERROR_IMPLEMENTATION_DEFICIENCY; + + goto NO_URB; + } + + if + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_TYPE_CONTROL + ) + { + memcpy + ( + resource->setup, + io_parameters.control.setup, + sizeof( resource->setup ) + ); + + usb_fill_control_urb + ( + urb, + usbback_driver_port_query_usbdev( resource->port ), + pipe, + resource->setup, + resource->request_element[ 0 ].mapping, + xenidc_remote_buffer_reference_query_byte_count + ( &resource->request_element[ 0 ].rbr ), + usbback_driver_port_resource_end_io, + resource + ); + } + else if + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_TYPE_BULK + ) + { + usb_fill_bulk_urb + ( + urb, + usbback_driver_port_query_usbdev( resource->port ), + pipe, + resource->request_element[ 0 ].mapping, + xenidc_remote_buffer_reference_query_byte_count + ( &resource->request_element[ 0 ].rbr ), + usbback_driver_port_resource_end_io, + resource + ); + } + else if + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_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, + usbback_driver_port_query_usbdev( resource->port ), + pipe, + resource->request_element[ 0 ].mapping, + xenidc_remote_buffer_reference_query_byte_count + ( &resource->request_element[ 0 ].rbr ), + usbback_driver_port_resource_end_io, + resource, + 1 /* For the time being... */ + ); + + /* ...now set the real value. */ + urb->interval = io_parameters.interrupt.interval; + } + else + { + ASSERT + ( + io_parameters.header.io_transaction_type + == + USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS + ); + + /* FIXME: where's usb_fill_isoc_urb ?!? */ + + spin_lock_init(&urb->lock); + urb->dev = usbback_driver_port_query_usbdev( resource->port ); + urb->pipe = pipe; + urb->transfer_buffer = resource->request_element[ 0 ].mapping; + urb->transfer_buffer_length = + xenidc_remote_buffer_reference_query_byte_count + ( &resource->request_element[ 0 ].rbr ); + urb->complete = usbback_driver_port_resource_end_io; + urb->context = resource; + 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 + ( + xenidc_remote_buffer_reference_query_byte_count + ( &resource->request_element[ 1 ].rbr ) + < + ( + io_parameters.isochronous.packet_count + * + sizeof( usbif_isochronous_io_schedule_element ) + ) + ) + { + resource->transaction_error = XENIDC_ERROR_INVALID_FORMAT; + + goto SCHEDULE_ERROR; + } + + { + usbif_isochronous_io_schedule_element * schedule_element = + resource->request_element[ 1 ].mapping; + + usbif_isochronous_io_schedule_element aligned_element; + + int i; + + 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 ) ) + { + resource->transaction_error = + XENIDC_ERROR_INVALID_PARAMETER; + + goto SCHEDULE_ERROR; + } + } + + /* On the backend, all unlinks are asynchronous. */ + + urb->transfer_flags |= URB_ASYNC_UNLINK; + + 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; + } + + if( usb_submit_urb( urb, GFP_KERNEL ) != 0 ) + { + printk( KERN_WARNING "URB %p submission failed.\n", urb ); + + /* FIXME: non-specific failure return. */ + resource->transaction_error = USBIF_XENIDC_ERROR_FAILURE; + + goto URB_ERROR; + } + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_us ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } + + return; + + URB_ERROR: + + SCHEDULE_ERROR: + + usb_free_urb( urb ); + } + + NO_URB: + + HANDLED_SPECIAL_CASE: + + FORMAT_ERROR: + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_un ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } + } +} + +static void usbback_driver_port_resource_end_io + ( struct urb * urb, struct pt_regs * regs ) +{ + trace(); + + { + struct usbback_driver_port_resource * resource = urb->context; + + if( urb->status != 0 ) + { + printk + ( KERN_WARNING "URB %p failed. Status %d\n", urb, urb->status ); + + resource->transaction_error = USBIF_XENIDC_ERROR_FAILURE; + } + + resource->transaction_status_length = urb->actual_length; + + if( usb_pipetype( urb->pipe ) == PIPE_ISOCHRONOUS ) + { + usbif_isochronous_io_schedule_element * schedule_element = + resource->request_element[ 1 ].mapping; + + 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 = + ( urb->iso_frame_desc[ i ].status == 0 ) ? + XENIDC_ERROR_SUCCESS : USBIF_XENIDC_ERROR_FAILURE; + + memcpy + ( + schedule_element++, + &aligned_element, + sizeof( *schedule_element ) + ); + } + } + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_uc ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } + } + + usb_free_urb( urb ); +} + +static void usbback_driver_port_resource_unlink_urb + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + usb_get_urb( resource->urb ); + + { + int scheduled = xenidc_work_schedule( &resource->unlink_urb_1_work ); + + ASSERT( scheduled ); + } +} + +static void usbback_driver_port_resource_unlink_urb_1( void * data ) +{ + trace(); + + { + struct usbback_driver_port_resource * resource = + (struct usbback_driver_port_resource *)data; + + usb_unlink_urb( resource->urb ); + + usb_put_urb( resource->urb ); + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbback_driver_port_resource_handle_stimulus + ( resource, usbback_driver_port_resource_stimulus_ul ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } + } +} + +static void usbback_driver_port_resource_set_error + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + resource->transaction_error = USBIF_XENIDC_ERROR_FAILURE; +} + +static void usbback_driver_port_resource_complete + ( struct usbback_driver_port_resource * resource ) +{ + trace(); + + resource->completing_transaction = resource->transaction; + resource->transaction = NULL; + + if( resource->rbrs_mapped ) + { + resource->rbrs_mapped = 0; + + xenidc_rbr_mapper_pool_unmap_and_unreserve_rbrs + ( + usbback_driver_port_resource_rbr_mapper_pool, + &resource->reserve_and_map_rbr_request + ); + } + else + { + xenidc_callback_success + ( + xenidc_reserve_and_map_rbr_request_to_unmap_callback + ( &resource->reserve_and_map_rbr_request ) + ); + } +} + +static void usbback_driver_port_resource_unmap_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_reserve_and_map_rbr_request * request = + xenidc_reserve_and_map_rbr_request_unmap_callback_to( callback ); + + struct usbback_driver_port_resource * resource = container_of + ( + request, + struct usbback_driver_port_resource, + reserve_and_map_rbr_request + ); + + usbif_io_transaction_status status; + + status.length = resource->transaction_status_length; + + if + ( + xenidc_local_buffer_reference_copy_in + ( + xenidc_endpoint_transaction_to_status_lbr + ( resource->completing_transaction ), + &status, + sizeof( status ) + ) + != + sizeof( status ) + ) + { + resource->transaction_error = XENIDC_ERROR_INVALID_FORMAT; + } + + xenidc_endpoint_transaction_complete + ( resource->completing_transaction, resource->transaction_error ); + + usbback_driver_port_resource_completed_io( resource->port, resource ); + } +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,87 @@ +/*****************************************************************************/ +/* Resource which processes an io transaction by translating it into an URB */ +/* and submitting it to the USB stack. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_DRIVER_PORT_RESOURCE_H +#define USBBACK_DRIVER_PORT_RESOURCE_H + +#include +#include "usbback_device.h" + +int usbback_driver_port_resource_class_init( void ); + +void usbback_driver_port_resource_class_exit( void ); + +struct usbback_driver_port; +struct usbback_driver_port_resource; + +void usbback_driver_port_resource_init +( + struct usbback_driver_port_resource * resource, + struct usbback_driver_port * port +); + +void usbback_driver_port_resource_start_io +( + struct usbback_driver_port_resource * resource, + xenidc_endpoint_transaction * transaction +); + +int usbback_driver_port_resource_try_unlink + ( struct usbback_driver_port_resource * resource, int unlink_id ); + +void usbback_driver_port_resource_flush_transaction + ( struct usbback_driver_port_resource * resource ); + +typedef enum +{ + usbback_driver_port_resource_state_i, + usbback_driver_port_resource_state_i_st, + usbback_driver_port_resource_state_i_st_tu, + usbback_driver_port_resource_state_i_st_ms, + usbback_driver_port_resource_state_i_st_ms_tu, + usbback_driver_port_resource_state_i_st_ms_us, + usbback_driver_port_resource_state_i_st_ms_uc, + usbback_driver_port_resource_state_i_st_ms_tu_us, + usbback_driver_port_resource_state_i_st_ms_tu_us_uc +} +usbback_driver_port_resource_state; + +struct usbback_driver_port_resource +{ + struct usbback_driver_port * port; + struct list_head link; + spinlock_t lock; + usbback_driver_port_resource_state state; + xenidc_error transaction_error; + unsigned long transaction_status_length; + xenidc_endpoint_transaction * transaction; + xenidc_endpoint_transaction * completing_transaction; + xenidc_reserve_and_map_rbr_request reserve_and_map_rbr_request; + xenidc_map_rbr_request_element request_element[ 2 ]; + int rbrs_mapped; + struct urb * urb; + u8 setup[ 8 ]; + xenidc_callback submit_urb_1_callback; + xenidc_work unlink_urb_1_work; +}; + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_module.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_module.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,83 @@ +/*****************************************************************************/ +/* Back-end module for Xen USB split driver. */ +/* */ +/* 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 "usbback_driver.h" +#include "usbback_device.h" +#include "usbback_trace.h" + +static int usbback_module_init_or_exit( int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + if( ( return_value = usbback_driver_init() ) != 0 ) + { + goto EXIT_NO_DRIVER; + } + + if( ( return_value = usbback_device_class_init() ) != 0 ) + { + goto EXIT_NO_DEVICE_CLASS; + } + + return 0; + + EXIT: + + usbback_device_class_exit(); + + EXIT_NO_DEVICE_CLASS: + + usbback_driver_exit(); + + EXIT_NO_DRIVER: + + return return_value; + } +} + +static int __init usbback_module_init( void ) +{ + trace(); + + return usbback_module_init_or_exit( 0 ); +} + +static void __exit usbback_module_exit( void ) +{ + trace(); + + (void)usbback_module_init_or_exit( 1 ); +} + +module_init( usbback_module_init ); +module_exit( usbback_module_exit ); + +MODULE_LICENSE( "GPL" ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,54 @@ +/*****************************************************************************/ +/* Simple trace macros */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBBACK_TRACE_H +#define USBBACK_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_BACKEND_TRACE + +#define trace0( format ) \ +printk( KERN_INFO "usbback %s:" format "\n", __PRETTY_FUNCTION__ ) + +#define trace1( format, a0 ) \ +printk( KERN_INFO "usbback %s:" format "\n", __PRETTY_FUNCTION__, a0 ) + +#define trace2( format, a0, a1 ) \ +printk( KERN_INFO "usbback %s:" format "\n", __PRETTY_FUNCTION__, a0, a1 ) + +#define trace3( format, a0, a1, a2 ) \ +printk( KERN_INFO "usbback %s:" format "\n", __PRETTY_FUNCTION__, a0, a1, a2 ) + +#define trace() trace0( "" ) + +#else + +#define trace0( format ) +#define trace1( format,a0 ) +#define trace2( format,a0, a1 ) +#define trace3( format,a0, a1, a2 ) +#define trace() + +#endif + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/Makefile Sun Oct 30 16:03:34 2005 @@ -0,0 +1,7 @@ +obj-$(CONFIG_XEN_USBDEV_FRONTEND) += usbfront.o + +usbfront-objs := \ +usbfront_hcd_resource.o \ +usbfront_driver.o \ +usbfront_device.o \ +usbfront_module.o diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_assert.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_assert.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,44 @@ +/*****************************************************************************/ +/* An implementation of the ASSERT macro. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_ASSERT_H +#define USBFRONT_ASSERT_H + +#include + +static inline void assert_failed + ( const char * function, int line, const char * statement ) +{ + printk + ( + KERN_ERR "usbfront assert failed: %s line %d, statement %s\n", + function, + line, + statement + ); + + BUG(); +} + +#define ASSERT( S ) \ +( ( S ) ? ( (void)0 ) : assert_failed( __PRETTY_FUNCTION__, __LINE__, #S ) ) + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1314 @@ +/*****************************************************************************/ +/* usbfront_device is a device which represents a connection to a back-end. */ +/* The intent was for it to have an interface like a hardware USB host */ +/* controller device. */ +/* */ +/* 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 +*/ + +/* FIXME: check all port numbering is one based */ + +#include +#include +#include +#include "usbfront_assert.h" +#include "usbfront_device.h" +#include "usbfront_driver.h" +#include "usbfront_trace.h" + +#define USBFRONT_DEVICE_PORT_COUNT 7 + +typedef enum +{ + usbfront_device_state_i, + usbfront_device_state_i_cn, + usbfront_device_state_i_cn_dn, + usbfront_device_state_i_cn_ps, + usbfront_device_state_i_cn_pf, + usbfront_device_state_i_cn_dn_ps, + usbfront_device_state_i_cn_ps_dn, + usbfront_device_state_i_cn_ps_pc, + usbfront_device_state_i_cn_ps_dn_rc +} +usbfront_device_state; + +typedef enum +{ + usbfront_device_stimulus_cn, /* Endpoint connect. */ + usbfront_device_stimulus_dn, /* Endpoint disconnect. */ + usbfront_device_stimulus_ps, /* Probe driver success. */ + usbfront_device_stimulus_pf, /* Probe driver failure. */ + usbfront_device_stimulus_rc, /* Remove driver complete. */ + usbfront_device_stimulus_pt, /* Polling tick. */ + usbfront_device_stimulus_pc /* Probe complete. */ +} +usbfront_device_stimulus; + +struct usbfront_device; + +struct usbfront_device_probe_transaction +{ + xenidc_endpoint_transaction transaction; + + union + { + struct + { + usbif_probe_transaction_parameters parameters; + usbif_probe_transaction_status status; + } + probe; + struct + { + usbif_reset_transaction_parameters parameters; + usbif_reset_transaction_status status; + } + reset; + }; + + struct usbfront_device * device; +}; + +struct usbfront_device +{ + struct xenbus_device * dev; + void * drvdata; + spinlock_t lock; + usbfront_device_state state; + long int backend_id; + char * backend; + xenidc_endpoint endpoint; + xenidc_callback * endpoint_disconnect_callback; + int tick_count; + int port_probe_count; + struct usbfront_device_probe_transaction + probe_transaction[ USBFRONT_DEVICE_PORT_COUNT ]; + struct usb_port_status port_status[ USBFRONT_DEVICE_PORT_COUNT ]; + xenidc_work probe_driver_1_work; + xenidc_work remove_driver_1_work; +}; + +static void usbfront_device_handle_stimulus + ( struct usbfront_device * device, usbfront_device_stimulus stimulus ); + +static inline struct usbfront_device * usbfront_device_endpoint_to + ( xenidc_endpoint * endpoint ) +{ + trace(); + + return container_of( endpoint, struct usbfront_device, endpoint ); +} + +void usbfront_device_set_drvdata + ( struct usbfront_device * device, void * data ) +{ + trace(); + + device->drvdata = data; +} + +void * usbfront_device_get_drvdata( struct usbfront_device * device ) +{ + trace(); + + return device->drvdata; +} + +struct device * usbfront_device_to_dev( struct usbfront_device * device ) +{ + trace(); + + return &device->dev->dev; +} + +struct usbfront_device * usbfront_device_dev_to( struct device * dev ) +{ + trace(); + + return to_xenbus_device( dev )->data; +} + +int usbfront_device_query_port_count( struct usbfront_device * device ) +{ + /* trace(); */ + + return USBFRONT_DEVICE_PORT_COUNT; +} + +int usbfront_device_query_port_status_changed + ( struct usbfront_device * device, int port_number ) +{ + /* trace(); */ + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + int changed; + + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + if( ++device->tick_count == USBFRONT_DEVICE_PORT_COUNT ) + { + device->tick_count = 0; + + usbfront_device_handle_stimulus + ( device, usbfront_device_stimulus_pt ); + } + + changed = ( device->port_status[ port_number - 1 ].wPortChange != 0 ); + + spin_unlock_irqrestore( &device->lock, flags ); + + return changed; + } +} + +struct usb_port_status usbfront_device_query_port_status + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + struct usb_port_status port_status; + + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + port_status = device->port_status[ port_number - 1 ]; + + spin_unlock_irqrestore( &device->lock, flags ); + + return port_status; + } +} + +void usbfront_device_set_port_power + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->port_status[ port_number - 1 ].wPortStatus |= + USB_PORT_STAT_POWER; + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +void usbfront_device_set_port_reset + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->port_status[ port_number - 1 ].wPortStatus |= + USB_PORT_STAT_RESET; + device->port_status[ port_number - 1 ].wPortStatus &= + ~USB_PORT_STAT_ENABLE; + + usbfront_device_handle_stimulus( device, usbfront_device_stimulus_pt ); + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +void usbfront_device_clear_port_enable + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->port_status[ port_number - 1 ].wPortStatus &= + ~USB_PORT_STAT_ENABLE; + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +void usbfront_device_clear_port_connection_change + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->port_status[ port_number - 1 ].wPortChange &= + ~USB_PORT_STAT_C_CONNECTION; + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +void usbfront_device_clear_port_reset_change + ( struct usbfront_device * device, int port_number ) +{ + trace(); + + ASSERT + ( + ( port_number > 0 ) + && + ( port_number <= USBFRONT_DEVICE_PORT_COUNT ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->port_status[ port_number - 1 ].wPortChange &= + ~USB_PORT_STAT_C_RESET; + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +void usbfront_device_submit_message + ( struct usbfront_device * device, xenidc_endpoint_message * message ) +{ + trace(); + + xenidc_endpoint_submit_message( &device->endpoint, message ); +} + +void usbfront_device_submit_transaction + ( struct usbfront_device * device, xenidc_endpoint_transaction * transaction ) +{ + trace(); + + xenidc_endpoint_submit_transaction( &device->endpoint, transaction ); +} + +static void usbfront_device_endpoint_connect( xenidc_endpoint * endpoint ) +{ + trace(); + + /* Between connect and completion of the disconnect callback we are */ + /* allowed to issue messages and transactions. */ + + { + struct usbfront_device * device = + usbfront_device_endpoint_to( endpoint ); + + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + usbfront_device_handle_stimulus( device, usbfront_device_stimulus_cn ); + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +static void usbfront_device_endpoint_disconnect + ( xenidc_endpoint * endpoint, xenidc_callback * callback ) +{ + trace(); + + /* We must stop issuing messages and transactions and complete the */ + /* callback once all of the messages and transactions we are issuing */ + /* have completed or failed back to us. */ + + { + struct usbfront_device * device = + usbfront_device_endpoint_to( endpoint ); + + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + device->endpoint_disconnect_callback = callback; + + usbfront_device_handle_stimulus( device, usbfront_device_stimulus_dn ); + + spin_unlock_irqrestore( &device->lock, flags ); + } +} + +static void usbfront_device_endpoint_message + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ) +{ + trace(); + + /* The protocol doesn't require any messages sent from BE to FE so we */ + /* just ignore anything sent to us. */ + + xenidc_callback_success( xenidc_endpoint_message_to_callback( message ) ); +} + +static void usbfront_device_endpoint_transaction + ( xenidc_endpoint * endpoint, xenidc_endpoint_transaction * transaction ) +{ + trace(); + + /* The protocol doesn't require any transactions sent from BE to FE so */ + /* we just ignore anything sent to us. */ + + xenidc_endpoint_transaction_complete + ( transaction, XENIDC_ERROR_INVALID_PROTOCOL ); +} + +static void usbfront_device_probe_driver_1( void * data ); +static void usbfront_device_remove_driver_1( void * data ); +static void usbfront_device_probe_all_ports_1( xenidc_callback * callback ); + +static int usbfront_device_init_or_exit + ( struct usbfront_device * device, struct xenbus_device * dev, int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + memset( device, 0, sizeof( *device ) ); + + device->dev = dev; + + spin_lock_init( &device->lock ); + + device->state = usbfront_device_state_i; + + { + int i; + + for( i = 0; i < USBFRONT_DEVICE_PORT_COUNT; i++ ) + { + struct usbfront_device_probe_transaction * probe = + &device->probe_transaction[ i ]; + + xenidc_endpoint_transaction_init + ( + &probe->transaction, + usbfront_device_probe_all_ports_1 + ); + + probe->device = device; + } + } + + xenidc_work_init + ( + &device->probe_driver_1_work, + usbfront_device_probe_driver_1, + device + ); + + xenidc_work_init + ( + &device->remove_driver_1_work, + usbfront_device_remove_driver_1, + device + ); + + device->backend = NULL; + + return_value = xenbus_gather + ( + NULL, /* FIXME? */ + dev->nodename, + "backend-id", "%li", &device->backend_id, + "backend", NULL, &device->backend, + NULL + ); + + if( XENBUS_EXIST_ERR( return_value ) ) + { + goto EXIT_NO_BACKEND; + } + + if( return_value < 0 ) + { + xenbus_dev_error + ( + dev, + return_value, + "reading %s/backend", + dev->nodename + ); + + goto EXIT_NO_BACKEND; + } + + return_value = xenidc_endpoint_init + ( + &device->endpoint, + usbfront_device_endpoint_connect, + usbfront_device_endpoint_message, + usbfront_device_endpoint_transaction, + usbfront_device_endpoint_disconnect, + USBIF_FE_INITIATOR_QUOTA, + USBIF_FE_INITIATOR_MAXIMUM_BYTE_COUNT, + USBIF_FE_TARGET_QUOTA, + USBIF_FE_TARGET_MAXIMUM_BYTE_COUNT + ); + + if( return_value != 0 ) + { + goto EXIT_NO_ENDPOINT; + } + + { + xenidc_address address; + + xenidc_address_init + ( + &address, + dev->nodename, + device->backend, + device->backend_id + ); + + xenidc_endpoint_create( &device->endpoint, address ); + } + + return 0; + + EXIT: + + xenidc_endpoint_destroy( &device->endpoint ); + + xenidc_endpoint_exit( &device->endpoint ); + + EXIT_NO_ENDPOINT: + + kfree( device->backend ); + + EXIT_NO_BACKEND: + + return return_value; + } +} + +static int usbfront_device_init + ( struct usbfront_device * device, struct xenbus_device * dev ) +{ + trace(); + + return usbfront_device_init_or_exit( device, dev, 0 ); +} + +static void usbfront_device_exit( struct usbfront_device * device ) +{ + trace(); + + (void)usbfront_device_init_or_exit( device, NULL, 1 ); +} + +static int usbfront_device_probe_or_remove + ( struct xenbus_device * dev, int remove ) +{ + trace(); + + { + int return_value = 0; + + struct usbfront_device * device; + + if( remove ) + { + goto REMOVE; + } + + device = kmalloc( sizeof( *device ), GFP_KERNEL ); + + if( device == NULL ) + { + xenbus_dev_error( dev, -ENOMEM, "allocating backend structure" ); + + return_value = -ENOMEM; + + goto EXIT_NO_DEVICE; + } + + dev->data = device; + + return_value = usbfront_device_init( device, dev ); + + if( return_value != 0 ) + { + goto EXIT_INIT_FAILED; + } + + return 0; + + REMOVE: + + device = dev->data; + + usbfront_device_exit( device ); + + EXIT_INIT_FAILED: + + dev->data = NULL; + + kfree( device ); + + EXIT_NO_DEVICE: + + return return_value; + } +} + +static int usbfront_device_probe + ( struct xenbus_device * dev, const struct xenbus_device_id * id ) +{ + trace(); + + return usbfront_device_probe_or_remove( dev, 0 ); +} + +static int usbfront_device_remove( struct xenbus_device * dev ) +{ + trace(); + + return usbfront_device_probe_or_remove( dev, 1 ); +} + +static struct xenbus_device_id usbfront_device_ids[] = +{ + { "usb" }, + { "" } +}; + +static struct xenbus_driver usbfront_device_driver = +{ + .name = "usb", + .owner = THIS_MODULE, + .ids = usbfront_device_ids, + .probe = usbfront_device_probe, + .remove = usbfront_device_remove, +}; + +int usbfront_device_class_init( void ) +{ + trace(); + + return xenbus_register_driver( &usbfront_device_driver ); +} + +void usbfront_device_class_exit( void ) +{ + trace(); + + xenbus_unregister_driver( &usbfront_device_driver ); +} + +static void usbfront_device_invalid_stimulus + ( struct usbfront_device * device, usbfront_device_stimulus stimulus ); + +static void usbfront_device_probe_driver( struct usbfront_device * device ); + +static void usbfront_device_remove_driver( struct usbfront_device * device ); + +static void usbfront_device_probe_all_ports( struct usbfront_device * device ); + +static void usbfront_device_disconnect_all_ports + ( struct usbfront_device * device ); + +static void usbfront_device_complete_endpoint_disconnect + ( struct usbfront_device * device ); + +static void usbfront_device_handle_stimulus + ( struct usbfront_device * device, usbfront_device_stimulus stimulus ) +{ + switch( device->state ) + { + case usbfront_device_state_i: + switch( stimulus ) + { + case usbfront_device_stimulus_cn: + device->state = usbfront_device_state_i_cn; + usbfront_device_probe_driver( device ); + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn: + /* Probing driver. */ + switch( stimulus ) + { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_dn; + break; + case usbfront_device_stimulus_ps: + device->state = usbfront_device_state_i_cn_ps; + usbfront_device_probe_all_ports( device ); + break; + case usbfront_device_stimulus_pf: + device->state = usbfront_device_state_i_cn_pf; + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_dn: + /* Probing driver. */ + /* Endpoint disconnecting. */ + switch( stimulus ) + { + case usbfront_device_stimulus_ps: + device->state = usbfront_device_state_i_cn_dn_ps; + usbfront_device_remove_driver( device ); + break; + case usbfront_device_stimulus_pf: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports( device ); + usbfront_device_complete_endpoint_disconnect( device ); + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_ps: + /* Driver Probed. */ + /* Polling ports. */ + switch( stimulus ) + { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_ps_dn; + usbfront_device_remove_driver( device ); + break; + case usbfront_device_stimulus_pt: + break; + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i_cn_ps_pc; + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_pf: + /* Driver probe failed. */ + switch( stimulus ) + { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports( device ); + usbfront_device_complete_endpoint_disconnect( device ); + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_dn_ps: + /* Endpoint disconnecting. */ + /* Removing driver. */ + switch( stimulus ) + { + case usbfront_device_stimulus_rc: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports( device ); + usbfront_device_complete_endpoint_disconnect( device ); + break; + case usbfront_device_stimulus_pt: + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_ps_dn: + /* Endpoint disconnecting. */ + /* Removing driver. */ + /* Polling ports. */ + switch( stimulus ) + { + case usbfront_device_stimulus_rc: + device->state = usbfront_device_state_i_cn_ps_dn_rc; + break; + case usbfront_device_stimulus_pt: + break; + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i_cn_dn_ps; + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_ps_pc: + /* Driver Probed. */ + switch( stimulus ) + { + case usbfront_device_stimulus_dn: + device->state = usbfront_device_state_i_cn_dn_ps; + usbfront_device_remove_driver( device ); + break; + case usbfront_device_stimulus_pt: + device->state = usbfront_device_state_i_cn_ps; + usbfront_device_probe_all_ports( device ); + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + case usbfront_device_state_i_cn_ps_dn_rc: + /* Endpoint disconnecting. */ + /* Polling ports. */ + switch( stimulus ) + { + case usbfront_device_stimulus_pc: + device->state = usbfront_device_state_i; + usbfront_device_disconnect_all_ports( device ); + usbfront_device_complete_endpoint_disconnect( device ); + break; + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } + default: + usbfront_device_invalid_stimulus( device, stimulus ); + break; + } +} + +static void usbfront_device_invalid_stimulus + ( struct usbfront_device * device, usbfront_device_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "usbfront: device %p in state %d" + "received invalid stimulus %d", + device, + device->state, + stimulus + ); +} + +static void usbfront_device_probe_driver( struct usbfront_device * device ) +{ + trace(); + + { + int scheduled = xenidc_work_schedule( &device->probe_driver_1_work ); + + ASSERT( scheduled ); + } +} + +static void usbfront_device_probe_driver_1( void * data ) +{ + trace(); + + { + struct usbfront_device * device = (struct usbfront_device *)data; + + usbfront_device_stimulus stimulus; + + if( usbfront_driver_probe( device ) == 0 ) + { + stimulus = usbfront_device_stimulus_ps; + } + else + { + stimulus = usbfront_device_stimulus_pf; + } + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + usbfront_device_handle_stimulus( device, stimulus ); + + spin_unlock_irqrestore( &device->lock, flags ); + } + } +} + +static void usbfront_device_remove_driver( struct usbfront_device * device ) +{ + trace(); + + { + int scheduled = xenidc_work_schedule( &device->remove_driver_1_work ); + + ASSERT( scheduled ); + } +} + +static void usbfront_device_remove_driver_1( void * data ) +{ + trace(); + + { + struct usbfront_device * device = (struct usbfront_device *)data; + + usbfront_driver_remove( device ); + + { + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + usbfront_device_handle_stimulus + ( device, usbfront_device_stimulus_rc ); + + spin_unlock_irqrestore( &device->lock, flags ); + } + } +} + +static void usbfront_device_probe_all_ports( struct usbfront_device * device ) +{ + device->port_probe_count = USBFRONT_DEVICE_PORT_COUNT; + + { + int i; + + for( i = 0; i < USBFRONT_DEVICE_PORT_COUNT; i++ ) + { + struct usbfront_device_probe_transaction * probe = + &device->probe_transaction[ i ]; + + if + ( + ( device->port_status[ i ].wPortStatus & USB_PORT_STAT_RESET ) + != + USB_PORT_STAT_RESET + ) + { + xenidc_endpoint_transaction_set_parameters_lbr + ( + &probe->transaction, + xenidc_vaddress_create_lbr + ( + &probe->probe.parameters, + sizeof( probe->probe.parameters ) + ) + ); + + xenidc_endpoint_transaction_set_status_lbr + ( + &probe->transaction, + xenidc_vaddress_create_lbr + ( + &probe->probe.status, + sizeof( probe->probe.status ) + ) + ); + + memset + ( + &probe->probe.parameters, + 0, + sizeof( probe->probe.parameters ) + ); + + probe->probe.parameters.header.transaction_type = + USBIF_TRANSACTION_TYPE_PROBE; + + probe->probe.parameters.port = i + 1; + + memset + ( &probe->probe.status, 0, sizeof( probe->probe.status ) ); + } + else + { + xenidc_endpoint_transaction_set_parameters_lbr + ( + &probe->transaction, + xenidc_vaddress_create_lbr + ( + &probe->reset.parameters, + sizeof( probe->reset.parameters ) + ) + ); + + xenidc_endpoint_transaction_set_status_lbr + ( + &probe->transaction, + xenidc_vaddress_create_lbr + ( + &probe->reset.status, + sizeof( probe->reset.status ) + ) + ); + + memset + ( + &probe->reset.parameters, + 0, + sizeof( probe->reset.parameters ) + ); + + probe->reset.parameters.header.transaction_type = + USBIF_TRANSACTION_TYPE_RESET; + + probe->reset.parameters.port = i + 1; + + memset + ( &probe->reset.status, 0, sizeof( probe->reset.status ) ); + } + + xenidc_endpoint_submit_transaction + ( &device->endpoint, &probe->transaction ); + } + } +} + +static void usbfront_device_probe_all_ports_1( xenidc_callback * callback ) +{ + struct usbfront_device_probe_transaction * probe = container_of + ( + xenidc_endpoint_transaction_callback_to( callback ), + struct usbfront_device_probe_transaction, + transaction + ); + + struct usbfront_device * device = probe->device; + + unsigned long flags; + + spin_lock_irqsave( &device->lock, flags ); + + if + ( + probe->probe.parameters.header.transaction_type + == + USBIF_TRANSACTION_TYPE_PROBE + ) + { + int port_number = probe->probe.parameters.port; + + if( xenidc_callback_query_error( callback ) == XENIDC_ERROR_SUCCESS ) + { + struct usb_port_status * port_status = + &device->port_status[ port_number - 1 ]; + + if + ( + probe->probe.status.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 + ( probe->probe.status.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 + { + trace3 + ( + "device %p: unexpected result %d probing port %d", + device, + (int)probe->probe.status.result, + port_number + ); + } + } + else + { + trace3 + ( + "device %p: error %d probing port %d", + device, + usbif_error_map_to_local + ( xenidc_callback_query_error( callback ) ), + port_number + ); + } + } + else + { + int port_number = probe->reset.parameters.port; + + struct usb_port_status * port_status = + &device->port_status[ port_number - 1 ]; + + if + ( + ( xenidc_callback_query_error( callback ) == XENIDC_ERROR_SUCCESS ) + && + ( + ( probe->reset.status.result == USBIF_RESET_RESULT_FULL_SPEED ) + || + ( probe->reset.status.result == USBIF_RESET_RESULT_LOW_SPEED ) + || + ( probe->reset.status.result == USBIF_RESET_RESULT_HIGH_SPEED ) + ) + ) + { + if( probe->reset.status.result == USBIF_RESET_RESULT_LOW_SPEED ) + { + port_status->wPortStatus |= USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus &= ~USB_PORT_STAT_HIGH_SPEED; + } + else if + ( probe->reset.status.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 + ( + xenidc_callback_query_error( callback ) + != + XENIDC_ERROR_SUCCESS + ) + { + printk + ( + KERN_ERR "usbfront: device %p: error %d resetting port %d", + device, + usbif_error_map_to_local + ( xenidc_callback_query_error( callback ) ), + port_number + ); + } + else + { + trace3 + ( + "device %p: unexpected result %d resetting port %d", + device, + (int)probe->reset.status.result, + port_number + ); + } + + port_status->wPortStatus &= ~USB_PORT_STAT_LOW_SPEED; + port_status->wPortStatus &= ~USB_PORT_STAT_HIGH_SPEED; + + port_status->wPortStatus &= ~USB_PORT_STAT_RESET; + port_status->wPortChange |= USB_PORT_STAT_C_RESET; + } + } + + if( --device->port_probe_count == 0 ) + { + usbfront_device_handle_stimulus( device, usbfront_device_stimulus_pc ); + } + + spin_unlock_irqrestore( &device->lock, flags ); +} + +static void usbfront_device_disconnect_all_ports + ( struct usbfront_device * device ) +{ + trace(); + + { + int i; + + for( i = 0; i < USBFRONT_DEVICE_PORT_COUNT; i++ ) + { + struct usb_port_status * port_status = &device->port_status[ i ]; + + if + ( + ( port_status->wPortStatus & USB_PORT_STAT_CONNECTION ) + == + USB_PORT_STAT_CONNECTION + ) + { + /* There was a device attached to the port before. */ + + port_status->wPortStatus &= ~USB_PORT_STAT_CONNECTION; + port_status->wPortChange |= USB_PORT_STAT_C_CONNECTION; + } + } + } +} + +static void usbfront_device_complete_endpoint_disconnect + ( struct usbfront_device * device ) +{ + trace(); + + xenidc_callback_success( device->endpoint_disconnect_callback ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_device.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,85 @@ +/*****************************************************************************/ +/* usbfront_device is a device which represents a connection to a back-end. */ +/* The intent was for it to have an interface like a hardware USB host */ +/* controller device. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_DEVICE_H +#define USBFRONT_DEVICE_H + +#include +#include +#include "../../usb/core/hcd.h" +#include + +extern int usbfront_device_class_init( void ); + +extern void usbfront_device_class_exit( void ); + +struct usbfront_device; + +void usbfront_device_set_drvdata + ( struct usbfront_device * device, void * data ); + +void * usbfront_device_get_drvdata( struct usbfront_device * device ); + +struct device * usbfront_device_to_dev + ( struct usbfront_device * device ); + +struct usbfront_device * usbfront_device_dev_to( struct device * dev ); + +int usbfront_device_query_port_count( struct usbfront_device * device ); + +int usbfront_device_query_port_status_changed + ( struct usbfront_device * device, int port_number ); + +struct usb_port_status usbfront_device_query_port_status + ( struct usbfront_device * device, int port_number ); + +/* Used to turn on power to a port. */ +void usbfront_device_set_port_power + ( struct usbfront_device * device, int port_number ); + +/* Used to reset a port. Completes asynchronously with notification via the */ +/* port status. */ +void usbfront_device_set_port_reset + ( struct usbfront_device * device, int port_number ); + +void usbfront_device_clear_port_enable + ( struct usbfront_device * device, int port_number ); + +/* Used to ack a connection change */ +void usbfront_device_clear_port_connection_change + ( struct usbfront_device * device, int port_number ); + +/* Used to ack a reset change */ +void usbfront_device_clear_port_reset_change + ( struct usbfront_device * device, int port_number ); + +void usbfront_device_submit_message + ( struct usbfront_device * device, xenidc_endpoint_message * message ); + +void usbfront_device_submit_transaction +( + struct usbfront_device * device, + xenidc_endpoint_transaction * transaction +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,880 @@ +/*****************************************************************************/ +/* A device driver for usbfront_device devices. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Based on */ +/* */ +/* arch/xen/drivers/usbif/frontend/main.c */ +/* pristine-linux-2.6.11/drivers/usb/host/uhci-hcd.c */ +/* pristine-linux-2.6.11/drivers/usb/core/hcd-pci.c */ +/* */ +/* original copyright notices follow... */ +/*****************************************************************************/ + +/* + * Xen Virtual USB Frontend Driver + * + * This file contains the first version of the Xen virtual USB hub + * that I've managed not to delete by mistake (3rd time lucky!). + * + * Based on Linux's uhci.c, original copyright notices are displayed + * below. Portions also (c) 2004 Intel Research Cambridge + * and (c) 2004, 2005 Mark Williamson + * + * Contact or + * regarding this code. + * + * Still to be (maybe) implemented: + * - migration / backend restart support? + * - support for building / using as a module + */ + +/* + * Universal Host Controller Interface driver for USB. + * + * Maintainer: Johannes Erdfelt + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@xxxxxxxxxxx + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@xxxxxxxxx + * (C) Copyright 1999 Deti Fliegl, deti@xxxxxxxxx + * (C) Copyright 1999 Thomas Sailer, sailer@xxxxxxxxxxxxxx + * (C) Copyright 1999 Roman Weissgaerber, weissg@xxxxxxxxx + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@xxxxxxxxxxxxx). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * + * Intel documents this fairly well, and as far as I know there + * are no royalties or anything like that, but even so there are + * people who decided that they want to do the same thing in a + * completely different way. + * + * WARNING! The USB documentation is downright evil. Most of it + * is just crap, written by a committee. You're better off ignoring + * most of it, the important stuff is: + * - the low-level protocol (fairly simple but lots of small details) + * - working around the horridness of the rest + */ + +/* + * Universal Host Controller Interface driver for USB. + * + * Maintainer: Alan Stern + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@xxxxxxxxxxx + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@xxxxxxxxx + * (C) Copyright 1999 Deti Fliegl, deti@xxxxxxxxx + * (C) Copyright 1999 Thomas Sailer, sailer@xxxxxxxxxxxxxx + * (C) Copyright 1999 Roman Weissgaerber, weissg@xxxxxxxxx + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@xxxxxxxxxxxxx). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004 Alan Stern, stern@xxxxxxxxxxxxxxxxxxx + * + * Intel documents this fairly well, and as far as I know there + * are no royalties or anything like that, but even so there are + * people who decided that they want to do the same thing in a + * completely different way. + * + * WARNING! The USB documentation is downright evil. Most of it + * is just crap, written by a committee. You're better off ignoring + * most of it, the important stuff is: + * - the low-level protocol (fairly simple but lots of small details) + * - working around the horridness of the rest + */ + +/* + * (C) Copyright David Brownell 2000-2002 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include "usbfront_driver.h" +#include "usbfront_hcd_resource.h" +#include "usbfront_sll.h" +#include "usbfront_trace.h" + +typedef enum +{ + usbfront_hcd_state_i +} +usbfront_hcd_state; + +typedef enum +{ + usbfront_hcd_stimulus_uq /* urb enqueue */ +} +usbfront_hcd_stimulus; + +#define USBFRONT_HCD_RESOURCE_COUNT 16 + +struct usbfront_hcd +{ + spinlock_t lock; + usbfront_hcd_state state; + struct usbfront_sll urb_sll; + struct list_head free_resource_list; + struct usbfront_hcd_resource resources[ USBFRONT_HCD_RESOURCE_COUNT ]; +}; + +static void usbfront_hcd_handle_stimulus + ( struct usbfront_hcd * uhcd, usbfront_hcd_stimulus stimulus ); + +static struct usbfront_hcd * usbfront_hcd_hcd_to_uhcd( struct usb_hcd * hcd ) +{ + return (struct usbfront_hcd *)( hcd->hcd_priv ); +} + +static struct usbfront_device * + usbfront_hcd_hcd_to_usbfront_device( struct usb_hcd * hcd ) +{ + return usbfront_device_dev_to( hcd_to_bus( hcd )->controller ); +} + +void usbfront_hcd_resource_completed( xenidc_callback * callback ); + +static int usbfront_hcd_start( struct usb_hcd * hcd ) +{ + trace(); + + { + int return_value = 0; + + struct usbfront_hcd * uhcd = usbfront_hcd_hcd_to_uhcd( hcd ); + + memset( uhcd, 0, sizeof( *uhcd ) ); + + spin_lock_init( &uhcd->lock ); + + uhcd->state = usbfront_hcd_state_i; + + usbfront_sll_init( &uhcd->urb_sll ); + + INIT_LIST_HEAD( &uhcd->free_resource_list ); + + { + int i; + + for( i = 0; i < USBFRONT_HCD_RESOURCE_COUNT; i++ ) + { + struct usbfront_hcd_resource * resource = + &uhcd->resources[ i ]; + + return_value = usbfront_hcd_resource_init + ( + resource, + usbfront_hcd_hcd_to_usbfront_device( hcd ), + i, + usbfront_hcd_resource_completed + ); + + if( return_value != 0 ) + { + goto EXIT_NO_RESOURCE; + } + + list_add + ( + usbfront_hcd_resource_to_link( resource ), + &uhcd->free_resource_list + ); + } + } + + { + struct usb_device * root_hub_device = + usb_alloc_dev( NULL, &hcd->self, 0 ); + + if( root_hub_device == NULL ) + { + return_value = -ENOMEM; + + goto EXIT_NO_ROOT_HUB; + } + + root_hub_device->speed = USB_SPEED_HIGH; + + if + ( + ( + return_value = + usb_hcd_register_root_hub( root_hub_device, hcd ) + ) + != + 0 + ) + { + goto EXIT_NO_REGISTER; + } + + return 0; + + EXIT_NO_REGISTER: + + usb_put_dev( root_hub_device ); + } + + EXIT_NO_ROOT_HUB: + + EXIT_NO_RESOURCE: + + while( !list_empty( &uhcd->free_resource_list ) ) + { + struct usbfront_hcd_resource * resource = list_entry + ( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + USBFRONT_HCD_RESOURCE_LINK + ); + + list_del_init( usbfront_hcd_resource_to_link( resource ) ); + + usbfront_hcd_resource_exit( resource ); + } + + return return_value; + } +} + +static void usbfront_hcd_stop( struct usb_hcd * hcd ) +{ + trace(); + + { + struct usbfront_hcd * uhcd = usbfront_hcd_hcd_to_uhcd( hcd ); + + while( !list_empty( &uhcd->free_resource_list ) ) + { + struct usbfront_hcd_resource * resource = list_entry + ( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + USBFRONT_HCD_RESOURCE_LINK + ); + + list_del_init( usbfront_hcd_resource_to_link( resource ) ); + + usbfront_hcd_resource_exit( resource ); + } + } +} + +static int usbfront_hcd_get_frame_number( struct usb_hcd * hcd ) +{ + trace(); + + return 0; +} + +static inline void usbfront_check_contiguous + ( unsigned long buffer, unsigned long length ) +{ + if( length != 0 ) + { + unsigned long offset; + + for + ( + offset = 0; + offset < ( length + ( buffer & ~PAGE_MASK ) ); + offset += PAGE_SIZE + ) + { + unsigned long maddress1 = + virt_to_machine( ( buffer & PAGE_MASK ) + offset ); + + unsigned long maddress2 = + virt_to_machine( buffer & PAGE_MASK ) + offset; + + ASSERT( maddress1 == maddress2 ); + } + } +} + +static int usbfront_hcd_urb_enqueue +( + struct usb_hcd * hcd, + struct usb_host_endpoint * ep, /* FIXME: what's the intended use for ep? */ + struct urb * urb, + int mem_flags +) +{ + trace(); + + usbfront_check_contiguous + ( (unsigned long)urb->transfer_buffer, urb->transfer_buffer_length ); + + { + struct usbfront_hcd * uhcd = usbfront_hcd_hcd_to_uhcd( hcd ); + + unsigned long flags; + + spin_lock_irqsave( &uhcd->lock, flags ); + + usbfront_slk_init( (struct usbfront_slk *)&urb->hcpriv ); + + usbfront_sll_add_last + ( &uhcd->urb_sll, (struct usbfront_slk *)&urb->hcpriv ); + + usbfront_hcd_handle_stimulus( uhcd, usbfront_hcd_stimulus_uq ); + + spin_unlock_irqrestore( &uhcd->lock, flags ); + } + + return 0; +} + +static int usbfront_hcd_urb_dequeue( struct usb_hcd * hcd, struct urb * urb ) +{ + trace(); + + { + int dequeued; + + { + struct usbfront_hcd * uhcd = usbfront_hcd_hcd_to_uhcd( hcd ); + + unsigned long flags; + + spin_lock_irqsave( &uhcd->lock, flags ); + + dequeued = usbfront_sll_remove_slk + ( &uhcd->urb_sll, (struct usbfront_slk *)&urb->hcpriv ); + + spin_unlock_irqrestore( &uhcd->lock, flags ); + } + + if( dequeued ) + { + urb->hcpriv = 0; + + urb->actual_length = 0; + + { + unsigned long flags; + + local_irq_save( flags ); + + usb_hcd_giveback_urb( hcd, urb, 0 ); + + local_irq_restore( flags ); + } + } + else + { + usbfront_hcd_resource_dequeue_urb( urb ); + } + } + + return 0; +} + +static void usbfront_hcd_endpoint_disable + ( struct usb_hcd * hcd, struct usb_host_endpoint * ep ) +{ + trace(); +} + +static int usbfront_hcd_hub_status_data( struct usb_hcd * hcd, char * buf ) +{ + int changed = 0; + + struct usbfront_device * device = + usbfront_hcd_hcd_to_usbfront_device( hcd ); + + int port_count = usbfront_device_query_port_count( device ); + + int byte_count = + ( 1 /* hub */ + port_count /* ports */ + 7 /* round up */ ) / 8; + + memset( buf, 0, byte_count ); + + { + int i; + + for( i = 0; i < port_count; i++ ) + { + if( usbfront_device_query_port_status_changed( device, i ) ) + { + buf[ ( i + 1 ) / 8 ] |= 1 << ( ( i + 1 ) % 8 ); + + changed = 1; + } + } + } + + return changed ? byte_count : 0; +} + +static int usbfront_hcd_hub_control +( + struct usb_hcd * hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char * buf, + u16 wLength +) +{ + trace(); + + { + struct usbfront_device * device = + usbfront_hcd_hcd_to_usbfront_device( hcd ); + + int return_value = 0; + + switch( typeReq ) + { + case GetHubStatus: + + trace0( "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: + + trace0( "GetPortStatus" ); + + if + ( + ( wValue != 0 ) + || + ( wIndex > usbfront_device_query_port_count( device ) ) + || + ( wLength != 4 ) + ) + { + goto ERROR; + } + + { + struct usb_port_status port_status = + usbfront_device_query_port_status( device, wIndex - 1 ); + + memcpy( buf, &port_status, wLength ); + } + + break; + + case GetHubDescriptor: + + trace0( "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 ] = usbfront_device_query_port_count( device ); + + memcpy + ( + buf, + &descriptor, + min_t( size_t, sizeof( descriptor ), wLength ) + ); + } + + break; + + case SetPortFeature: + + trace0( "SetPortFeature" ); + + if + ( + ( + ( wIndex & 0x00FF ) + > + usbfront_device_query_port_count( device ) + ) + || + ( wLength != 0 ) + ) + { + goto ERROR; + } + + switch( wValue ) + { + case USB_PORT_FEAT_SUSPEND: + trace0( "USB_PORT_FEAT_SUSPEND" ); + break; + case USB_PORT_FEAT_RESET: + trace0( "USB_PORT_FEAT_RESET" ); + usbfront_device_set_port_reset( device, wIndex - 1 ); + break; + case USB_PORT_FEAT_POWER: + trace0( "USB_PORT_FEAT_POWER" ); + usbfront_device_set_port_power( device, wIndex - 1 ); + break; + default: + trace1( "Unknown:%x", wValue ); + goto ERROR; + } + + break; + + case ClearPortFeature: + + trace0( "ClearPortFeature" ); + + if + ( + ( + ( wIndex & 0x00FF ) + > + usbfront_device_query_port_count( device ) + ) + || + ( wLength != 0 ) + ) + { + goto ERROR; + } + + switch( wValue ) + { + case USB_PORT_FEAT_ENABLE: + trace0( "USB_PORT_FEAT_ENABLE" ); + usbfront_device_clear_port_enable( device, wIndex - 1 ); + break; + case USB_PORT_FEAT_SUSPEND: + trace0( "USB_PORT_FEAT_SUSPEND" ); + break; + case USB_PORT_FEAT_POWER: + trace0( "USB_PORT_FEAT_POWER" ); + break; + case USB_PORT_FEAT_INDICATOR: + trace0( "USB_PORT_FEAT_INDICATOR" ); + break; + case USB_PORT_FEAT_C_CONNECTION: + trace0( "USB_PORT_C_CONNECTION" ); + usbfront_device_clear_port_connection_change + ( device, wIndex - 1 ); + break; + case USB_PORT_FEAT_C_RESET: + trace0( "USB_PORT_C_RESET" ); + usbfront_device_clear_port_reset_change( device, wIndex - 1 ); + break; + case USB_PORT_FEAT_C_ENABLE: + trace0( "USB_PORT_C_ENABLE" ); + break; + case USB_PORT_FEAT_C_SUSPEND: + trace0( "USB_PORT_C_SUSPEND" ); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + trace0( "USB_PORT_C_OVER_CURRENT" ); + break; + default: + trace1( "Unknown:%x", wValue ); + goto ERROR; + } + + break; + + default: + trace1( "Unknown:%x", typeReq ); + ERROR: + return_value = -EPIPE; + } + + return return_value; + } +} + +static int usbfront_hcd_start_port_reset + ( struct usb_hcd * hcd, unsigned port_num ) +{ + trace(); + + return 0; +} + +static struct hc_driver usbfront_hc_driver = +{ + .description = "usbfront_hc_driver", + .product_desc = "Xen USB Front-End Driver", + .hcd_priv_size = sizeof( struct usbfront_hcd ), + /* .irq */ + .flags = HCD_USB2, + /* reset optional. */ + .start = usbfront_hcd_start, + /* suspend optional. */ + /* resume optional. */ + .stop = usbfront_hcd_stop, + .get_frame_number = usbfront_hcd_get_frame_number, + .urb_enqueue = usbfront_hcd_urb_enqueue, + .urb_dequeue = usbfront_hcd_urb_dequeue, + .endpoint_disable = usbfront_hcd_endpoint_disable, + .hub_status_data = usbfront_hcd_hub_status_data, + .hub_control = usbfront_hcd_hub_control, + /* hub_suspend optional. */ + /* hub_resume optional. */ + .start_port_reset = usbfront_hcd_start_port_reset, +}; + +static void usbfront_hcd_invalid_stimulus + ( struct usbfront_hcd * uhcd, usbfront_hcd_stimulus stimulus ); + +static void usbfront_hcd_kick_urbs( struct usbfront_hcd * uhcd ); + +static void usbfront_hcd_handle_stimulus + ( struct usbfront_hcd * uhcd, usbfront_hcd_stimulus stimulus ) +{ + trace3 + ( + "uhcd %p in state %d received stimulus %d", + uhcd, + uhcd->state, + stimulus + ); + + switch( uhcd->state ) + { + case usbfront_hcd_state_i: + switch( stimulus ) + { + case usbfront_hcd_stimulus_uq: + usbfront_hcd_kick_urbs( uhcd ); + break; + } + break; + default: + usbfront_hcd_invalid_stimulus( uhcd, stimulus ); + break; + } +} + +static void usbfront_hcd_invalid_stimulus + ( struct usbfront_hcd * uhcd, usbfront_hcd_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "usbfront: hc_driver_uhcd %p in state %d" + "received invalid stimulus %d", + uhcd, + uhcd->state, + stimulus + ); +} + +static void usbfront_hcd_kick_urbs( struct usbfront_hcd * uhcd ) +{ + trace(); + + while + ( + !usbfront_sll_is_empty( &uhcd->urb_sll ) + && + !list_empty( &uhcd->free_resource_list ) + ) + { + trace0( "Starting URB" ); + + { + struct urb * urb = container_of + ( + (void **)usbfront_sll_remove_first( &uhcd->urb_sll ), + struct urb, + hcpriv + ); + + struct usbfront_hcd_resource * resource = list_entry + ( + uhcd->free_resource_list.next, + struct usbfront_hcd_resource, + USBFRONT_HCD_RESOURCE_LINK + ); + + list_del_init( usbfront_hcd_resource_to_link( resource ) ); + + usbfront_hcd_resource_start_urb( resource, urb ); + } + } +} + +void usbfront_hcd_resource_completed( xenidc_callback * callback ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = + usbfront_hcd_resource_callback_to( callback ); + + struct usbfront_hcd * uhcd = usbfront_hcd_hcd_to_uhcd + ( + usbfront_device_get_drvdata + ( usbfront_hcd_resource_query_device( resource ) ) + ); + + unsigned long flags; + + spin_lock_irqsave( &uhcd->lock, flags ); + + list_add + ( + usbfront_hcd_resource_to_link( resource ), + &uhcd->free_resource_list + ); + + usbfront_hcd_kick_urbs( uhcd ); + + spin_unlock_irqrestore( &uhcd->lock, flags ); + } +} + +static int usbfront_driver_probe_or_remove + ( struct usbfront_device * device, int remove ) +{ + trace(); + + { + int return_value = 0; + + struct usb_hcd * hcd; + + if( remove ) + { + goto REMOVE; + } + + if( usb_disabled() ) + { + return_value = -ENODEV; + + goto REMOVE_0; + } + + if + ( + ( + hcd = usb_create_hcd + ( + &usbfront_hc_driver, + usbfront_device_to_dev( device ), + usbfront_device_to_dev( device )->bus_id + ) + ) + == + NULL + ) + { + return_value = -ENOMEM; + + goto REMOVE_0; + } + + usbfront_device_set_drvdata( device, hcd ); + + if( ( return_value = usb_add_hcd( hcd, 0, 0 ) ) != 0 ) + { + goto REMOVE_1; + } + + return 0; + + REMOVE: + + hcd = usbfront_device_get_drvdata( device ); + + usb_remove_hcd( hcd ); + + REMOVE_1: + + usb_put_hcd( hcd ); + + usbfront_device_set_drvdata( device, NULL ); + + REMOVE_0: + + return return_value; + } +} + +int usbfront_driver_probe( struct usbfront_device * device ) +{ + trace(); + + return usbfront_driver_probe_or_remove( device, 0 ); +} + +void usbfront_driver_remove( struct usbfront_device * device ) +{ + trace(); + + usbfront_driver_probe_or_remove( device, 1 ); +} + +int usbfront_driver_class_init( void ) +{ + trace(); + + return usbfront_hcd_resource_class_init(); +} + +void usbfront_driver_class_exit( void ) +{ + trace(); + + usbfront_hcd_resource_class_exit(); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_driver.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,35 @@ +/*****************************************************************************/ +/* A device driver for usbfront_device devices. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_DRIVER_H +#define USBFRONT_DRIVER_H + +#include "usbfront_device.h" + +extern int usbfront_driver_class_init( void ); + +extern void usbfront_driver_class_exit( void ); + +extern int usbfront_driver_probe( struct usbfront_device * device ); + +extern void usbfront_driver_remove( struct usbfront_device * device ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,945 @@ +/*****************************************************************************/ +/* A resource used by the usbfront host controller device driver to process */ +/* an URB by translating the URB and optional unlink request into */ +/* a transaction and corresponding optional unlink message for submission to */ +/* the back end. */ +/* */ +/* 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 + */ + +/* FIXME is URB serialisation maintained correctly? */ + +#include +#include "usbfront_assert.h" +#include "usbfront_driver.h" +#include "usbfront_hcd_resource.h" +#include "usbfront_trace.h" + +xenidc_rbr_provider_pool * usbfront_hcd_resource_rbr_provider_pool; + +int usbfront_hcd_resource_class_init( void ) +{ + trace(); + + usbfront_hcd_resource_rbr_provider_pool = + xenidc_allocate_rbr_provider_pool( USBIF_MAX_PAGES_PER_REQUEST ); + + return ( usbfront_hcd_resource_rbr_provider_pool != NULL ) ? 0 : -ENOMEM; +} + +void usbfront_hcd_resource_class_exit( void ) +{ + trace(); + + xenidc_free_rbr_provider_pool( usbfront_hcd_resource_rbr_provider_pool ); +} + +typedef enum +{ + usbfront_hcd_resource_stimulus_st, /* Start URB */ + usbfront_hcd_resource_stimulus_dq, /* Dequeue URB */ + usbfront_hcd_resource_stimulus_cs, /* Create RBRs success */ + usbfront_hcd_resource_stimulus_cf, /* Create RBRs failure */ + usbfront_hcd_resource_stimulus_tc, /* Transaction complete */ + usbfront_hcd_resource_stimulus_rc, /* Revoke RBRs complete */ + usbfront_hcd_resource_stimulus_uc, /* Unlink complete */ +} +usbfront_hcd_resource_stimulus; + +static void usbfront_hcd_resource_handle_stimulus +( + struct usbfront_hcd_resource * resource, + usbfront_hcd_resource_stimulus stimulus +); + +static void usbfront_hcd_resource_create_rbrs_1( xenidc_callback * callback ); + +static void usbfront_hcd_resource_submit_transaction_1 + ( xenidc_callback * callback ); + +static void usbfront_hcd_resource_revoke_rbrs_1 + ( xenidc_callback * callback ); + +static void usbfront_hcd_resource_submit_unlink_1 + ( xenidc_callback * callback ); + +static void usbfront_hcd_resource_complete_1( void * context ); + +static int usbfront_hcd_resource_init_or_exit +( + struct usbfront_hcd_resource * resource, + struct usbfront_device * device, + int index, + xenidc_callback_function * callback, + int exit +) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + xenidc_callback_init( &resource->callback, callback ); + + resource->device = device; + + resource->index = index; + + resource->schedule = (usbif_isochronous_io_schedule_element *) + __get_free_page( GFP_KERNEL ); + + if( resource->schedule == NULL ) + { + return_value = -ENOMEM; + + goto EXIT_NO_SCHEDULE; + } + + spin_lock_init( &resource->lock ); + + resource->state = usbfront_hcd_resource_state_i; + + xenidc_create_rbr_request_element_init + ( &resource->rbr_request_element[ 0 ] ); + + xenidc_create_rbr_request_element_init + ( &resource->rbr_request_element[ 1 ] ); + + xenidc_reserve_and_create_rbr_request_init + ( + &resource->rbr_request, + usbfront_hcd_resource_create_rbrs_1, + usbfront_hcd_resource_revoke_rbrs_1 + ); + + xenidc_endpoint_transaction_init + ( + &resource->io_transaction, + usbfront_hcd_resource_submit_transaction_1 + ); + + xenidc_endpoint_transaction_set_parameters_lbr + ( + &resource->io_transaction, + xenidc_vaddress_create_lbr + ( + &resource->io_parameters, + sizeof( resource->io_parameters ) + ) + ); + + xenidc_endpoint_transaction_set_status_lbr + ( + &resource->io_transaction, + xenidc_vaddress_create_lbr + ( + &resource->io_status, + sizeof( resource->io_status ) + ) + ); + + xenidc_endpoint_message_init + ( &resource->unlink_message, usbfront_hcd_resource_submit_unlink_1 ); + + xenidc_endpoint_message_set_message_lbr + ( + &resource->unlink_message, + xenidc_vaddress_create_lbr + ( + &resource->unlink_message_body, + sizeof( resource->unlink_message_body ) + ) + ); + + xenidc_work_init + ( + &resource->complete_1_work, + usbfront_hcd_resource_complete_1, + resource + ); + + return 0; + + EXIT: + + free_page( (unsigned long)resource->schedule ); + + EXIT_NO_SCHEDULE: + + return return_value; + } +} + +int usbfront_hcd_resource_init +( + struct usbfront_hcd_resource * resource, + struct usbfront_device * device, + int index, + xenidc_callback_function * callback +) +{ + trace(); + + return usbfront_hcd_resource_init_or_exit + ( resource, device, index, callback, 0 ); +} + +void usbfront_hcd_resource_exit( struct usbfront_hcd_resource * resource ) +{ + trace(); + + usbfront_hcd_resource_init_or_exit( resource, NULL, 0, NULL, 1 ); +} + +void usbfront_hcd_resource_start_urb + ( struct usbfront_hcd_resource * resource, struct urb * urb ) +{ + trace(); + + urb->hcpriv = resource; + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + resource->urb = urb; + + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_st ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static DEFINE_RWLOCK( dequeue_lock ); + +void usbfront_hcd_resource_dequeue_urb( struct urb * urb ) +{ + trace(); + + { + unsigned long flags; + + write_lock_irqsave( &dequeue_lock, flags ); + + if( urb->hcpriv != NULL ) + { + struct usbfront_hcd_resource * resource = + (struct usbfront_hcd_resource *)urb->hcpriv; + + unsigned long flags2; + + spin_lock_irqsave( &resource->lock, flags2 ); + + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_dq ); + + spin_unlock_irqrestore( &resource->lock, flags2 ); + } + + write_unlock_irqrestore( &dequeue_lock, flags ); + } +} + +static void usbfront_hcd_resource_invalid_stimulus +( + struct usbfront_hcd_resource * resource, + usbfront_hcd_resource_stimulus stimulus +); + +static void usbfront_hcd_resource_create_rbrs + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_abort_create_rbrs + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_set_aborted_error + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_submit_transaction + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_revoke_rbrs + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_submit_unlink + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_complete + ( struct usbfront_hcd_resource * resource ); + +static void usbfront_hcd_resource_handle_stimulus +( + struct usbfront_hcd_resource * resource, + usbfront_hcd_resource_stimulus stimulus +) +{ + trace(); + + switch( resource->state ) + { + case usbfront_hcd_resource_state_i: + /* Initialised. */ + /* Maybe completing previous URB. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_st: + resource->state = usbfront_hcd_resource_state_i_st; + usbfront_hcd_resource_create_rbrs( resource ); + break; + /* A dequeue is possible whilst we are making the complete */ + /* response after we have transitioned back to the i state. */ + case usbfront_hcd_resource_stimulus_dq: + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st: + /* Creating RBRs. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + resource->state = usbfront_hcd_resource_state_i_st_dq; + usbfront_hcd_resource_abort_create_rbrs( resource ); + break; + case usbfront_hcd_resource_stimulus_cs: + resource->state = usbfront_hcd_resource_state_i_st_cs; + usbfront_hcd_resource_submit_transaction( resource ); + break; + case usbfront_hcd_resource_stimulus_cf: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete( resource ); + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st_dq: + /* Creating RBRs. */ + /* Dequeue URB. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_cs: + resource->state = usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_set_aborted_error( resource ); + usbfront_hcd_resource_revoke_rbrs( resource ); + break; + case usbfront_hcd_resource_stimulus_cf: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete( resource ); + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs: + /* Transaction submitted. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + resource->state = usbfront_hcd_resource_state_i_st_cs_dq; + usbfront_hcd_resource_submit_unlink( resource ); + break; + case usbfront_hcd_resource_stimulus_tc: + resource->state = usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_revoke_rbrs( resource ); + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st_dq_cs: + /* Revoking RBRs. */ + /* Maybe Dequeue URB. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_rc: + resource->state = usbfront_hcd_resource_state_i; + usbfront_hcd_resource_complete( resource ); + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs_dq: + /* Transaction submitted. */ + /* Unlink submitted. */ + /* Dequeue URB. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_tc: + case usbfront_hcd_resource_stimulus_uc: + resource->state = usbfront_hcd_resource_state_i_st_cs_dq_tc; + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + case usbfront_hcd_resource_state_i_st_cs_dq_tc: + /* Transaction submitted or Unlink submitted. */ + /* Dequeue URB. */ + switch( stimulus ) + { + case usbfront_hcd_resource_stimulus_dq: + break; + case usbfront_hcd_resource_stimulus_tc: + case usbfront_hcd_resource_stimulus_uc: + resource->state = usbfront_hcd_resource_state_i_st_dq_cs; + usbfront_hcd_resource_revoke_rbrs( resource ); + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } + break; + default: + usbfront_hcd_resource_invalid_stimulus( resource, stimulus ); + break; + } +} + +static void usbfront_hcd_resource_invalid_stimulus +( + struct usbfront_hcd_resource * resource, + usbfront_hcd_resource_stimulus stimulus +) +{ + trace(); + + printk + ( + KERN_ERR "usbfront: hcd resource %p in state %d" + "received invalid stimulus %d", + resource, + resource->state, + stimulus + ); +} + +static void usbfront_hcd_resource_create_rbrs + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + xenidc_create_rbr_request_element_ensure_removed + ( &resource->rbr_request_element[ 0 ] ); + + xenidc_create_rbr_request_element_ensure_removed + ( &resource->rbr_request_element[ 1 ] ); + + xenidc_create_rbr_request_element_set_lbr + ( + &resource->rbr_request_element[ 0 ], + xenidc_vaddress_create_lbr + ( + resource->urb->transfer_buffer, + resource->urb->transfer_buffer_length + ) + ); + + xenidc_reserve_and_create_rbr_request_add_element + ( + &resource->rbr_request, + &resource->rbr_request_element[ 0 ] + ); + + if( usb_pipetype( resource->urb->pipe ) == PIPE_ISOCHRONOUS ) + { + if + ( + resource->urb->number_of_packets + > + ( PAGE_SIZE / sizeof( usbif_isochronous_io_schedule_element ) ) + ) + { + goto SCHEDULE_TOO_BIG; + } + + { + usbif_isochronous_io_schedule_element * element = + resource->schedule; + + int i; + + for( i = 0; i < resource->urb->number_of_packets; i++ ) + { + memset( &element[ i ], 0, sizeof( element[ i ] ) ); + + element[ i ].offset = + resource->urb->iso_frame_desc[ i ].offset; + element[ i ].length = + resource->urb->iso_frame_desc[ i ].length; + } + } + + xenidc_create_rbr_request_element_set_lbr + ( + &resource->rbr_request_element[ 1 ], + xenidc_vaddress_create_lbr + ( + resource->schedule, + sizeof( usbif_isochronous_io_schedule_element ) + * + resource->urb->number_of_packets + ) + ); + + xenidc_reserve_and_create_rbr_request_add_element + ( + &resource->rbr_request, + &resource->rbr_request_element[ 1 ] + ); + } + + xenidc_rbr_provider_pool_reserve_and_create_rbrs + ( + usbfront_hcd_resource_rbr_provider_pool, + &resource->rbr_request + ); + + return; + + SCHEDULE_TOO_BIG: + + xenidc_callback_complete + ( + xenidc_reserve_and_create_rbr_request_to_create_callback + ( &resource->rbr_request ), + XENIDC_ERROR_TOO_BIG + ); +} + +static void usbfront_hcd_resource_create_rbrs_1( xenidc_callback * callback ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = container_of + ( + xenidc_reserve_and_create_rbr_request_create_callback_to + ( callback ), + struct usbfront_hcd_resource, + rbr_request + ); + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + if( xenidc_callback_query_error( callback ) == XENIDC_ERROR_SUCCESS ) + { + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_cs ); + } + else + { + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_cf ); + } + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void usbfront_hcd_resource_abort_create_rbrs + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + xenidc_rbr_provider_pool_abort_reserve_and_create_rbrs + ( + usbfront_hcd_resource_rbr_provider_pool, + &resource->rbr_request + ); +} + +static void usbfront_hcd_resource_set_aborted_error + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + xenidc_callback_set_error + ( + xenidc_reserve_and_create_rbr_request_to_create_callback + ( &resource->rbr_request ), + XENIDC_ERROR_ABORTED + ); +} + +static void usbfront_hcd_resource_submit_transaction + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + memset( &resource->io_parameters, 0, sizeof( resource->io_parameters ) ); + memset( &resource->io_status, 0, sizeof( resource->io_status ) ); + + resource->io_parameters.header.header.transaction_type = + USBIF_TRANSACTION_TYPE_IO; + + { + struct urb * urb = resource->urb; + + resource->io_parameters.header.device_number = + usb_pipedevice( urb->pipe ); + + resource->io_parameters.header.endpoint = + usb_pipeendpoint( urb->pipe ); + + resource->io_parameters.header.direction = usb_pipein( urb->pipe ) ? + USBIF_IO_TRANSACTION_DIRECTION_IN : + USBIF_IO_TRANSACTION_DIRECTION_OUT; + + resource->io_parameters.header.unlink_id = resource->index; + + if( urb->transfer_flags & URB_SHORT_NOT_OK ) + { + resource->io_parameters.header.flags |= + USBIF_IO_FLAGS_SHORT_NOT_OK; + } + if( urb->transfer_flags & URB_ZERO_PACKET ) + { + resource->io_parameters.header.flags |= + USBIF_IO_FLAGS_ZERO_PACKET; + } + + resource->io_parameters.header.rbr = + xenidc_create_rbr_request_element_query_rbr + ( &resource->rbr_request_element[ 0 ] ); + + if( usb_pipetype( urb->pipe ) == PIPE_CONTROL ) + { + resource->io_parameters.header.io_transaction_type = + USBIF_IO_TRANSACTION_TYPE_CONTROL; + + ASSERT( urb->setup_packet != NULL ); + + memcpy + ( resource->io_parameters.control.setup, urb->setup_packet, 8 ); + } + else if( usb_pipetype( urb->pipe ) == PIPE_BULK ) + { + resource->io_parameters.header.io_transaction_type = + USBIF_IO_TRANSACTION_TYPE_BULK; + } + else if( usb_pipetype( urb->pipe ) == PIPE_INTERRUPT ) + { + resource->io_parameters.header.io_transaction_type = + USBIF_IO_TRANSACTION_TYPE_INTERRUPT; + + resource->io_parameters.interrupt.interval = urb->interval; + } + else if( usb_pipetype( urb->pipe ) == PIPE_ISOCHRONOUS ) + { + resource->io_parameters.header.io_transaction_type = + USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS; + + resource->io_parameters.isochronous.interval = urb->interval; + + resource->io_parameters.isochronous.schedule_rbr = + xenidc_create_rbr_request_element_query_rbr + ( &resource->rbr_request_element[ 1 ] ); + + resource->io_parameters.isochronous.packet_count = + urb->number_of_packets; + } + } + + usbfront_device_submit_transaction + ( resource->device, &resource->io_transaction ); +} + +static void usbfront_hcd_resource_submit_transaction_1 + ( xenidc_callback * callback ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = container_of + ( + xenidc_endpoint_transaction_callback_to( callback ), + struct usbfront_hcd_resource, + io_transaction + ); + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_tc ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void usbfront_hcd_resource_revoke_rbrs + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + xenidc_rbr_provider_pool_revoke_and_unreserve_rbrs + ( + usbfront_hcd_resource_rbr_provider_pool, + &resource->rbr_request + ); +} + +static void usbfront_hcd_resource_revoke_rbrs_1 + ( xenidc_callback * callback ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = container_of + ( + xenidc_reserve_and_create_rbr_request_revoke_callback_to + ( callback ), + struct usbfront_hcd_resource, + rbr_request + ); + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_rc ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void usbfront_hcd_resource_submit_unlink + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + memset + ( + &resource->unlink_message_body, + 0, + sizeof( resource->unlink_message_body ) + ); + + resource->unlink_message_body.header.message_type = + USBIF_MESSAGE_TYPE_UNLINK; + + resource->unlink_message_body.unlink_id = resource->index; + + usbfront_device_submit_message + ( resource->device, &resource->unlink_message ); +} + +static void usbfront_hcd_resource_submit_unlink_1( xenidc_callback * callback ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = container_of + ( + xenidc_endpoint_message_callback_to( callback ), + struct usbfront_hcd_resource, + unlink_message + ); + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + usbfront_hcd_resource_handle_stimulus + ( resource, usbfront_hcd_resource_stimulus_uc ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void usbfront_hcd_resource_complete + ( struct usbfront_hcd_resource * resource ) +{ + trace(); + + { + int scheduled = xenidc_work_schedule( &resource->complete_1_work ); + + ASSERT( scheduled ); + } +} + +static void usbfront_hcd_resource_complete_1( void * context ) +{ + trace(); + + { + struct usbfront_hcd_resource * resource = + (struct usbfront_hcd_resource *)context; + + struct urb * urb = resource->urb; + + xenidc_error error; + + if + ( + ( + ( + error = xenidc_callback_query_error + ( + xenidc_reserve_and_create_rbr_request_to_create_callback + ( &resource->rbr_request ) + ) + ) + == + XENIDC_ERROR_SUCCESS + ) + && + ( + ( + error = xenidc_callback_query_error + ( + xenidc_reserve_and_create_rbr_request_to_revoke_callback + ( &resource->rbr_request ) + ) + ) + == + XENIDC_ERROR_SUCCESS + ) + ) + { + urb->status = usbif_error_map_to_local + ( + xenidc_endpoint_transaction_query_error + ( &resource->io_transaction ) + ); + urb->actual_length = resource->io_status.length; + + if( usb_pipetype( urb->pipe ) == PIPE_ISOCHRONOUS ) + { + int i; + + for( i = 0; i < urb->number_of_packets; i++ ) + { + usbif_isochronous_io_schedule_element * schedule = + &resource->schedule[ i ]; + + urb->iso_frame_desc[ i ].actual_length = schedule->length; + urb->iso_frame_desc[ i ].status = + usbif_error_map_to_local( schedule->error ); + } + + /* FIXME isoc error_count ?!? */ + } + } + else + { + urb->status = usbif_error_map_to_local( error ); + urb->actual_length = 0; + } + + { + unsigned long flags; + + read_lock_irqsave( &dequeue_lock, flags ); + + urb->hcpriv = 0; + + read_unlock_irqrestore( &dequeue_lock, flags ); + } + + { + unsigned long flags; + + local_irq_save( flags ); + + usb_hcd_giveback_urb( /* FIXME hcd */ NULL, urb, NULL ); + + local_irq_restore( flags ); + } + + xenidc_callback_success( &resource->callback ); + } +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_hcd_resource.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,104 @@ +/*****************************************************************************/ +/* A resource used by the usbfront host controller device driver to process */ +/* an URB by translating the URB and optional unlink request into */ +/* a transaction and corresponding optional unlink message for submission to */ +/* the back end. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_HCD_RESOURCE_H +#define USBFRONT_HCD_RESOURCE_H + +#include +#include +#include +#include "usbfront_device.h" + +int usbfront_hcd_resource_class_init( void ); + +void usbfront_hcd_resource_class_exit( void ); + +typedef enum +{ + usbfront_hcd_resource_state_i, + usbfront_hcd_resource_state_i_st, + usbfront_hcd_resource_state_i_st_dq, + usbfront_hcd_resource_state_i_st_cs, + usbfront_hcd_resource_state_i_st_dq_cs, + usbfront_hcd_resource_state_i_st_cs_dq, + usbfront_hcd_resource_state_i_st_cs_dq_tc +} +usbfront_hcd_resource_state; + +struct usbfront_hcd_resource +{ + xenidc_callback callback; + struct usbfront_device * device; + int index; + usbif_isochronous_io_schedule_element * schedule; + spinlock_t lock; + usbfront_hcd_resource_state state; + struct urb * urb; + xenidc_create_rbr_request_element rbr_request_element[ 2 ]; + xenidc_reserve_and_create_rbr_request rbr_request; + xenidc_endpoint_transaction io_transaction; + usbif_io_transaction_parameters io_parameters; + usbif_io_transaction_status io_status; + xenidc_endpoint_message unlink_message; + usbif_unlink_message_body unlink_message_body; + xenidc_work complete_1_work; +}; + +#define USBFRONT_HCD_RESOURCE_LINK callback.XENIDC_CALLBACK_LINK + +static inline struct list_head * usbfront_hcd_resource_to_link + ( struct usbfront_hcd_resource * resource ) +{ + return xenidc_callback_to_link( &resource->callback ); +} + +static inline struct usbfront_hcd_resource * + usbfront_hcd_resource_callback_to( xenidc_callback * callback ) +{ + return container_of( callback, struct usbfront_hcd_resource, callback ); +} + +static inline struct usbfront_device * usbfront_hcd_resource_query_device + ( struct usbfront_hcd_resource * resource ) +{ + return resource->device; +} + +int usbfront_hcd_resource_init +( + struct usbfront_hcd_resource * resource, + struct usbfront_device * device, + int index, + xenidc_callback_function * callback +); + +void usbfront_hcd_resource_exit + ( struct usbfront_hcd_resource * resource ); + +void usbfront_hcd_resource_start_urb + ( struct usbfront_hcd_resource * resource, struct urb * urb ); + +void usbfront_hcd_resource_dequeue_urb( struct urb * urb ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_module.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_module.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,92 @@ +/*****************************************************************************/ +/* Front-end module for Xen USB split driver. */ +/* */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#include +#include +#include "usbfront_device.h" +#include "usbfront_driver.h" +#include "usbfront_trace.h" + +static int usbfront_module_init_or_exit( int exit ) +{ + trace(); + + { + int return_value = 0; + + if( usb_disabled() ) + { + return_value = -ENODEV; + + goto EXIT_NO_USB; + } + + if( exit ) + { + goto EXIT; + } + + if( ( return_value = usbfront_driver_class_init() ) != 0 ) + { + goto EXIT_NO_DRIVER_CLASS; + } + + if( ( return_value = usbfront_device_class_init() ) != 0 ) + { + goto EXIT_NO_DEVICE_CLASS; + } + + return 0; + + EXIT: + + usbfront_device_class_exit(); + + EXIT_NO_DEVICE_CLASS: + + usbfront_driver_class_exit(); + + EXIT_NO_DRIVER_CLASS: + + EXIT_NO_USB: + + return return_value; + } +} + +static int __init usbfront_module_init( void ) +{ + trace(); + + return usbfront_module_init_or_exit( 0 ); +} + +static void __exit usbfront_module_exit( void ) +{ + trace(); + + (void)usbfront_module_init_or_exit( 1 ); +} + +module_init( usbfront_module_init ); +module_exit( usbfront_module_exit ); + +MODULE_LICENSE( "GPL" ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_sll.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_sll.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,133 @@ +/*****************************************************************************/ +/* A singly linked list implementation */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_SLL_H +#define USBFRONT_SLL_H + +#include "usbfront_assert.h" + +struct usbfront_slk +{ + struct usbfront_slk * next; +}; + +struct usbfront_sll +{ + struct usbfront_slk * first; + struct usbfront_slk * last; +}; + +static inline void usbfront_slk_init( struct usbfront_slk * slk ) +{ + slk->next = slk; +} + +static inline int usbfront_slk_is_singular( struct usbfront_slk * slk ) +{ + return ( slk->next == slk ); +} + +static inline void usbfront_sll_init( struct usbfront_sll * sll ) +{ + sll->first = 0; +} + +static inline int usbfront_sll_is_empty( struct usbfront_sll * sll ) +{ + return ( sll->first == 0 ); +} + +static inline void usbfront_sll_add_last + ( struct usbfront_sll * sll, struct usbfront_slk * slk ) +{ + ASSERT( usbfront_slk_is_singular( slk ) ); + + slk->next = 0; + + if( sll->first != 0 ) + { + sll->last->next = slk; + } + else + { + sll->first = slk; + } + + sll->last = slk; +} + +static inline struct usbfront_slk * usbfront_sll_remove_first + ( struct usbfront_sll * sll ) +{ + struct usbfront_slk * slk = sll->first; + + if( slk != 0 ) + { + sll->first = slk->next; + + slk->next = slk; + } + + return slk; +} + +/* Returns 1 if slk removed, 0 otherwise. */ + +static inline int usbfront_sll_remove_slk + ( struct usbfront_sll * sll, struct usbfront_slk * slk ) +{ + struct usbfront_slk * prev_slk = NULL; + struct usbfront_slk * next_slk = sll->first; + + while( next_slk != NULL ) + { + if( next_slk != slk ) + { + prev_slk = next_slk; + next_slk = next_slk->next; + } + else + { + next_slk = next_slk->next; + + if( prev_slk != NULL ) + { + prev_slk->next = next_slk; + } + else + { + sll->first = next_slk; + } + + if( next_slk == NULL ) + { + sll->last = prev_slk; + } + + slk->next = slk; + + return 1; + } + } + + return 0; +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_trace.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/usbfront/usbfront_trace.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,53 @@ +/*****************************************************************************/ +/* Simple trace macros */ +/* Copyright (c) 2005 Harry Butterworth IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by the */ +/* Free Software Foundation; either version 2 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */ +/* Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License along */ +/* with this program; if not, write to the Free Software Foundation, Inc., */ +/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*****************************************************************************/ + +#ifndef USBFRONT_TRACE_H +#define USBFRONT_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_USBDEV_FRONTEND_TRACE + +#define trace0( format ) \ +printk( KERN_INFO "usbfront %s:" format "\n", __PRETTY_FUNCTION__ ) + +#define trace1( format, a0 ) \ +printk( KERN_INFO "usbfront %s:" format "\n", __PRETTY_FUNCTION__, a0 ) + +#define trace2( format, a0, a1 ) \ +printk( KERN_INFO "usbfront %s:" format "\n", __PRETTY_FUNCTION__, a0, a1 ) + +#define trace3( format, a0, a1, a2 ) \ +printk( KERN_INFO "usbfront %s:" format "\n", __PRETTY_FUNCTION__, a0, a1, a2 ) + +#define trace() trace0( "" ) + +#else + +#define trace0( format ) +#define trace1( format,a0 ) +#define trace2( format,a0, a1 ) +#define trace3( format,a0, a1, a2 ) +#define trace() + +#endif + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/Makefile Sun Oct 30 16:03:34 2005 @@ -0,0 +1,17 @@ +obj-y += xenidc.o + +xenidc-objs = +xenidc-objs += xenidc_callback.o +xenidc-objs += xenidc_concatenate.o +xenidc-objs += xenidc_endpoint.o +xenidc-objs += xenidc_gateway.o +xenidc-objs += xenidc_gateway_initiator_resource.o +xenidc-objs += xenidc_gateway_target_resource.o +xenidc-objs += xenidc_gnttab_channel.o +xenidc-objs += xenidc_local_buffer_reference.o +xenidc-objs += xenidc_rbr_mapper_pool.o +xenidc-objs += xenidc_rbr_provider_pool.o +xenidc-objs += xenidc_vaddress.o +xenidc-objs += xenidc_work.o +xenidc-objs += xenidc_wrapping.o +xenidc-objs += xenidc_xbgt_channel.o diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_callback.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_callback.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* 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 */ +/* */ +/*****************************************************************************/ + +#include "xenidc_callback.h" +#include + +void xenidc_callback_serialiser_function( void * context ) +{ + xenidc_callback_serialiser * serialiser = + (xenidc_callback_serialiser *)context; + + unsigned long flags; + + spin_lock_irqsave( &serialiser->lock, flags ); + + while + ( + ( !list_empty( &serialiser->list ) ) + && + ( !serialiser->running ) + ) + { + xenidc_callback * callback = + xenidc_callback_link_to( serialiser->list.next ); + + list_del_init( xenidc_callback_to_link( callback ) ); + + serialiser->running = 1; + + spin_unlock_irqrestore( &serialiser->lock, flags ); + + xenidc_callback_complete_synchronously( callback ); + + spin_lock_irqsave( &serialiser->lock, flags ); + + serialiser->running = 0; + } + + spin_unlock_irqrestore( &serialiser->lock, flags ); +} + +EXPORT_SYMBOL( xenidc_callback_serialiser_function ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_channel_ring.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_channel_ring.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,44 @@ +/*****************************************************************************/ +/* Xen inter-domain communication channel ring structure definitions. */ +/* */ +/* 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 _XENIDC_CHANNEL_RING_H +#define _XENIDC_CHANNEL_RING_H + +#include + +typedef struct xenidc_channel_ring_header_struct xenidc_channel_ring_header; + +struct xenidc_channel_ring_header_struct +{ + u16 this_ring_producer_offset; + u16 other_ring_consumer_offset; +}; + +typedef struct xenidc_channel_ring_element_header_struct + xenidc_channel_ring_element_header; + +struct xenidc_channel_ring_element_header_struct +{ + u16 length; + u8 reserved1[6]; +}; + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_concatenate.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_concatenate.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,140 @@ +/*****************************************************************************/ +/* Xen inter-domain communication concatenate local buffer reference type. */ +/* */ +/* 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 "xenidc_trace.h" + +static xenidc_buffer_type xenidc_concatenate_type; + +static xenidc_local_buffer_reference xenidc_concatenate_resolve +( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr +) +{ + trace(); + + { + xenidc_local_buffer_reference resolved_lbr; + + xenidc_concatenate_base * base = (xenidc_concatenate_base *)lbr->base; + + if( lbr->byte_offset < base->head->byte_count ) + { + resolved_lbr = *base->head; + + xenidc_local_buffer_reference_subrange + ( &resolved_lbr, lbr->byte_offset, lbr->byte_count ); + } + else + { + resolved_lbr = *base->tail; + + xenidc_local_buffer_reference_subrange + ( + &resolved_lbr, + lbr->byte_offset - base->head->byte_count, + lbr->byte_count + ); + } + + return resolved_lbr; + } +} + +static void xenidc_concatenate_init( void ) +{ + trace(); + + { + static DEFINE_RWLOCK( xenidc_concatenate_lock ); + + unsigned long flags; + + read_lock_irqsave( &xenidc_concatenate_lock, flags ); + + { + static int initialised = 0; + + if( !initialised ) + { + read_unlock_irqrestore + ( &xenidc_concatenate_lock, flags ); + + write_lock_irqsave + ( &xenidc_concatenate_lock, flags ); + + if( !initialised ) + { + static xenidc_buffer_virtual_class + xenidc_concatenate_virtual_class; + + xenidc_concatenate_type = + xenidc_local_buffer_reference_register_buffer_virtual_class + ( + &xenidc_concatenate_virtual_class, + xenidc_concatenate_resolve, + NULL + ); + + initialised = 1; + } + + write_unlock_irqrestore + ( &xenidc_concatenate_lock, flags ); + + read_lock_irqsave + ( &xenidc_concatenate_lock, flags ); + } + } + + read_unlock_irqrestore( &xenidc_concatenate_lock, flags ); + } +} + +xenidc_local_buffer_reference xenidc_concatenate_create_lbr +( + xenidc_concatenate_base * base, + xenidc_local_buffer_reference * head, + xenidc_local_buffer_reference * tail +) +{ + trace(); + + xenidc_concatenate_init(); + + base->head = head; + base->tail = tail; + + { + xenidc_local_buffer_reference lbr; + + lbr.type = xenidc_concatenate_type; + lbr.base = base; + lbr.byte_offset = 0; + lbr.byte_count = head->byte_count + tail->byte_count; + + return lbr; + } +} + +EXPORT_SYMBOL( xenidc_concatenate_create_lbr ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_endpoint.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_endpoint.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,176 @@ +/*****************************************************************************/ +/* Xen inter-domain communication API, endpoint object. */ +/* */ +/* 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 "xenidc_trace.h" + +int xenidc_endpoint_init +( + xenidc_endpoint * endpoint, + + void ( * connect )( xenidc_endpoint * endpoint ), + void ( * handle_message ) + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ), + void ( * handle_transaction ) + ( + xenidc_endpoint * endpoint, + xenidc_endpoint_transaction * transaction + ), + void ( * disconnect ) + ( xenidc_endpoint * endpoint, xenidc_callback * callback ), + u32 initiator_quota, + xenidc_buffer_byte_count initiator_maximum_byte_count, + u32 target_quota, + xenidc_buffer_byte_count target_maximum_byte_count +) +{ + trace(); + + { + int return_value = xenidc_xbgt_channel_init( &endpoint->channel ); + + if( return_value != 0 ) + { + goto EXIT_NO_CHANNEL; + } + + return_value = xenidc_gateway_init + ( + &endpoint->gateway, + xenidc_xbgt_channel_to_channel( &endpoint->channel ), + (void (*)( xenidc_gateway * ))connect, + (void (*)( xenidc_gateway *, xenidc_endpoint_message* )) + handle_message, + (void (*)( xenidc_gateway *, xenidc_endpoint_transaction *)) + handle_transaction, + (void (*)( xenidc_gateway *, xenidc_callback *))disconnect, + initiator_quota, + initiator_maximum_byte_count, + target_quota, + target_maximum_byte_count + ); + + if( return_value != 0 ) + { + goto EXIT_NO_GATEWAY; + } + + return 0; + + EXIT_NO_GATEWAY: + + xenidc_xbgt_channel_exit( &endpoint->channel ); + + EXIT_NO_CHANNEL: + + return return_value; + } +} + +void xenidc_endpoint_create + ( xenidc_endpoint * endpoint, xenidc_address address ) +{ + trace(); + + xenidc_xbgt_channel_connect + ( + &endpoint->channel, + xenidc_address_query_local_domain( &address ), + xenidc_address_query_remote_domain( &address ), + xenidc_address_query_remote_domain_id( &address ) + ); +} + +void xenidc_endpoint_submit_message + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ) +{ + trace(); + + xenidc_gateway_submit_message( &endpoint->gateway, message ); +} + +void xenidc_endpoint_submit_transaction + ( xenidc_endpoint * endpoint, xenidc_endpoint_transaction * transaction ) +{ + trace(); + + xenidc_gateway_submit_transaction( &endpoint->gateway, transaction ); +} + +typedef struct xenidc_endpoint_callback_struct xenidc_endpoint_callback; + +struct xenidc_endpoint_callback_struct +{ + xenidc_callback callback; + int destroyed; +}; + +static void xenidc_endpoint_destroy_1( xenidc_callback * callback ); + +void xenidc_endpoint_destroy( xenidc_endpoint * endpoint ) +{ + trace(); + + { + xenidc_endpoint_callback endpoint_callback; + + xenidc_callback_init + ( &endpoint_callback.callback, xenidc_endpoint_destroy_1 ); + + endpoint_callback.destroyed = 0; + + xenidc_xbgt_channel_disconnect + ( &endpoint->channel, &endpoint_callback.callback ); + + xenidc_work_until( endpoint_callback.destroyed ); + } +} + +static void xenidc_endpoint_destroy_1( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_endpoint_callback * endpoint_callback = + container_of( callback, xenidc_endpoint_callback, callback ); + + endpoint_callback->destroyed = 1; + + xenidc_work_wake_up(); + } +} + +void xenidc_endpoint_exit( xenidc_endpoint * endpoint ) +{ + trace(); + + xenidc_gateway_exit( &endpoint->gateway ); + + xenidc_xbgt_channel_exit( &endpoint->channel ); +} + +EXPORT_SYMBOL( xenidc_endpoint_init ); +EXPORT_SYMBOL( xenidc_endpoint_create ); +EXPORT_SYMBOL( xenidc_endpoint_submit_message ); +EXPORT_SYMBOL( xenidc_endpoint_submit_transaction ); +EXPORT_SYMBOL( xenidc_endpoint_destroy ); +EXPORT_SYMBOL( xenidc_endpoint_exit ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1344 @@ +#include +#include +#include +#include "xenidc_gateway_initiator_resource.h" +#include "xenidc_gateway_target_resource.h" +#include "xenidc_trace.h" + +typedef enum +{ + xenidc_gateway_stimulus_mt, /* Message or transaction queued. */ + xenidc_gateway_stimulus_cc, /* Channel connect. */ + xenidc_gateway_stimulus_cm, /* Channel message. */ + xenidc_gateway_stimulus_cd, /* Channel disconnect. */ + xenidc_gateway_stimulus_km, /* Kick messages and transactions completed. */ + xenidc_gateway_stimulus_kc, /* Kick channel messages completed. */ + xenidc_gateway_stimulus_ic, /* Initiator resource completed. */ + xenidc_gateway_stimulus_ii, /* Initiator resources idle. */ + xenidc_gateway_stimulus_tc, /* Target resource completed. */ + xenidc_gateway_stimulus_ti, /* Target resources idle. */ + xenidc_gateway_stimulus_lc, /* Client connect completed. */ + xenidc_gateway_stimulus_lg, /* Client disconnect called. */ + xenidc_gateway_stimulus_ld, /* Client disconnect completed. */ +} +xenidc_gateway_stimulus; + +static void xenidc_gateway_handle_stimulus + ( xenidc_gateway * gateway, xenidc_gateway_stimulus stimulus ); + +static void xenidc_gateway_channel_connect( void * context ); + +static void xenidc_gateway_handle_channel_message + ( void * context, xenidc_channel_message * message ); + +static void xenidc_gateway_channel_disconnect + ( void * context, xenidc_callback * callback ); + +static int xenidc_gateway_init_or_exit( xenidc_gateway * gateway, int exit ); + +int xenidc_gateway_init +( + xenidc_gateway * gateway, + xenidc_channel * channel, + void ( * connect )( xenidc_gateway * gateway ), + void ( * handle_message ) + ( xenidc_gateway * gateway, xenidc_gateway_message * message ), + void ( * handle_transaction ) + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ), + void ( * disconnect ) + ( xenidc_gateway * gateway, xenidc_callback * callback ), + u32 initiator_quota, + xenidc_buffer_byte_count initiator_maximum_byte_count, + u32 target_quota, + xenidc_buffer_byte_count target_maximum_byte_count +) +{ + trace(); + + gateway->channel = channel; + gateway->connect = connect; + gateway->handle_message = handle_message; + gateway->handle_transaction = handle_transaction; + gateway->disconnect = disconnect; + + gateway->initiator_quota = initiator_quota; + gateway->target_quota = target_quota; + gateway->target_maximum_byte_count = target_maximum_byte_count; + + xenidc_channel_install_client + ( + channel, + gateway, + xenidc_gateway_channel_connect, + xenidc_gateway_handle_channel_message, + xenidc_gateway_channel_disconnect + ); + + return xenidc_gateway_init_or_exit( gateway, 0 ); +} + +static void xenidc_gateway_kick_messages_and_transactions_1( void * data ); + +static void xenidc_gateway_kick_messages_and_transactions_2 + ( xenidc_callback * callback ); + +static void xenidc_gateway_kick_channel_messages_1( void * data ); + +static void xenidc_gateway_kick_channel_messages_2 + ( xenidc_callback * callback ); + +static void xenidc_gateway_connect_client_1( void * data ); + +static void xenidc_gateway_disconnect_client_1( void * data ); + +static void xenidc_gateway_disconnect_client_2( xenidc_callback * callback ); + +static int xenidc_gateway_init_or_exit( xenidc_gateway * gateway, int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + INIT_LIST_HEAD( &gateway->initiator_resource_list ); + + if( gateway->initiator_quota != 0 ) + { + gateway->initiator_resources = vmalloc + ( + sizeof( xenidc_gateway_initiator_resource ) + * + gateway->initiator_quota + ); + + if( gateway->initiator_resources == NULL ) + { + trace0( "failed to allocate initiator resources" ); + + return_value = -ENOMEM; + + goto EXIT_NO_INITIATOR_RESOURCES; + } + + { + u32 i; + + for( i = 0; i < gateway->initiator_quota; i++ ) + { + xenidc_gateway_initiator_resource * resource = + &gateway->initiator_resources[ i ]; + + xenidc_gateway_initiator_resource_init + ( + resource, + gateway, + xenidc_gateway_kick_messages_and_transactions_2, + i + ); + + list_add_tail + ( + xenidc_gateway_initiator_resource_to_link( resource ), + &gateway->initiator_resource_list + ); + } + } + } + + INIT_LIST_HEAD( &gateway->target_resource_list ); + + if( gateway->target_quota != 0 ) + { + gateway->target_resources = vmalloc + ( + ( + sizeof( xenidc_gateway_target_resource ) + * + gateway->target_quota + ) + + + ( + gateway->target_maximum_byte_count + * + gateway->target_quota + ) + ); + + if( gateway->target_resources == NULL ) + { + trace0( "failed to allocate target resources" ); + + return_value = -ENOMEM; + + goto EXIT_NO_TARGET_RESOURCES; + } + + { + xenidc_local_buffer_reference buffer_area = + xenidc_vaddress_create_lbr + ( + &gateway->target_resources + [ gateway->target_quota ], + gateway->target_maximum_byte_count * + gateway->target_quota + ); + + u32 i; + + for( i = 0; i < gateway->target_quota; i++ ) + { + xenidc_gateway_target_resource * resource = + &gateway->target_resources[ i ]; + + xenidc_local_buffer_reference buffer = buffer_area; + + xenidc_local_buffer_reference_truncate + ( + &buffer, + gateway->target_maximum_byte_count + ); + + xenidc_gateway_target_resource_init + ( + resource, + gateway, + xenidc_gateway_kick_channel_messages_2, + buffer + ); + + list_add_tail + ( + xenidc_gateway_target_resource_to_link( resource ), + &gateway->target_resource_list + ); + + xenidc_local_buffer_reference_advance + ( + &buffer_area, + gateway->target_maximum_byte_count + ); + } + } + } + + spin_lock_init( &gateway->lock ); + + gateway->state = xenidc_gateway_state_i; + + INIT_LIST_HEAD( &gateway->message_and_transaction_list ); + INIT_LIST_HEAD( &gateway->channel_message_list ); + + xenidc_work_init + ( + &gateway->kick_messages_and_transactions_1_work, + xenidc_gateway_kick_messages_and_transactions_1, + gateway + ); + + xenidc_work_init + ( + &gateway->kick_channel_messages_1_work, + xenidc_gateway_kick_channel_messages_1, + gateway + ); + + xenidc_work_init + ( + &gateway->connect_client_1_work, + xenidc_gateway_connect_client_1, + gateway + ); + + xenidc_work_init + ( + &gateway->disconnect_client_1_work, + xenidc_gateway_disconnect_client_1, + gateway + ); + + xenidc_callback_init + ( + &gateway->disconnect_client_2_callback, + xenidc_gateway_disconnect_client_2 + ); + + gateway->kick_messages_and_transactions_out = 0; + gateway->kick_channel_messages_out = 0; + gateway->initiator_resources_out = 0; + gateway->target_resources_out = 0; + + return 0; + + EXIT: + + if( gateway->target_quota != 0 ) + { + vfree( gateway->target_resources ); + } + + EXIT_NO_TARGET_RESOURCES: + + if( gateway->initiator_quota != 0 ) + { + vfree( gateway->initiator_resources ); + } + + EXIT_NO_INITIATOR_RESOURCES: + + return return_value; + } +} + +void xenidc_gateway_submit_message + ( xenidc_gateway * gateway, xenidc_gateway_message * message ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + list_add_tail + ( + xenidc_gateway_message_to_link( message ), + &gateway->message_and_transaction_list + ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_mt ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +void xenidc_gateway_submit_transaction + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + list_add_tail + ( + xenidc_gateway_transaction_to_link( transaction ), + &gateway->message_and_transaction_list + ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_mt ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +void xenidc_gateway_exit( xenidc_gateway * gateway ) +{ + trace(); + + (void)xenidc_gateway_init_or_exit( gateway, 1 ); +} + +static void xenidc_gateway_channel_connect( void * context ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)context; + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_cc ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_handle_channel_message + ( void * context, xenidc_channel_message * message ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)context; + + xenidc_gateway_ring_element_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( &message->message_lbr, &header, sizeof( header ) ) + != + sizeof( header ) + ) + { + goto PROTOCOL_ERROR; + } + + if( header.type == XENIDC_GATEWAY_RING_ELEMENT_TYPE_STATUS ) + { + xenidc_local_buffer_reference status_lbr = message->message_lbr; + + xenidc_gateway_status_ring_element status_element; + + if + ( + xenidc_local_buffer_reference_copy_out + ( &status_lbr, &status_element, sizeof( status_element ) ) + != + sizeof( status_element ) + ) + { + goto PROTOCOL_ERROR; + } + + xenidc_local_buffer_reference_advance + ( &status_lbr, sizeof( status_element ) ); + + if( status_element.id >= gateway->initiator_quota ) + { + goto PROTOCOL_ERROR; + } + + { + xenidc_gateway_initiator_resource * resource = + &gateway->initiator_resources[ status_element.id ]; + + if + ( + xenidc_gateway_initiator_resource_handle_status + ( resource, status_element.error, status_lbr ) + != + 0 + ) + { + goto PROTOCOL_ERROR; + } + } + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + } + else + { + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + list_add_tail + ( + xenidc_channel_message_to_link( message ), + &gateway->channel_message_list + ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_cm ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } + } + + return; + + PROTOCOL_ERROR: + + xenidc_callback_complete + ( + xenidc_channel_message_to_callback( message ), + XENIDC_ERROR_INVALID_PROTOCOL + ); +} + +static void xenidc_gateway_channel_disconnect + ( void * context, xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)context; + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + gateway->channel_disconnect_callback = callback; + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_cd ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +void xenidc_gateway_submit_channel_message + ( xenidc_gateway * gateway, xenidc_channel_message * message ) +{ + trace(); + + xenidc_channel_submit_message( gateway->channel, message ); +} + +void xenidc_gateway_submit_message_to_client + ( xenidc_gateway * gateway, xenidc_gateway_message * message ) +{ + trace(); + + gateway->handle_message( gateway, message ); +} + +void xenidc_gateway_submit_transaction_to_client + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ) +{ + trace(); + + gateway->handle_transaction( gateway, transaction ); +} + +static void xenidc_gateway_invalid_stimulus + ( xenidc_gateway * gateway, xenidc_gateway_stimulus stimulus ); + +static void xenidc_gateway_kick_messages_and_transactions + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_fail_out_messages_and_transactions + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_kick_channel_messages( xenidc_gateway * gateway ); + +static void xenidc_gateway_complete_channel_messages + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_connect_client( xenidc_gateway * gateway ); + +static void xenidc_gateway_disconnect_client( xenidc_gateway * gateway ); + +static void xenidc_gateway_abort_initiator_resources + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_test_initiator_resources + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_test_target_resources( xenidc_gateway * gateway ); + +static void xenidc_gateway_complete_channel_disconnect + ( xenidc_gateway * gateway ); + +static void xenidc_gateway_handle_stimulus + ( xenidc_gateway * gateway, xenidc_gateway_stimulus stimulus ) +{ + trace3 + ( + "gateway %p in state %d received stimulus %d", + gateway, + gateway->state, + stimulus + ); + + switch( gateway->state ) + { + case xenidc_gateway_state_i: + /* Channel disconnected. */ + /* Client disconnected. */ + /* No messages or transactions queued. */ + /* No channel messages queued. */ + /* Initiator resources idle. */ + /* Target resources idle. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_cc: + gateway->state = xenidc_gateway_state_i_cc; + xenidc_gateway_connect_client( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc: + /* Channel connected. */ + /* Client connecting. */ + /* Maybe messages or transactions queued. */ + /* Maybe channel messages queued. */ + /* Initiator resources idle. */ + /* Target resources idle. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + case xenidc_gateway_stimulus_cm: + break; + case xenidc_gateway_stimulus_cd: + gateway->state = xenidc_gateway_state_i_cc_cd; + xenidc_gateway_complete_channel_messages( gateway ); + break; + case xenidc_gateway_stimulus_lc: + gateway->state = xenidc_gateway_state_i_cc_lc; + xenidc_gateway_kick_messages_and_transactions( gateway ); + xenidc_gateway_kick_channel_messages( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd: + /* Channel disconnecting. */ + /* Client connecting. */ + /* Maybe messages or transactions queued. */ + /* No channel messages queued. */ + /* Initiator resources idle. */ + /* Target resources idle. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + break; + case xenidc_gateway_stimulus_lc: + gateway->state = xenidc_gateway_state_i_cc_cd_lc; + xenidc_gateway_disconnect_client( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_lc: + /* Channel connected. */ + /* Client connected. */ + /* Maybe messages or transactions queued. */ + /* Maybe channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* Maybe kicking m/t. */ + /* Maybe kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + xenidc_gateway_kick_messages_and_transactions( gateway ); + break; + case xenidc_gateway_stimulus_cm: + xenidc_gateway_kick_channel_messages( gateway ); + break; + case xenidc_gateway_stimulus_cd: + gateway->state = xenidc_gateway_state_i_cc_lc_cd; + xenidc_gateway_complete_channel_messages( gateway ); + xenidc_gateway_kick_messages_and_transactions( gateway ); + xenidc_gateway_kick_channel_messages( gateway ); + break; + case xenidc_gateway_stimulus_km: + case xenidc_gateway_stimulus_kc: + break; + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + xenidc_gateway_kick_messages_and_transactions( gateway ); + break; + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + xenidc_gateway_kick_channel_messages( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_lc_cd: + /* Channel disconnecting. */ + /* Client connected. */ + /* Maybe messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* Kicking m/t. */ + /* Kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + break; + case xenidc_gateway_stimulus_km: + case xenidc_gateway_stimulus_kc: + gateway->state = xenidc_gateway_state_i_cc_lc_cd_km; + break; + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_lc_cd_km: + /* Channel disconnecting. */ + /* Client connected. */ + /* Maybe messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* One of kicking m/t or cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + break; + case xenidc_gateway_stimulus_km: + case xenidc_gateway_stimulus_kc: + gateway->state = xenidc_gateway_state_i_cc_cd_lc; + xenidc_gateway_disconnect_client( gateway ); + break; + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd_lc: + /* Channel disconnecting. */ + /* Calling client disconnect. */ + /* Maybe messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + break; + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + break; + case xenidc_gateway_stimulus_lg: + gateway->state = xenidc_gateway_state_i_cc_cd_lc_lg; + xenidc_gateway_fail_out_messages_and_transactions( gateway ); + xenidc_gateway_abort_initiator_resources( gateway ); + break; + case xenidc_gateway_stimulus_ld: + gateway->state = xenidc_gateway_state_i_cc_cd_lc_ld; + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd_lc_lg: + /* Channel disconnecting. */ + /* Client disconnecting. */ + /* No messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_mt: + break; + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + break; + case xenidc_gateway_stimulus_ld: + gateway->state = xenidc_gateway_state_i_cc_cd_lc_lg_ld; + xenidc_gateway_test_target_resources( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd_lc_ld: + /* Channel disconnecting. */ + /* Client disconnected but call still in progress. */ + /* No messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Maybe target resources busy. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + case xenidc_gateway_stimulus_ti: + break; + case xenidc_gateway_stimulus_lg: + gateway->state = xenidc_gateway_state_i_cc_cd_lc_lg_ld; + xenidc_gateway_test_target_resources( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd_lc_lg_ld: + /* Channel disconnecting. */ + /* Client disconnected. */ + /* No messages or transactions queued. */ + /* No channel messages queued. */ + /* Maybe initiator resources busy. */ + /* Test target resources or target resources busy. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_ic: + case xenidc_gateway_stimulus_ii: + case xenidc_gateway_stimulus_tc: + break; + case xenidc_gateway_stimulus_ti: + gateway->state = xenidc_gateway_state_i_cc_cd_lc_lg_ld_ti; + xenidc_gateway_test_initiator_resources( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + case xenidc_gateway_state_i_cc_cd_lc_lg_ld_ti: + /* Channel disconnecting. */ + /* Client disconnected. */ + /* No messages or transactions queued. */ + /* No channel messages queued. */ + /* Test initiator resources or initiator resources busy. */ + /* Target resources idle. */ + /* Not kicking m/t. */ + /* Not kicking cm. */ + switch( stimulus ) + { + case xenidc_gateway_stimulus_ic: + break; + case xenidc_gateway_stimulus_ii: + gateway->state = xenidc_gateway_state_i; + xenidc_gateway_complete_channel_disconnect( gateway ); + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } + break; + default: + xenidc_gateway_invalid_stimulus( gateway, stimulus ); + break; + } +} + +static void xenidc_gateway_invalid_stimulus + ( xenidc_gateway * gateway, xenidc_gateway_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "xenidc: gateway %p in state %d" + "received invalid stimulus %d", + gateway, + gateway->state, + stimulus + ); +} + +static void xenidc_gateway_kick_messages_and_transactions + ( xenidc_gateway * gateway ) +{ + trace(); + + if( !gateway->kick_messages_and_transactions_out ) + { + gateway->kick_messages_and_transactions_out = 1; + + (void)xenidc_work_schedule + ( &gateway->kick_messages_and_transactions_1_work ); + } +} + +static void xenidc_gateway_kick_messages_and_transactions_1( void * data ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)data; + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + gateway->kick_messages_and_transactions_out = 0; + + while + ( + ( !list_empty( &gateway->message_and_transaction_list ) ) + && + ( !list_empty( &gateway->initiator_resource_list ) ) + ) + { + xenidc_gateway_message_and_transaction_header * header = list_entry + ( + gateway->message_and_transaction_list.next, + xenidc_gateway_message_and_transaction_header, + XENIDC_GATEWAY_MESSAGE_AND_TRANSACTION_HEADER_LINK + ); + + xenidc_gateway_initiator_resource * resource = list_entry + ( + gateway->initiator_resource_list.next, + xenidc_gateway_initiator_resource, + XENIDC_GATEWAY_INITIATOR_RESOURCE_LINK + ); + + list_del_init + ( + xenidc_gateway_message_and_transaction_header_to_link + ( header ) + ); + + list_del_init + ( xenidc_gateway_initiator_resource_to_link( resource ) ); + + gateway->initiator_resources_out++; + + spin_unlock_irqrestore( &gateway->lock, flags ); + + xenidc_gateway_initiator_resource_start( resource, header ); + + spin_lock_irqsave( &gateway->lock, flags ); + } + + if( !gateway->kick_messages_and_transactions_out ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_km ); + } + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_kick_messages_and_transactions_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_initiator_resource * resource = + xenidc_gateway_initiator_resource_callback_to( callback ); + + xenidc_gateway * gateway = + xenidc_gateway_initiator_resource_query_gateway( resource ); + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + list_add_tail + ( + xenidc_gateway_initiator_resource_to_link( resource ), + &gateway->initiator_resource_list + ); + + if( --gateway->initiator_resources_out != 0 ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ic ); + } + else + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ii ); + } + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_fail_out_messages_and_transactions + ( xenidc_gateway * gateway ) +{ + trace(); + + while( !list_empty( &gateway->message_and_transaction_list ) ) + { + xenidc_gateway_message_and_transaction_header * header = list_entry + ( + gateway->message_and_transaction_list.next, + xenidc_gateway_message_and_transaction_header, + XENIDC_GATEWAY_MESSAGE_AND_TRANSACTION_HEADER_LINK + ); + + list_del_init + ( xenidc_gateway_message_and_transaction_header_to_link( header ) ); + + xenidc_callback_complete + ( + xenidc_gateway_message_and_transaction_header_to_callback + ( header ), + XENIDC_ERROR_DISCONNECT + ); + } +} + +static void xenidc_gateway_kick_channel_messages( xenidc_gateway * gateway ) +{ + trace(); + + if( !gateway->kick_channel_messages_out ) + { + gateway->kick_channel_messages_out = 1; + + (void)xenidc_work_schedule( &gateway->kick_channel_messages_1_work ); + } +} + +static void xenidc_gateway_kick_channel_messages_1( void * data ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)data; + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + gateway->kick_channel_messages_out = 0; + + while + ( + ( !list_empty( &gateway->channel_message_list ) ) + && + ( !list_empty( &gateway->target_resource_list ) ) + ) + { + xenidc_channel_message * message = list_entry + ( + gateway->channel_message_list.next, + xenidc_channel_message, + XENIDC_CHANNEL_MESSAGE_LINK + ); + + xenidc_gateway_target_resource * resource = list_entry + ( + gateway->target_resource_list.next, + xenidc_gateway_target_resource, + XENIDC_GATEWAY_TARGET_RESOURCE_LINK + ); + + list_del_init( xenidc_channel_message_to_link( message ) ); + + { + xenidc_gateway_ring_element_header header; + + if + ( + xenidc_local_buffer_reference_copy_out + ( &message->message_lbr, &header, sizeof( header ) ) + != + sizeof( header ) + ) + { + goto PROTOCOL_ERROR; + } + + if( header.type == XENIDC_GATEWAY_RING_ELEMENT_TYPE_MESSAGE ) + { + xenidc_local_buffer_reference message_lbr = + message->message_lbr; + + xenidc_local_buffer_reference_advance + ( + &message_lbr, + sizeof( xenidc_gateway_message_ring_element ) + ); + + list_del_init + ( xenidc_gateway_target_resource_to_link( resource ) ); + + gateway->target_resources_out++; + + spin_unlock_irqrestore( &gateway->lock, flags ); + + xenidc_gateway_target_resource_start_message + ( resource, message_lbr ); + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + } + else if + ( + header.type + == + XENIDC_GATEWAY_RING_ELEMENT_TYPE_PARAMETERS + ) + { + xenidc_local_buffer_reference parameters_lbr = + message->message_lbr; + + xenidc_gateway_parameters_ring_element parameters_element; + + if + ( + xenidc_local_buffer_reference_copy_out + ( + ¶meters_lbr, + ¶meters_element, + sizeof( parameters_element ) + ) + != + sizeof( parameters_element ) + ) + { + goto PROTOCOL_ERROR; + } + + xenidc_local_buffer_reference_advance + ( ¶meters_lbr, sizeof( parameters_element ) ); + + list_del_init + ( xenidc_gateway_target_resource_to_link( resource ) ); + + gateway->target_resources_out++; + + spin_unlock_irqrestore( &gateway->lock, flags ); + + xenidc_gateway_target_resource_start_transaction + ( + resource, + parameters_element.id, + parameters_lbr, + parameters_element.status_byte_count + ); + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + } + else + { + PROTOCOL_ERROR: + + xenidc_callback_complete + ( + xenidc_channel_message_to_callback( message ), + XENIDC_ERROR_INVALID_PROTOCOL + ); + } + } + } + + if( !gateway->kick_channel_messages_out ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_kc ); + } + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_kick_channel_messages_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_target_resource * resource = + xenidc_gateway_target_resource_callback_to( callback ); + + xenidc_gateway * gateway = + xenidc_gateway_target_resource_query_gateway( resource ); + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + list_add_tail + ( + xenidc_gateway_target_resource_to_link( resource ), + &gateway->target_resource_list + ); + + if( --gateway->target_resources_out != 0 ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_tc ); + } + else + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ti ); + } + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_complete_channel_messages + ( xenidc_gateway * gateway ) +{ + trace(); + + while( !list_empty( &gateway->channel_message_list ) ) + { + xenidc_channel_message * message = list_entry + ( + gateway->channel_message_list.next, + xenidc_channel_message, + XENIDC_CHANNEL_MESSAGE_LINK + ); + + list_del_init( xenidc_channel_message_to_link( message ) ); + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + } +} + +static void xenidc_gateway_connect_client( xenidc_gateway * gateway ) +{ + trace(); + + (void)xenidc_work_schedule( &gateway->connect_client_1_work ); +} + +static void xenidc_gateway_connect_client_1( void * data ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)data; + + gateway->connect( gateway ); + + { + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_lc ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } + } +} + +static void xenidc_gateway_disconnect_client( xenidc_gateway * gateway ) +{ + trace(); + + (void)xenidc_work_schedule( &gateway->disconnect_client_1_work ); +} + +static void xenidc_gateway_disconnect_client_1( void * data ) +{ + trace(); + + { + xenidc_gateway * gateway = (xenidc_gateway *)data; + + gateway->disconnect( gateway, &gateway->disconnect_client_2_callback ); + + { + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_lg ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } + } +} + +static void xenidc_gateway_disconnect_client_2( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway * gateway = container_of + ( callback, xenidc_gateway, disconnect_client_2_callback ); + + unsigned long flags; + + spin_lock_irqsave( &gateway->lock, flags ); + + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ld ); + + spin_unlock_irqrestore( &gateway->lock, flags ); + } +} + +static void xenidc_gateway_abort_initiator_resources + ( xenidc_gateway * gateway ) +{ + trace(); + + { + u32 i; + + for( i = 0; i < gateway->initiator_quota; i++ ) + { + xenidc_gateway_initiator_resource_abort + ( &gateway->initiator_resources[ i ] ); + } + } +} + +static void xenidc_gateway_test_initiator_resources + ( xenidc_gateway * gateway ) +{ + trace(); + + if( gateway->initiator_resources_out == 0 ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ii ); + } +} + +static void xenidc_gateway_test_target_resources + ( xenidc_gateway * gateway ) +{ + trace(); + + if( gateway->target_resources_out == 0 ) + { + xenidc_gateway_handle_stimulus + ( gateway, xenidc_gateway_stimulus_ti ); + } +} + +static void xenidc_gateway_complete_channel_disconnect + ( xenidc_gateway * gateway ) +{ + trace(); + + xenidc_callback_success( gateway->channel_disconnect_callback ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_initiator_resource.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_initiator_resource.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,411 @@ +#include +#include "xenidc_gateway_initiator_resource.h" +#include "xenidc_trace.h" + +extern void xenidc_gateway_submit_channel_message + ( xenidc_gateway * gateway, xenidc_channel_message * message ); + +static void xenidc_gateway_initiator_resource_handle_stimulus +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_initiator_resource_stimulus stimulus +); + +static void xenidc_gateway_initiator_resource_channel_message_callback + ( xenidc_callback * callback ); + +void xenidc_gateway_initiator_resource_init +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway * gateway, + xenidc_callback_function callback, + int id +) +{ + trace(); + + xenidc_callback_init( &resource->callback, callback ); + + resource->gateway = gateway; + + resource->id = id; + + spin_lock_init( &resource->lock ); + + resource->state = xenidc_gateway_initiator_resource_state_i; + + xenidc_callback_init + ( + xenidc_channel_message_to_callback( &resource->channel_message ), + xenidc_gateway_initiator_resource_channel_message_callback + ); +} + +void xenidc_gateway_initiator_resource_start +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_message_and_transaction_header * header +) +{ + trace(); + + resource->header = header; + + resource->error = XENIDC_ERROR_SUCCESS; + + { + xenidc_gateway_initiator_resource_stimulus stimulus; + + if( header->transaction_not_message ) + { + xenidc_gateway_transaction * transaction = + xenidc_gateway_transaction_header_to( header ); + + resource->element_lbr = xenidc_vaddress_create_lbr + ( + &resource->parameters_element, + sizeof( resource->parameters_element ) + ); + + resource->channel_message.message_lbr = + xenidc_concatenate_create_lbr + ( + &resource->base, + &resource->element_lbr, + &transaction->parameters_lbr + ); + + memset + ( + &resource->parameters_element, + 0, + sizeof( resource->parameters_element ) + ); + + resource->parameters_element.header.type = + XENIDC_GATEWAY_RING_ELEMENT_TYPE_PARAMETERS; + + resource->parameters_element.id = resource->id; + + resource->parameters_element.status_byte_count = + xenidc_local_buffer_reference_query_byte_count + ( &transaction->status_lbr ); + + stimulus = xenidc_gateway_initiator_resource_stimulus_st; + } + else + { + xenidc_gateway_message * message = + xenidc_gateway_message_header_to( header ); + + resource->element_lbr = xenidc_vaddress_create_lbr + ( + &resource->message_element, + sizeof( resource->message_element ) + ); + + resource->channel_message.message_lbr = + xenidc_concatenate_create_lbr + ( + &resource->base, + &resource->element_lbr, + &message->message_lbr + ); + + memset + ( + &resource->message_element, + 0, + sizeof( resource->message_element ) + ); + + resource->parameters_element.header.type = + XENIDC_GATEWAY_RING_ELEMENT_TYPE_MESSAGE; + + stimulus = xenidc_gateway_initiator_resource_stimulus_sm; + } + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + xenidc_gateway_initiator_resource_handle_stimulus + ( resource, stimulus ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } + + xenidc_gateway_submit_channel_message + ( resource->gateway, &resource->channel_message ); + } +} + +void xenidc_gateway_initiator_resource_abort + ( xenidc_gateway_initiator_resource * resource ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + xenidc_gateway_initiator_resource_handle_stimulus + ( resource, xenidc_gateway_initiator_resource_stimulus_ab ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +static void xenidc_gateway_initiator_resource_channel_message_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_initiator_resource * resource = container_of + ( + callback, + xenidc_gateway_initiator_resource, + channel_message.callback + ); + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + xenidc_gateway_initiator_resource_handle_stimulus + ( resource, xenidc_gateway_initiator_resource_stimulus_sc ); + + spin_unlock_irqrestore( &resource->lock, flags ); + } +} + +int xenidc_gateway_initiator_resource_handle_status +( + xenidc_gateway_initiator_resource * resource, + xenidc_error error, + xenidc_local_buffer_reference status_lbr +) +{ + trace(); + + { + int return_value = -1; + + unsigned long flags; + + spin_lock_irqsave( &resource->lock, flags ); + + if + ( + ( + resource->state + == + xenidc_gateway_initiator_resource_state_i_st + ) + || + ( + resource->state + == + xenidc_gateway_initiator_resource_state_i_st_sc + ) + ) + { + xenidc_gateway_transaction * transaction = + xenidc_gateway_transaction_header_to( resource->header ); + + if + ( + xenidc_local_buffer_reference_query_byte_count( &status_lbr ) + == + xenidc_local_buffer_reference_query_byte_count + ( &transaction->status_lbr ) + ) + { + xenidc_local_buffer_reference_copy + ( &transaction->status_lbr, &status_lbr ); + + resource->error = error; + + xenidc_gateway_initiator_resource_handle_stimulus + ( resource, xenidc_gateway_initiator_resource_stimulus_ts ); + + return_value = 0; + } + } + + spin_unlock_irqrestore( &resource->lock, flags ); + + return return_value; + } +} + +static void xenidc_gateway_initiator_resource_invalid_stimulus +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_initiator_resource_stimulus stimulus +); + +static void xenidc_gateway_initiator_resource_set_aborted + ( xenidc_gateway_initiator_resource * resource ); + +static void xenidc_gateway_initiator_resource_complete + ( xenidc_gateway_initiator_resource * resource ); + +static void xenidc_gateway_initiator_resource_handle_stimulus +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_initiator_resource_stimulus stimulus +) +{ + trace3 + ( + "gateway initiator resource %p in state %d received stimulus %d", + resource, + resource->state, + stimulus + ); + + switch( resource->state ) + { + case xenidc_gateway_initiator_resource_state_i: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_sm: + resource->state = xenidc_gateway_initiator_resource_state_i_sm; + break; + case xenidc_gateway_initiator_resource_stimulus_st: + resource->state = xenidc_gateway_initiator_resource_state_i_st; + break; + case xenidc_gateway_initiator_resource_stimulus_ab: + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case xenidc_gateway_initiator_resource_state_i_sm: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_ab: + break; + case xenidc_gateway_initiator_resource_stimulus_sc: + resource->state = xenidc_gateway_initiator_resource_state_i; + xenidc_gateway_initiator_resource_complete( resource ); + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case xenidc_gateway_initiator_resource_state_i_st: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_ab: + resource->state = xenidc_gateway_initiator_resource_state_i_st_ab; + break; + case xenidc_gateway_initiator_resource_stimulus_sc: + resource->state = xenidc_gateway_initiator_resource_state_i_st_sc; + break; + case xenidc_gateway_initiator_resource_stimulus_ts: + resource->state = xenidc_gateway_initiator_resource_state_i_st_ts; + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case xenidc_gateway_initiator_resource_state_i_st_ab: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_sc: + resource->state = xenidc_gateway_initiator_resource_state_i; + xenidc_gateway_initiator_resource_set_aborted( resource ); + xenidc_gateway_initiator_resource_complete( resource ); + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case xenidc_gateway_initiator_resource_state_i_st_sc: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_ab: + resource->state = xenidc_gateway_initiator_resource_state_i; + xenidc_gateway_initiator_resource_set_aborted( resource ); + xenidc_gateway_initiator_resource_complete( resource ); + break; + case xenidc_gateway_initiator_resource_stimulus_ts: + resource->state = xenidc_gateway_initiator_resource_state_i; + xenidc_gateway_initiator_resource_complete( resource ); + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + case xenidc_gateway_initiator_resource_state_i_st_ts: + switch( stimulus ) + { + case xenidc_gateway_initiator_resource_stimulus_ab: + break; + case xenidc_gateway_initiator_resource_stimulus_sc: + resource->state = xenidc_gateway_initiator_resource_state_i; + xenidc_gateway_initiator_resource_complete( resource ); + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } + break; + default: + xenidc_gateway_initiator_resource_invalid_stimulus + ( resource, stimulus ); + break; + } +} + +static void xenidc_gateway_initiator_resource_invalid_stimulus +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_initiator_resource_stimulus stimulus +) +{ + trace(); + + printk + ( + KERN_ERR "xenidc: gateway initiator resource %p in state %d" + "received invalid stimulus %d", + resource, + resource->state, + stimulus + ); +} + +static void xenidc_gateway_initiator_resource_set_aborted + ( xenidc_gateway_initiator_resource * resource ) +{ + trace(); + + resource->error = XENIDC_ERROR_ABORTED; +} + +static void xenidc_gateway_initiator_resource_complete + ( xenidc_gateway_initiator_resource * resource ) +{ + trace(); + + xenidc_callback_complete( &resource->header->callback, resource->error ); + + xenidc_callback_success( &resource->callback ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_initiator_resource.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_initiator_resource.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,95 @@ +#ifndef _XENIDC_GATEWAY_INITIATOR_RESOURCE_H +#define _XENIDC_GATEWAY_INITIATOR_RESOURCE_H + +#include +#include +#include +#include "xenidc_gateway_ring.h" + +typedef enum +{ + xenidc_gateway_initiator_resource_state_i, + xenidc_gateway_initiator_resource_state_i_sm, + xenidc_gateway_initiator_resource_state_i_st, + xenidc_gateway_initiator_resource_state_i_st_ab, + xenidc_gateway_initiator_resource_state_i_st_sc, + xenidc_gateway_initiator_resource_state_i_st_ts +} +xenidc_gateway_initiator_resource_state; + +typedef enum +{ + xenidc_gateway_initiator_resource_stimulus_sm, /* Start message */ + xenidc_gateway_initiator_resource_stimulus_st, /* Start transaction */ + xenidc_gateway_initiator_resource_stimulus_ab, /* Abort */ + xenidc_gateway_initiator_resource_stimulus_sc, /* Send complete */ + xenidc_gateway_initiator_resource_stimulus_ts, /* Transaction status */ +} +xenidc_gateway_initiator_resource_stimulus; + +struct xenidc_gateway_initiator_resource_struct +{ + xenidc_callback callback; + xenidc_gateway * gateway; + int id; + spinlock_t lock; + xenidc_gateway_initiator_resource_state state; + xenidc_gateway_message_and_transaction_header * header; + xenidc_error error; + xenidc_channel_message channel_message; + xenidc_local_buffer_reference element_lbr; + xenidc_concatenate_base base; + union + { + xenidc_gateway_parameters_ring_element parameters_element; + xenidc_gateway_message_ring_element message_element; + }; +}; + +#define XENIDC_GATEWAY_INITIATOR_RESOURCE_LINK callback.XENIDC_CALLBACK_LINK + +static inline struct list_head * xenidc_gateway_initiator_resource_to_link + ( xenidc_gateway_initiator_resource * resource ) +{ + return &resource->XENIDC_GATEWAY_INITIATOR_RESOURCE_LINK; +} + +static inline xenidc_gateway_initiator_resource * + xenidc_gateway_initiator_resource_callback_to( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_gateway_initiator_resource, callback ); +} + +static inline xenidc_gateway * + xenidc_gateway_initiator_resource_query_gateway + ( xenidc_gateway_initiator_resource* resource ) +{ + return resource->gateway; +} + +void xenidc_gateway_initiator_resource_init +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway * gateway, + xenidc_callback_function callback, + int id +); + +void xenidc_gateway_initiator_resource_start +( + xenidc_gateway_initiator_resource * resource, + xenidc_gateway_message_and_transaction_header * header +); + +void xenidc_gateway_initiator_resource_abort + ( xenidc_gateway_initiator_resource * resource ); + +int xenidc_gateway_initiator_resource_handle_status +( + xenidc_gateway_initiator_resource * resource, + xenidc_error error, + xenidc_local_buffer_reference status +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_ring.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_ring.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,69 @@ +/*****************************************************************************/ +/* Xen inter-domain communication gateway ring structure definitions. */ +/* */ +/* 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 _XENIDC_GATEWAY_RING_H +#define _XENIDC_GATEWAY_RING_H + +#include + +typedef struct xenidc_gateway_ring_element_header_struct + xenidc_gateway_ring_element_header; + +struct xenidc_gateway_ring_element_header_struct +{ + u8 type; + u8 reserved[ 7 ]; +}; + +#define XENIDC_GATEWAY_RING_ELEMENT_TYPE_MESSAGE 0 +#define XENIDC_GATEWAY_RING_ELEMENT_TYPE_PARAMETERS 1 +#define XENIDC_GATEWAY_RING_ELEMENT_TYPE_STATUS 2 + +typedef struct xenidc_gateway_message_ring_element_struct + xenidc_gateway_message_ring_element; + +struct xenidc_gateway_message_ring_element_struct +{ + xenidc_gateway_ring_element_header header; +}; + +typedef struct xenidc_gateway_parameters_ring_element_struct + xenidc_gateway_parameters_ring_element; + +struct xenidc_gateway_parameters_ring_element_struct +{ + xenidc_gateway_ring_element_header header; + u32 id; + u16 status_byte_count; + u16 reserved; +}; + +typedef struct xenidc_gateway_status_ring_element_struct + xenidc_gateway_status_ring_element; + +struct xenidc_gateway_status_ring_element_struct +{ + xenidc_gateway_ring_element_header header; + u32 id; + xenidc_error error; +}; + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_target_resource.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_target_resource.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,193 @@ +#include +#include "xenidc_gateway_target_resource.h" +#include "xenidc_trace.h" + +extern void xenidc_gateway_submit_channel_message + ( xenidc_gateway * gateway, xenidc_channel_message * message ); + +extern void xenidc_gateway_submit_message_to_client + ( xenidc_gateway * gateway, xenidc_gateway_message * message ); + +extern void xenidc_gateway_submit_transaction_to_client + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ); + +static void xenidc_gateway_target_resource_channel_message_callback + ( xenidc_callback * callback ); + +void xenidc_gateway_target_resource_init +( + xenidc_gateway_target_resource * resource, + xenidc_gateway * gateway, + xenidc_callback_function callback, + xenidc_local_buffer_reference buffer +) +{ + trace(); + + xenidc_callback_init( &resource->callback, callback ); + + resource->gateway = gateway; + + resource->buffer = buffer; + + xenidc_callback_init + ( + xenidc_channel_message_to_callback( &resource->channel_message ), + xenidc_gateway_target_resource_channel_message_callback + ); + + resource->element_lbr = xenidc_vaddress_create_lbr + ( &resource->status_element, sizeof( resource->status_element ) ); + + memset( &resource->status_element, 0, sizeof( resource->status_element ) ); + + resource->status_element.header.type = + XENIDC_GATEWAY_RING_ELEMENT_TYPE_STATUS; +} + +static void xenidc_gateway_target_resource_message_callback + ( xenidc_callback * callback ); + +void xenidc_gateway_target_resource_start_message +( + xenidc_gateway_target_resource * resource, + xenidc_local_buffer_reference message_lbr +) +{ + trace(); + + xenidc_gateway_message_init + ( + &resource->message, + xenidc_gateway_target_resource_message_callback + ); + + { + xenidc_local_buffer_reference lbr = resource->buffer; + + xenidc_local_buffer_reference_truncate + ( + &lbr, + xenidc_local_buffer_reference_copy( &lbr, &message_lbr ) + ); + + resource->message.message_lbr = lbr; + } + + xenidc_gateway_submit_message_to_client + ( resource->gateway, &resource->message ); +} + +static void xenidc_gateway_target_resource_message_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_target_resource * resource = container_of + ( + xenidc_gateway_message_callback_to( callback ), + xenidc_gateway_target_resource, + message + ); + + xenidc_callback_success( &resource->callback ); + } +} + +static void xenidc_gateway_target_resource_transaction_callback + ( xenidc_callback * callback ); + +void xenidc_gateway_target_resource_start_transaction +( + xenidc_gateway_target_resource * resource, + u32 id, + xenidc_local_buffer_reference parameters_lbr, + xenidc_buffer_byte_count status_byte_count +) +{ + trace(); + + xenidc_gateway_transaction_init + ( + &resource->transaction, + xenidc_gateway_target_resource_transaction_callback + ); + + { + xenidc_local_buffer_reference lbr = resource->buffer; + + xenidc_local_buffer_reference_truncate + ( + &lbr, + xenidc_local_buffer_reference_copy( &lbr, ¶meters_lbr ) + ); + + resource->transaction.parameters_lbr = lbr; + } + + { + xenidc_local_buffer_reference lbr = resource->buffer; + + xenidc_local_buffer_reference_subrange + ( + &lbr, + xenidc_local_buffer_reference_query_byte_count( ¶meters_lbr ), + status_byte_count + ); + + xenidc_local_buffer_reference_zero( &lbr ); + + resource->transaction.status_lbr = lbr; + } + + resource->status_element.id = id; + + xenidc_gateway_submit_transaction_to_client + ( resource->gateway, &resource->transaction ); +} + +static void xenidc_gateway_target_resource_transaction_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_target_resource * resource = container_of + ( + xenidc_gateway_transaction_callback_to( callback ), + xenidc_gateway_target_resource, + transaction + ); + + resource->status_element.error = + xenidc_callback_query_error( callback ); + + resource->channel_message.message_lbr = xenidc_concatenate_create_lbr + ( + &resource->base, + &resource->element_lbr, + &resource->transaction.status_lbr + ); + + xenidc_gateway_submit_channel_message + ( resource->gateway, &resource->channel_message ); + } +} + +static void xenidc_gateway_target_resource_channel_message_callback + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gateway_target_resource * resource = container_of + ( + callback, + xenidc_gateway_target_resource, + channel_message.callback + ); + + xenidc_callback_success( &resource->callback ); + } +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_target_resource.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gateway_target_resource.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,68 @@ +#ifndef _XENIDC_GATEWAY_TARGET_RESOURCE_H +#define _XENIDC_GATEWAY_TARGET_RESOURCE_H + +#include +#include +#include +#include "xenidc_gateway_ring.h" + +struct xenidc_gateway_target_resource_struct +{ + xenidc_callback callback; + xenidc_gateway * gateway; + xenidc_local_buffer_reference buffer; + union + { + xenidc_gateway_message message; + xenidc_gateway_transaction transaction; + }; + xenidc_channel_message channel_message; + xenidc_local_buffer_reference element_lbr; + xenidc_concatenate_base base; + xenidc_gateway_status_ring_element status_element; +}; + +#define XENIDC_GATEWAY_TARGET_RESOURCE_LINK callback.XENIDC_CALLBACK_LINK + +static inline struct list_head * xenidc_gateway_target_resource_to_link + ( xenidc_gateway_target_resource * resource ) +{ + return &resource->XENIDC_GATEWAY_TARGET_RESOURCE_LINK; +} + +static inline xenidc_gateway_target_resource * + xenidc_gateway_target_resource_callback_to( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_gateway_target_resource, callback ); +} + +static inline xenidc_gateway * xenidc_gateway_target_resource_query_gateway + ( xenidc_gateway_target_resource* resource ) +{ + return resource->gateway; +} + +extern void xenidc_gateway_target_resource_init +( + xenidc_gateway_target_resource * resource, + xenidc_gateway * gateway, + xenidc_callback_function callback, + xenidc_local_buffer_reference buffer +); + +extern void xenidc_gateway_target_resource_start_message +( + xenidc_gateway_target_resource * resource, + xenidc_local_buffer_reference message_lbr +); + +extern void xenidc_gateway_target_resource_start_transaction +( + xenidc_gateway_target_resource * resource, + u32 id, + xenidc_local_buffer_reference parameters_lbr, + xenidc_buffer_byte_count status_byte_count +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gnttab_channel.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gnttab_channel.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1693 @@ +/*****************************************************************************/ +/* This is a class which implements a grant-tables based inter-domain */ +/* message channel. The implementation of the bring-up and tear-down */ +/* handshaking is left to a derived class. */ +/* This class is used by xenidc_xbgt_channel (which implements bring-up and */ +/* teardown using xenbus) which is in turn used to implement the */ +/* xenidc_endpoint class. */ +/* */ +/* 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 +#include +#include +#include +#include +#include "xenidc_trace.h" +#include "xenidc_channel_ring.h" + +static inline void xenidc_gnttab_channel_target_resource_init +( + xenidc_gnttab_channel_target_resource * resource, + xenidc_gnttab_channel * channel, + xenidc_callback_function * callback +) +{ + trace(); + + xenidc_channel_message_init( &resource->message, callback ); + + resource->channel = channel; +} + +static inline struct list_head * xenidc_gnttab_channel_target_resource_to_link + ( xenidc_gnttab_channel_target_resource * resource ) +{ + trace(); + + return xenidc_channel_message_to_link( &resource->message ); +} + +static inline xenidc_channel_message * + xenidc_gnttab_channel_target_resource_to_message + ( xenidc_gnttab_channel_target_resource * resource ) +{ + trace(); + + return &resource->message; +} + +static inline xenidc_gnttab_channel_target_resource * + xenidc_gnttab_channel_target_resource_callback_to + ( xenidc_callback * callback ) +{ + trace(); + + return container_of + ( + xenidc_channel_message_callback_to( callback ), + xenidc_gnttab_channel_target_resource, + message + ); +} + +static inline xenidc_gnttab_channel * + xenidc_gnttab_channel_target_resource_query_channel + ( xenidc_gnttab_channel_target_resource * resource ) +{ + trace(); + + return resource->channel; +} + +typedef enum +{ + xenidc_gnttab_channel_stimulus_c1r, /* phase one connect request */ + xenidc_gnttab_channel_stimulus_c2r, /* phase two connect request */ + xenidc_gnttab_channel_stimulus_mqr, /* message queued */ + xenidc_gnttab_channel_stimulus_d2r, /* phase two disconnect request */ + xenidc_gnttab_channel_stimulus_d1r, /* phase one disconnect request */ + xenidc_gnttab_channel_stimulus_sir, /* send interrupt */ + xenidc_gnttab_channel_stimulus_rir, /* recv interrupt */ + xenidc_gnttab_channel_stimulus_c1c, /* phase one connect completed */ + xenidc_gnttab_channel_stimulus_c2s, /* phase two connect successful */ + xenidc_gnttab_channel_stimulus_c2f, /* phase two connect failed */ + xenidc_gnttab_channel_stimulus_ccc, /* connect client completed */ + xenidc_gnttab_channel_stimulus_dcc, /* disconnect client completed */ + xenidc_gnttab_channel_stimulus_d2c, /* phase two disconnect completed */ + xenidc_gnttab_channel_stimulus_d1c, /* phase one disconnect completed */ + xenidc_gnttab_channel_stimulus_ksc, /* kick send ring completed */ + xenidc_gnttab_channel_stimulus_krc, /* kick recv ring completed */ + xenidc_gnttab_channel_stimulus_trc, /* target resource completed */ + xenidc_gnttab_channel_stimulus_tri /* target resources idle */ +} +xenidc_gnttab_channel_stimulus; + +static void xenidc_gnttab_channel_handle_stimulus + ( xenidc_gnttab_channel * channel, xenidc_gnttab_channel_stimulus stimulus ); + +static void xenidc_gnttab_channel_submit_message + ( xenidc_channel * base_channel, xenidc_channel_message * message ); + +static int xenidc_gnttab_channel_init_or_exit + ( xenidc_gnttab_channel * channel, int exit ); + +int xenidc_gnttab_channel_init +( + xenidc_gnttab_channel * channel, + void ( * protocol_error )( xenidc_gnttab_channel * channel ) +) +{ + trace(); + + xenidc_channel_init + ( &channel->channel, xenidc_gnttab_channel_submit_message ); + + channel->protocol_error = protocol_error; + + return xenidc_gnttab_channel_init_or_exit( channel, 0 ); +} + +static void xenidc_gnttab_channel_do_phase_one_connect_1( void * data ); + +static void xenidc_gnttab_channel_do_phase_two_connect_1( void * data ); + +static void xenidc_gnttab_channel_connect_client_1( void * data ); + +static void xenidc_gnttab_channel_disconnect_client_1( void * data ); + +static void xenidc_gnttab_channel_disconnect_client_2 + ( xenidc_callback * callback ); + +static void xenidc_gnttab_channel_do_phase_two_disconnect_1( void * data ); + +static void xenidc_gnttab_channel_do_phase_one_disconnect_1( void * data ); + +static void xenidc_gnttab_channel_kick_send_ring_1( void * data ); + +static void xenidc_gnttab_channel_kick_recv_ring_1( void * data ); + +static void xenidc_gnttab_channel_kick_recv_ring_2 + ( xenidc_callback * callback ); + +static int xenidc_gnttab_channel_init_or_exit + ( xenidc_gnttab_channel * channel, int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + channel->send_irq_context = channel; + channel->recv_irq_context = channel; + + channel->send_ring = (void *)__get_free_page( GFP_KERNEL ); + + if( channel->send_ring == NULL ) + { + trace0( "failed to allocate send ring" ); + + return_value = -ENOMEM; + + goto EXIT_NO_SEND_RING; + } + + if + ( + ( channel->recv_ring_area = alloc_vm_area( PAGE_SIZE ) ) + == + NULL + ) + { + trace0( "failed to allocate receive ring area" ); + + return_value = -ENOMEM; + + goto EXIT_NO_RING_AREA; + } + + return_value = + gnttab_alloc_grant_references( 1, &channel->grant_ref_pool ); + + if( return_value != 0 ) + { + trace0( "failed to allocate grant reference pool" ); + + goto EXIT_NO_GRANT_REF; + } + + spin_lock_init( &channel->lock ); + + channel->state = xenidc_gnttab_channel_state_i; + + INIT_LIST_HEAD( &channel->message_list ); + + xenidc_work_init + ( + &channel->do_phase_one_connect_1_work, + xenidc_gnttab_channel_do_phase_one_connect_1, + channel + ); + + xenidc_work_init + ( + &channel->do_phase_two_connect_1_work, + xenidc_gnttab_channel_do_phase_two_connect_1, + channel + ); + + xenidc_work_init + ( + &channel->connect_client_1_work, + xenidc_gnttab_channel_connect_client_1, + channel + ); + + xenidc_work_init + ( + &channel->disconnect_client_1_work, + xenidc_gnttab_channel_disconnect_client_1, + channel + ); + + xenidc_callback_init + ( + &channel->disconnect_client_2_callback, + xenidc_gnttab_channel_disconnect_client_2 + ); + + xenidc_work_init + ( + &channel->do_phase_two_disconnect_1_work, + xenidc_gnttab_channel_do_phase_two_disconnect_1, + channel + ); + + xenidc_work_init + ( + &channel->do_phase_one_disconnect_1_work, + xenidc_gnttab_channel_do_phase_one_disconnect_1, + channel + ); + + xenidc_work_init + ( + &channel->kick_send_ring_1_work, + xenidc_gnttab_channel_kick_send_ring_1, + channel + ); + + xenidc_work_init + ( + &channel->kick_recv_ring_1_work, + xenidc_gnttab_channel_kick_recv_ring_1, + channel + ); + + { + int i; + + for( i = 0; i < XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT; i++ ) + { + xenidc_gnttab_channel_target_resource * resource = + &channel->target_resources[ i ]; + + xenidc_gnttab_channel_target_resource_init + ( + resource, + channel, + xenidc_gnttab_channel_kick_recv_ring_2 + ); + + channel->target_resource_free[ i ] = 1; + } + } + + channel->first_target_resource = 0; + channel->next_target_resource = 0; + + channel->send_ring_kick_out = 0; + channel->recv_ring_kick_out = 0; + + return 0; + + EXIT: + + gnttab_free_grant_references( channel->grant_ref_pool ); + + EXIT_NO_GRANT_REF: + + free_vm_area( channel->recv_ring_area ); + + EXIT_NO_RING_AREA: + + free_page( (unsigned long)channel->send_ring ); + + EXIT_NO_SEND_RING: + + return return_value; + } +} + +void xenidc_gnttab_channel_phase_one_connect +( + xenidc_gnttab_channel * channel, + xenidc_gnttab_channel_phase_one_connect_request * request +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->current_callback = &request->callback; + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_c1r ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_gnttab_channel_reset_ring( xenidc_gnttab_channel * channel ) +{ + trace(); + + channel->recv_ring_offset = 0; + + memset( channel->send_ring, 0, PAGE_SIZE ); +} + +void xenidc_gnttab_channel_phase_two_connect +( + xenidc_gnttab_channel * channel, + xenidc_gnttab_channel_phase_two_connect_request * request +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->current_callback = &request->callback; + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_c2r ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_gnttab_channel_submit_message + ( xenidc_channel * base_channel, xenidc_channel_message * message ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = + xenidc_gnttab_channel_channel_to( base_channel ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + list_add_tail + ( + xenidc_channel_message_to_link( message ), + &channel->message_list + ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_mqr ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_gnttab_channel_phase_two_disconnect + ( xenidc_gnttab_channel * channel, xenidc_callback * callback ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->current_callback = callback; + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_d2r ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_gnttab_channel_phase_one_disconnect + ( xenidc_gnttab_channel * channel, xenidc_callback * callback ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->current_callback = callback; + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_d1r ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_gnttab_channel_exit( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_gnttab_channel_init_or_exit( channel, 1 ); +} + +static irqreturn_t xenidc_gnttab_channel_send_interrupt + ( int irq, void * context, struct pt_regs * ptregs ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = *(xenidc_gnttab_channel **)context; + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_sir ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return IRQ_HANDLED; +} + +static irqreturn_t xenidc_gnttab_channel_recv_interrupt + ( int irq, void * context, struct pt_regs * ptregs ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = *(xenidc_gnttab_channel **)context; + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_rir ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return IRQ_HANDLED; +} + +static void xenidc_gnttab_channel_invalid_stimulus + ( xenidc_gnttab_channel * channel, xenidc_gnttab_channel_stimulus stimulus ); + +static void xenidc_gnttab_channel_do_phase_one_connect + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_do_phase_two_connect + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_connect_client + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_disconnect_client + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_do_phase_two_disconnect + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_do_phase_one_disconnect + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_kick_send_ring + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_kick_recv_ring + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_complete_current_callback + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_fail_current_callback + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_fail_out_messages + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_test_target_resources + ( xenidc_gnttab_channel * channel ); + +static void xenidc_gnttab_channel_handle_stimulus + ( xenidc_gnttab_channel * channel, xenidc_gnttab_channel_stimulus stimulus ) +{ + trace3 + ( + "channel %p in state %d received stimulus %d", + channel, + channel->state, + stimulus + ); + + switch( channel->state ) + { + case xenidc_gnttab_channel_state_i: + /* Interface disconnected. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_c1r: + channel->state = xenidc_gnttab_channel_state_i_c1r; + xenidc_gnttab_channel_do_phase_one_connect( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r: + /* Interface phase one connecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + /* do phase one connect in progress */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_sir: + break; + case xenidc_gnttab_channel_stimulus_c1c: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c; + xenidc_gnttab_channel_complete_current_callback( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c: + /* Interface phase one connected. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + /* Phase one connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_c2r: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c_c2r; + xenidc_gnttab_channel_do_phase_two_connect( channel ); + break; + case xenidc_gnttab_channel_stimulus_d1r: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c_d1r; + xenidc_gnttab_channel_do_phase_one_disconnect( channel ); + break; + case xenidc_gnttab_channel_stimulus_sir: + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r: + /* Interface phase two connecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + /* do phase two connect in progress */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_c2s: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s; + xenidc_gnttab_channel_connect_client( channel ); + break; + case xenidc_gnttab_channel_stimulus_c2f: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c; + xenidc_gnttab_channel_fail_current_callback( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_d1r: + /* Interface phase one disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + /* do phase one disconnect in progress */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_sir: + break; + case xenidc_gnttab_channel_stimulus_d1c: + channel->state = xenidc_gnttab_channel_state_i; + xenidc_gnttab_channel_complete_current_callback( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s: + /* Interface phase two connecting. */ + /* Client connecting. */ + /* Maybe messages queued. */ + /* Kick send idle. */ + /* Kick recv idle. */ + /* Target resources idle. */ + /* Phase two connected. */ + /* connect client in progress */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_mqr: + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_ccc: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc; + xenidc_gnttab_channel_complete_current_callback( channel ); + xenidc_gnttab_channel_kick_send_ring( channel ); + xenidc_gnttab_channel_kick_recv_ring( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc: + /* Interface phase two connected. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Maybe kick send in progress. */ + /* Maybe kick recv in progress. */ + /* Maybe target resources busy. */ + /* Phase two connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_mqr: + xenidc_gnttab_channel_kick_send_ring( channel ); + break; + case xenidc_gnttab_channel_stimulus_d2r: + channel->state = + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r; + xenidc_gnttab_channel_kick_send_ring( channel ); + xenidc_gnttab_channel_kick_recv_ring( channel ); + break; + case xenidc_gnttab_channel_stimulus_sir: + xenidc_gnttab_channel_kick_send_ring( channel ); + break; + case xenidc_gnttab_channel_stimulus_rir: + xenidc_gnttab_channel_kick_recv_ring( channel ); + break; + case xenidc_gnttab_channel_stimulus_ksc: + case xenidc_gnttab_channel_stimulus_krc: + break; + case xenidc_gnttab_channel_stimulus_trc: + case xenidc_gnttab_channel_stimulus_tri: + xenidc_gnttab_channel_kick_recv_ring( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r: + /* Interface phase two disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* Kick send in progress. */ + /* Kick recv in progress. */ + /* Maybe target resources busy. */ + /* Phase two connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_mqr: + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_ksc: + case xenidc_gnttab_channel_stimulus_krc: + channel->state = + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc; + break; + case xenidc_gnttab_channel_stimulus_trc: + case xenidc_gnttab_channel_stimulus_tri: + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc: + /* Interface phase two disconnecting. */ + /* Client connected. */ + /* Maybe messages queued. */ + /* One of kick send /recv in progress. */ + /* Maybe target resources busy. */ + /* Phase two connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_mqr: + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_ksc: + case xenidc_gnttab_channel_stimulus_krc: + channel->state = + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc; + xenidc_gnttab_channel_disconnect_client( channel ); + xenidc_gnttab_channel_fail_out_messages( channel ); + break; + case xenidc_gnttab_channel_stimulus_trc: + case xenidc_gnttab_channel_stimulus_tri: + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc: + /* Interface phase two disconnecting. */ + /* Client disconnecting. */ + /* No messages queued. */ + /* Kick send /recv idle. */ + /* Maybe target resources busy. */ + /* Phase two connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_mqr: + xenidc_gnttab_channel_fail_out_messages( channel ); + break; + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_dcc: + channel->state = + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc; + xenidc_gnttab_channel_test_target_resources( channel ); + break; + case xenidc_gnttab_channel_stimulus_trc: + case xenidc_gnttab_channel_stimulus_tri: + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc: + /* Interface phase two disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send /recv idle. */ + /* Testing target resources/ target resources busy */ + /* Phase two connected. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + case xenidc_gnttab_channel_stimulus_trc: + break; + case xenidc_gnttab_channel_stimulus_tri: + channel->state = + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri; + xenidc_gnttab_channel_do_phase_two_disconnect( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri: + /* Interface phase two disconnecting. */ + /* Client disconnected. */ + /* No messages queued. */ + /* Kick send /recv idle. */ + /* Target resources idle. */ + /* Phase two disconnecting. */ + switch( stimulus ) + { + case xenidc_gnttab_channel_stimulus_sir: + case xenidc_gnttab_channel_stimulus_rir: + break; + case xenidc_gnttab_channel_stimulus_d2c: + channel->state = xenidc_gnttab_channel_state_i_c1r_c1c; + xenidc_gnttab_channel_complete_current_callback( channel ); + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + default: + xenidc_gnttab_channel_invalid_stimulus( channel, stimulus ); + break; + } +} + +static void xenidc_gnttab_channel_invalid_stimulus + ( xenidc_gnttab_channel * channel, xenidc_gnttab_channel_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "xenidc: channel %p in state %d" + "received invalid stimulus %d", + channel, + channel->state, + stimulus + ); +} + +static void xenidc_gnttab_channel_do_phase_one_connect + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->do_phase_one_connect_1_work ); +} + +static void xenidc_gnttab_channel_do_phase_one_connect_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + xenidc_gnttab_channel_phase_one_connect_request * request = + xenidc_gnttab_channel_phase_one_connect_request_callback_to + ( channel->current_callback ); + + { + evtchn_op_t op = + { + .cmd = EVTCHNOP_alloc_unbound, + .u.alloc_unbound.dom = request->remote_domain_id + }; + + BUG_ON( HYPERVISOR_event_channel_op( &op ) != 0 ); + + channel->send_event_channel = op.u.alloc_unbound.port; + } + + { + int error = bind_evtchn_to_irqhandler + ( + channel->send_event_channel, + xenidc_gnttab_channel_send_interrupt, + SA_SAMPLE_RANDOM, + "xenidc", + &channel->send_irq_context + ); + + BUG_ON( error < 0 ); + + channel->send_irq = error; + } + + channel->send_ring_ref = + gnttab_claim_grant_reference( &channel->grant_ref_pool ); + + gnttab_grant_foreign_access_ref + ( + channel->send_ring_ref, + request->remote_domain_id, + virt_to_mfn( channel->send_ring ), + 1 /* readonly */ + ); + + request->send_ring_ref = channel->send_ring_ref; + request->send_event_channel = channel->send_event_channel; + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_c1c ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_do_phase_two_connect + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->do_phase_two_connect_1_work ); +} + +static void xenidc_gnttab_channel_do_phase_two_connect_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + xenidc_gnttab_channel_phase_two_connect_request * request = + xenidc_gnttab_channel_phase_two_connect_request_callback_to + ( channel->current_callback ); + + { + struct gnttab_map_grant_ref op = + { + .host_addr = (unsigned long)channel->recv_ring_area->addr, + .flags = GNTMAP_host_map | GNTMAP_readonly, + .dom = request->remote_domain_id, + .ref = request->recv_ring_ref + }; + + lock_vm_area( channel->recv_ring_area ); + + BUG_ON + ( HYPERVISOR_grant_table_op( GNTTABOP_map_grant_ref, &op, 1 ) ); + + unlock_vm_area( channel->recv_ring_area ); + + if( op.handle < 0 ) + { + trace0( "failed to map remote page" ); + + goto EXIT_NO_MAPPING; + } + + channel->recv_ring_handle = op.handle; + } + + { + evtchn_op_t op = + { + .cmd = EVTCHNOP_bind_interdomain, + .u.bind_interdomain.remote_dom = request->remote_domain_id, + .u.bind_interdomain.remote_port = request->recv_event_channel + }; + + if( HYPERVISOR_event_channel_op( &op ) != 0 ) + { + trace0( "failed to bind to remote event channel" ); + + goto EXIT_NO_BIND; + } + + channel->recv_event_channel = op.u.bind_interdomain.local_port; + } + + channel->recv_irq = bind_evtchn_to_irqhandler + ( + channel->recv_event_channel, + xenidc_gnttab_channel_recv_interrupt, + 0, + "xenidc", + &channel->recv_irq_context + ); + + if( channel->recv_irq < 0 ) + { + trace0( "failed to bind remote irq" ); + + goto EXIT_NO_IRQ; + } + + channel->recv_ring_lbr = xenidc_vaddress_create_lbr + ( + ( + (xenidc_channel_ring_header *) + channel->recv_ring_area->addr + ) + 1, + PAGE_SIZE - sizeof( xenidc_channel_ring_header ) + ); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_c2s ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return; + + EXIT_NO_IRQ: + + { + evtchn_op_t op = + { + .cmd = EVTCHNOP_close, + .u.close.port = channel->recv_event_channel + }; + + BUG_ON( HYPERVISOR_event_channel_op( &op ) != 0 ); + } + + EXIT_NO_BIND: + + { + struct gnttab_unmap_grant_ref op; + + op.host_addr = (unsigned long)channel->recv_ring_area->addr; + op.handle = channel->recv_ring_handle; + op.dev_bus_addr = 0; + + lock_vm_area( channel->recv_ring_area ); + + BUG_ON + ( + HYPERVISOR_grant_table_op( GNTTABOP_unmap_grant_ref, &op, 1 ) + ); + + unlock_vm_area( channel->recv_ring_area ); + } + + EXIT_NO_MAPPING: + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_c2f ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_connect_client + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->connect_client_1_work ); +} + +static void xenidc_gnttab_channel_connect_client_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + xenidc_channel_connect( &channel->channel ); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_ccc ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_disconnect_client + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->disconnect_client_1_work ); +} + +static void xenidc_gnttab_channel_disconnect_client_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + xenidc_channel_disconnect + ( &channel->channel, &channel->disconnect_client_2_callback ); + } +} + +static void xenidc_gnttab_channel_disconnect_client_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = container_of + ( callback, xenidc_gnttab_channel, disconnect_client_2_callback ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_dcc ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_gnttab_channel_do_phase_two_disconnect + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->do_phase_two_disconnect_1_work ); +} + +static void xenidc_gnttab_channel_do_phase_two_disconnect_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + unbind_evtchn_from_irqhandler + ( channel->recv_irq, &channel->recv_irq_context ); + + { + struct gnttab_unmap_grant_ref op; + + op.host_addr = (unsigned long)channel->recv_ring_area->addr; + op.handle = channel->recv_ring_handle; + op.dev_bus_addr = 0; + + lock_vm_area( channel->recv_ring_area ); + + BUG_ON + ( + HYPERVISOR_grant_table_op( GNTTABOP_unmap_grant_ref, &op, 1 ) + ); + + unlock_vm_area( channel->recv_ring_area ); + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_d2c ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_do_phase_one_disconnect + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->do_phase_one_disconnect_1_work ); +} + +static void xenidc_gnttab_channel_do_phase_one_disconnect_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + gnttab_end_foreign_access_ref( channel->send_ring_ref, 1 ); + + /* FIXME: need to extend grant-tables API so we can wait for other */ + /* side to stop referencing page. */ + + gnttab_release_grant_reference + ( &channel->grant_ref_pool, channel->send_ring_ref ); + + unbind_evtchn_from_irqhandler + ( channel->send_irq, &channel->send_irq_context ); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_d1c ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_kick_send_ring + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + if( !channel->send_ring_kick_out ) + { + channel->send_ring_kick_out = 1; + + (void)xenidc_work_schedule( &channel->kick_send_ring_1_work ); + } +} + +static void xenidc_gnttab_channel_kick_send_ring_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + int notify = 0; + + xenidc_local_buffer_reference ring_lbr; + xenidc_local_buffer_reference wrapping_lbr; + xenidc_channel_message * message; + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->send_ring_kick_out = 0; + + spin_unlock_irqrestore( &channel->lock, flags ); + + /* Create a reference to the send ring buffer. */ + + ring_lbr = xenidc_vaddress_create_lbr + ( + ( (xenidc_channel_ring_header *)channel->send_ring ) + 1, + PAGE_SIZE - sizeof( xenidc_channel_ring_header ) + ); + + /* Create a reference to the free space in the send ring buffer. */ + /* We use a wrapping reference to the above buffer so that we can */ + /* copy into the buffer and wrap automatically. */ + + wrapping_lbr = xenidc_wrapping_create_lbr + ( + &ring_lbr, + ( (xenidc_channel_ring_header *)channel->send_ring )-> + this_ring_producer_offset, + ( + (xenidc_channel_ring_header *) + channel->recv_ring_area->addr + ) + ->other_ring_consumer_offset, + xenidc_wrapping_client_type_producer + ); + + spin_lock_irqsave( &channel->lock, flags ); + + while + ( + ( !list_empty( &channel->message_list ) ) + && + ( + xenidc_local_buffer_reference_query_byte_count( &wrapping_lbr ) + > + ( + sizeof( xenidc_channel_ring_element_header ) + + + xenidc_local_buffer_reference_query_byte_count + ( + & + ( + message = list_entry + ( + channel->message_list.next, + xenidc_channel_message, + XENIDC_CHANNEL_MESSAGE_LINK + ) + ) + ->message_lbr + ) + ) + ) + ) + { + list_del_init( xenidc_channel_message_to_link( message ) ); + + spin_unlock_irqrestore( &channel->lock, flags ); + + { + xenidc_channel_ring_element_header header; + xenidc_local_buffer_reference header_lbr; + xenidc_concatenate_base base; + xenidc_local_buffer_reference element_lbr; + + memset( &header, 0, sizeof( header ) ); + + header.length = xenidc_local_buffer_reference_query_byte_count + ( &message->message_lbr ); + + header_lbr = + xenidc_vaddress_create_lbr( &header, sizeof( header ) ); + + element_lbr = xenidc_concatenate_create_lbr + ( &base, &header_lbr, &message->message_lbr ); + + xenidc_local_buffer_reference_advance + ( + &wrapping_lbr, + xenidc_local_buffer_reference_copy + ( &wrapping_lbr, &element_lbr ) + ); + } + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + + notify = 1; + + spin_lock_irqsave( &channel->lock, flags ); + } + + channel->send_ring_kick_out = 0; + + spin_unlock_irqrestore( &channel->lock, flags ); + + if( notify ) + { + wmb(); /* Ensure contents of ring written before offset updated. */ + + ( (xenidc_channel_ring_header *)channel->send_ring )-> + this_ring_producer_offset = + xenidc_local_buffer_reference_query_byte_offset + ( &wrapping_lbr ); + + notify_remote_via_irq( channel->send_irq ); + } + + spin_lock_irqsave( &channel->lock, flags ); + + if( !channel->send_ring_kick_out ) + { + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_ksc ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_gnttab_channel_kick_recv_ring + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + if( !channel->recv_ring_kick_out ) + { + channel->recv_ring_kick_out = 1; + + (void)xenidc_work_schedule( &channel->kick_recv_ring_1_work ); + } +} + +static void xenidc_gnttab_channel_kick_recv_ring_1( void * data ) +{ + trace(); + + { + xenidc_gnttab_channel * channel = (xenidc_gnttab_channel *)data; + + int error = 0; + + xenidc_local_buffer_reference wrapping_lbr; + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->recv_ring_kick_out = 0; + + spin_unlock_irqrestore( &channel->lock, flags ); + + /* Create a reference to the elements in the recv ring buffer. */ + /* We use a wrapping reference to the buffer so that we can copy out */ + /* of the buffer and wrap automatically. */ + + wrapping_lbr = xenidc_wrapping_create_lbr + ( + &channel->recv_ring_lbr, + channel->recv_ring_offset, + ( + (xenidc_channel_ring_header *) + channel->recv_ring_area->addr + ) + ->this_ring_producer_offset, + xenidc_wrapping_client_type_consumer + ); + + spin_lock_irqsave( &channel->lock, flags ); + + rmb(); /* Ensure we see latest data for contents of wrapping buffer. */ + + while + ( + channel->target_resource_free[ channel->next_target_resource ] + && + ( + xenidc_local_buffer_reference_query_byte_count( &wrapping_lbr ) + != + 0 + ) + ) + { + xenidc_local_buffer_reference element = wrapping_lbr; + + xenidc_gnttab_channel_target_resource * resource = + &channel->target_resources[ channel->next_target_resource ]; + + { + xenidc_channel_ring_element_header header; + + if + ( + ( + xenidc_local_buffer_reference_copy_out + ( &wrapping_lbr, &header, sizeof( header ) ) + == + sizeof( header ) + ) + && + ( + xenidc_local_buffer_reference_subrange + ( &element, sizeof( header ), header.length ) + == + header.length + ) + ) + { + xenidc_local_buffer_reference_advance + ( &wrapping_lbr, sizeof( header ) + header.length ); + + channel->target_resource_free + [ channel->next_target_resource ] = 0; + + channel->target_resource_offset + [ channel->next_target_resource ] = + xenidc_local_buffer_reference_query_byte_offset + ( &wrapping_lbr ); + if + ( + ++channel->next_target_resource + == + XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT + ) + { + channel->next_target_resource = 0; + } + } + else + { + error = 1; + + break; + } + } + + spin_unlock_irqrestore( &channel->lock, flags ); + + { + xenidc_channel_message * message = + xenidc_gnttab_channel_target_resource_to_message( resource ); + + xenidc_channel_message_set_message_lbr( message, element ); + + xenidc_channel_handle_message( &channel->channel, message ); + } + + spin_lock_irqsave( &channel->lock, flags ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + + channel->recv_ring_offset = + xenidc_local_buffer_reference_query_byte_offset( &wrapping_lbr ); + + if( error ) + { + channel->protocol_error( channel ); + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if( !channel->recv_ring_kick_out ) + { + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_krc ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_kick_recv_ring_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_gnttab_channel_target_resource * resource = + xenidc_gnttab_channel_target_resource_callback_to( callback ); + + xenidc_gnttab_channel * channel = + xenidc_gnttab_channel_target_resource_query_channel( resource ); + + int current_target_resource = + ( + ( + ( (unsigned long)resource ) + - + ( (unsigned long)&channel->target_resources[ 0 ] ) + ) + / + sizeof( xenidc_gnttab_channel_target_resource ) + ); + + if( xenidc_callback_query_error( callback ) != XENIDC_ERROR_SUCCESS ) + { + channel->protocol_error( channel ); + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->target_resource_free[ current_target_resource ] = 1; + + if( current_target_resource == channel->first_target_resource ) + { + u16 offset; + + do + { + offset = channel->target_resource_offset + [ channel->first_target_resource ]; + + if + ( + ++channel->first_target_resource + == + XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT + ) + { + channel->first_target_resource = 0; + } + } + while + ( + channel->target_resource_free + [ channel->first_target_resource ] + && + ( + channel->first_target_resource + != + channel->next_target_resource + ) + ); + + mb(); /* Ensure reads complete before we free space. */ + + ( (xenidc_channel_ring_header *)channel->send_ring )-> + other_ring_consumer_offset = offset; + + notify_remote_via_irq( channel->recv_irq ); + + if + ( + !channel->target_resource_free + [ channel->first_target_resource ] + ) + { + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_trc ); + } + else + { + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_tri ); + } + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_gnttab_channel_complete_current_callback + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + xenidc_callback_success( channel->current_callback ); +} + +static void xenidc_gnttab_channel_fail_current_callback + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + xenidc_callback_complete + ( channel->current_callback, XENIDC_ERROR_FAILURE ); +} + +static void xenidc_gnttab_channel_fail_out_messages + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + while( !list_empty( &channel->message_list ) ) + { + xenidc_channel_message * message = list_entry + ( + channel->message_list.next, + xenidc_channel_message, + XENIDC_CHANNEL_MESSAGE_LINK + ); + + list_del_init( xenidc_channel_message_to_link( message ) ); + + xenidc_callback_success + ( xenidc_channel_message_to_callback( message ) ); + } +} + +static void xenidc_gnttab_channel_test_target_resources + ( xenidc_gnttab_channel * channel ) +{ + trace(); + + if( channel->target_resource_free[ channel->first_target_resource ] ) + { + xenidc_gnttab_channel_handle_stimulus + ( channel, xenidc_gnttab_channel_stimulus_tri ); + } +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gnttab_channel_enumeration.dot --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_gnttab_channel_enumeration.dot Sun Oct 30 16:03:34 2005 @@ -0,0 +1,55 @@ +digraph enumeration { +size="7,7" + +i[style=filled,fillcolor=green] +i->i_c1r[label="c1r\ndo_phase_one_connect"] + +i_c1r[style=filled,fillcolor=green] +i_c1r->i_c1r[label="sir"] +i_c1r->i_c1r_c1c[label="c1c\ncomplete_current_callback"] + +i_c1r_c1c[style=filled,fillcolor=green] +i_c1r_c1c->i_c1r_c1c_c2r[label="c2r\ndo_phase_two_connect"] +i_c1r_c1c->i_c1r_c1c_d1r[label="d1r\ndo_phase_one_disconnect"] +i_c1r_c1c->i_c1r_c1c[label="sir"] + +i_c1r_c1c_c2r[style=filled,fillcolor=green] +i_c1r_c1c_c2r->i_c1r_c1c_c2r[label="sir/rir"] +i_c1r_c1c_c2r->i_c1r_c1c_c2r_c2s[label="c2s\nconnect_client"] +i_c1r_c1c_c2r->i_c1r_c1c[label="c2f\nfail_current_callback"] + +i_c1r_c1c_d1r[style=filled,fillcolor=orange] +i_c1r_c1c_d1r->i_c1r_c1c_d1r[label="sir"] +i_c1r_c1c_d1r->i[label="d1c\ncomplete_current_callback"] + +i_c1r_c1c_c2r_c2s[style=filled,fillcolor=green] +i_c1r_c1c_c2r_c2s->i_c1r_c1c_c2r_c2s[label="mqr/sir/rir"] +i_c1r_c1c_c2r_c2s->i_c1r_c1c_c2r_c2s_ccc[label="ccc\ncomplete_current_callback\nkick_send_ring\nkick_recv_ring"] + +i_c1r_c1c_c2r_c2s_ccc[style=filled,fillcolor=green] +i_c1r_c1c_c2r_c2s_ccc->i_c1r_c1c_c2r_c2s_ccc[label="mqr/sir\nkick_send_ring"] +i_c1r_c1c_c2r_c2s_ccc->i_c1r_c1c_c2r_c2s_ccc[label="rir/trc/tri\nkick_recv_ring"] +i_c1r_c1c_c2r_c2s_ccc->i_c1r_c1c_c2r_c2s_ccc[label="ksc/krc/trb"] +i_c1r_c1c_c2r_c2s_ccc->i_c1r_c1c_c2r_c2s_ccc_d2r[label="d2r\nkick_send_ring\nkick_recv_ring"] + +i_c1r_c1c_c2r_c2s_ccc_d2r[style=filled,fillcolor=orange] +i_c1r_c1c_c2r_c2s_ccc_d2r->i_c1r_c1c_c2r_c2s_ccc_d2r[label="mqr/sir/rir/trb/trc/tri"] +i_c1r_c1c_c2r_c2s_ccc_d2r->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc[label="ksc/krc"] + +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc[style=filled,fillcolor=orange] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc[label="mqr/sir/rir/trb/trc/tri"] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc[label="ksc/krc\ndisconnect_client\nfail_out_messages"] + +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc[style=filled,fillcolor=orange] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc[label="mqr\nfail_out_messages"] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc[label="sir/rir/trc/tri"] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc[label="dcc\ntest_target_resources"] + +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc[style=filled,fillcolor=orange] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc[label="sir/rir/trc"] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri[label="tri\nphase_two_disconnect"] + +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri[style=filled,fillcolor=orange] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri->i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri[label="sir/rir"] +i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri->i_c1r_c1c[label="d2c\ncomplete_current_callback"] +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_local_buffer_reference.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_local_buffer_reference.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,618 @@ +/*****************************************************************************/ +/* Xen inter-domain communication local buffer references. */ +/* */ +/* 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 +#include +#include "xenidc_trace.h" + +#ifdef MIN +#undef MIN +#endif + +#define MIN( X, Y ) ( ( (X) < (Y) ) ? (X) : (Y) ) + +#define XENIDC_BUFFER_HASH_COUNT 255 + +static DEFINE_RWLOCK( xenidc_local_buffer_reference_lock ); + +static struct list_head + xenidc_buffer_concrete_hash[ XENIDC_BUFFER_HASH_COUNT ]; + +static struct list_head + xenidc_buffer_copy_hash[ XENIDC_BUFFER_HASH_COUNT ]; + +static struct list_head + xenidc_buffer_virtual_hash[ XENIDC_BUFFER_HASH_COUNT ]; + +static void xenidc_local_buffer_reference_init( void ) +{ + trace(); + + { + unsigned long flags; + + write_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + { + static int initialised = 0; + + if( !initialised ) + { + int i; + + for( i = 0; i < XENIDC_BUFFER_HASH_COUNT; i++ ) + { + INIT_LIST_HEAD( &xenidc_buffer_concrete_hash[ i ] ); + INIT_LIST_HEAD( &xenidc_buffer_copy_hash[ i ] ); + INIT_LIST_HEAD( &xenidc_buffer_virtual_hash[ i ] ); + } + + initialised = 1; + } + } + + write_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + } +} + +static void xenidc_local_buffer_reference_default_zero +( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr +) +{ + trace(); + + { + u8 buffer[ 64 ]; + + xenidc_local_buffer_reference temp_lbr = *lbr; + + memset( buffer, 0, 64 ); + + do + { + xenidc_buffer_byte_count this_go = MIN( temp_lbr.byte_count, 64 ); + + class->copy_in_or_out( class, &temp_lbr, buffer, this_go, 0 ); + + xenidc_local_buffer_reference_advance( &temp_lbr, this_go ); + } + while( temp_lbr.byte_count != 0 ); + } +} + +xenidc_buffer_type xenidc_local_buffer_reference_register_buffer_concrete_class +( + xenidc_buffer_concrete_class * class, + void ( * copy_in_or_out ) + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count byte_count, + int out + ), + void ( * zero ) + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr + ) +) +{ + trace(); + + xenidc_local_buffer_reference_init(); + + INIT_LIST_HEAD( &class->link ); + + class->copy_in_or_out = copy_in_or_out; + class->zero = ( zero != NULL ) ? + zero : xenidc_local_buffer_reference_default_zero; + + { + unsigned long flags; + + write_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + { + static xenidc_buffer_type type_index = 0; + + class->type = ++type_index; + } + + { + int i = ( class->type % XENIDC_BUFFER_HASH_COUNT ); + + list_add_tail( &class->link, &xenidc_buffer_concrete_hash[ i ] ); + } + + write_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + } + + return class->type; +} + +static inline xenidc_buffer_concrete_class * + xenidc_local_buffer_reference_find_concrete_class( xenidc_buffer_type type ) +{ + trace(); + + { + int i = type % XENIDC_BUFFER_HASH_COUNT; + + xenidc_buffer_concrete_class * class; + + unsigned long flags; + + read_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + list_for_each_entry( class, &xenidc_buffer_concrete_hash[ i ], link ) + { + if( class->type == type ) + { + read_unlock_irqrestore + ( &xenidc_local_buffer_reference_lock, flags ); + + return class; + } + } + + read_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + + BUG(); + + return NULL; + } +} + +void xenidc_local_buffer_reference_register_buffer_copy_class +( + xenidc_buffer_copy_class * class, + xenidc_buffer_type target_type, + xenidc_buffer_type source_type, + void ( * copy ) + ( + xenidc_buffer_copy_class * class, + xenidc_local_buffer_reference * target, + xenidc_local_buffer_reference * source, + xenidc_buffer_byte_count byte_count + ) +) +{ + trace(); + + INIT_LIST_HEAD( &class->link ); + + class->target_type = target_type; + class->source_type = source_type; + class->copy = copy; + + { + unsigned long flags; + + write_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + { + int i = + ( + ( class->target_type << 16 | class->source_type ) + % + XENIDC_BUFFER_HASH_COUNT + ); + + list_add_tail( &class->link, &xenidc_buffer_copy_hash[ i ] ); + } + + write_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + } +} + +static inline xenidc_buffer_copy_class * + xenidc_local_buffer_reference_find_copy_class + ( xenidc_buffer_type target_type, xenidc_buffer_type source_type ) +{ + trace(); + + { + int i = + ( ( target_type << 16 | source_type ) % XENIDC_BUFFER_HASH_COUNT ); + + xenidc_buffer_copy_class * class; + + unsigned long flags; + + read_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + list_for_each_entry( class, &xenidc_buffer_copy_hash[ i ], link ) + { + if + ( + ( class->target_type == target_type ) + && + ( class->source_type == source_type ) + ) + { + read_unlock_irqrestore + ( &xenidc_local_buffer_reference_lock, flags ); + + return class; + } + } + + read_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + + return NULL; + } +} + +xenidc_buffer_type xenidc_local_buffer_reference_register_buffer_virtual_class +( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference ( * resolve ) + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr + ), + void ( * advance ) + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_count + ) +) +{ + trace(); + + xenidc_local_buffer_reference_init(); + + INIT_LIST_HEAD( &class->link ); + + class->resolve = resolve; + class->advance = advance; + + { + unsigned long flags; + + write_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + { + static xenidc_buffer_type type_index = 0; + + class->type = + ( + ++type_index + | + XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL + | + ( + ( advance != NULL ) + ? XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL_ADVANCE + : 0 + ) + ); + } + + { + int i = ( class->type % XENIDC_BUFFER_HASH_COUNT ); + + list_add_tail( &class->link, &xenidc_buffer_virtual_hash[ i ] ); + } + + write_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + } + + return class->type; +} + +static inline xenidc_buffer_virtual_class * + xenidc_local_buffer_reference_find_virtual_class( xenidc_buffer_type type ) +{ + trace(); + + { + int i = type % XENIDC_BUFFER_HASH_COUNT; + + xenidc_buffer_virtual_class * class; + + unsigned long flags; + + read_lock_irqsave( &xenidc_local_buffer_reference_lock, flags ); + + list_for_each_entry( class, &xenidc_buffer_virtual_hash[ i ], link ) + { + if( class->type == type ) + { + read_unlock_irqrestore + ( &xenidc_local_buffer_reference_lock, flags ); + + return class; + } + } + + read_unlock_irqrestore( &xenidc_local_buffer_reference_lock, flags ); + + BUG(); + + return NULL; + } +} + +static xenidc_local_buffer_reference xenidc_local_buffer_reference_resolve + ( xenidc_local_buffer_reference * lbr ) +{ + trace(); + + { + xenidc_local_buffer_reference resolved_lbr = *lbr; + + while( 1 ) + { + if + ( + ( + resolved_lbr.type + & + XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL + ) + == + 0 + ) + { + return resolved_lbr; + } + else + { + xenidc_buffer_virtual_class * class = + xenidc_local_buffer_reference_find_virtual_class + ( resolved_lbr.type ); + + resolved_lbr = class->resolve( class, &resolved_lbr ); + } + } + } +} + +xenidc_buffer_byte_count xenidc_local_buffer_reference_copy_in_or_out +( + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count buffer_byte_count, + int out +) +{ + trace(); + + { + /* Total amount to copy is the smaller of the two buffers. */ + + xenidc_buffer_byte_count byte_count = + MIN( lbr->byte_count, buffer_byte_count ); + + xenidc_buffer_byte_count remainder_offset = 0; + xenidc_buffer_byte_count remainder_count = byte_count; + + xenidc_local_buffer_reference top_lbr = *lbr; + + while( remainder_count != 0 ) + { + /* top_lbr might be virtual so we resolve it to get a concrete */ + /* lbr for the first contiguous chunk of top_lbr. */ + + xenidc_local_buffer_reference resolved_lbr = + xenidc_local_buffer_reference_resolve( &top_lbr ); + + /* resolved_lbr is guaranteed to be concrete so we can look up */ + /* its copy method. */ + + xenidc_buffer_concrete_class * class = + xenidc_local_buffer_reference_find_concrete_class + ( resolved_lbr.type ); + + /* Amount to copy this go may be restricted by the size of the */ + /* resolved lbr. */ + + xenidc_buffer_byte_count this_go = + MIN( resolved_lbr.byte_count, remainder_count ); + + class->copy_in_or_out + ( + class, + &resolved_lbr, + ( (char *)buffer ) + remainder_offset, + this_go, + out + ); + + remainder_offset += this_go; + remainder_count -= this_go; + + xenidc_local_buffer_reference_advance( &top_lbr, this_go ); + } + + return byte_count; + } +} + +void xenidc_local_buffer_reference_zero( xenidc_local_buffer_reference * lbr ) +{ + trace(); + + { + xenidc_local_buffer_reference top_lbr = *lbr; + + while( top_lbr.byte_count != 0 ) + { + /* top_lbr might be virtual so we resolve it to get a concrete */ + /* lbr for the first contiguous chunk of top_lbr. */ + + xenidc_local_buffer_reference resolved_lbr = + xenidc_local_buffer_reference_resolve( &top_lbr ); + + xenidc_buffer_concrete_class * class = + xenidc_local_buffer_reference_find_concrete_class + ( resolved_lbr.type ); + + class->zero( class, &resolved_lbr ); + + xenidc_local_buffer_reference_advance + ( &top_lbr, resolved_lbr.byte_count ); + } + } +} + +xenidc_buffer_byte_count xenidc_local_buffer_reference_copy +( + xenidc_local_buffer_reference * target, + xenidc_local_buffer_reference * source +) +{ + trace(); + + { + /* Total amount to copy is the smaller of the two buffers. */ + + xenidc_buffer_byte_count byte_count = + MIN( target->byte_count, source->byte_count ); + + xenidc_local_buffer_reference top_target_lbr = *target; + xenidc_local_buffer_reference top_source_lbr = *source; + + xenidc_buffer_byte_count remainder_count = byte_count; + + while( remainder_count != 0 ) + { + xenidc_local_buffer_reference resolved_target_lbr = + xenidc_local_buffer_reference_resolve( &top_target_lbr ); + + xenidc_local_buffer_reference resolved_source_lbr = + xenidc_local_buffer_reference_resolve( &top_source_lbr ); + + xenidc_buffer_byte_count this_go = MIN + ( + resolved_target_lbr.byte_count, + resolved_source_lbr.byte_count + ); + + xenidc_buffer_copy_class * copy_class = + xenidc_local_buffer_reference_find_copy_class + ( resolved_target_lbr.type, resolved_source_lbr.type ); + + if( copy_class != NULL ) + { + copy_class->copy + ( + copy_class, + &resolved_target_lbr, + &resolved_source_lbr, + this_go + ); + } + else + { + u8 buffer[ 64 ]; + + xenidc_buffer_concrete_class * target_concrete_class = + xenidc_local_buffer_reference_find_concrete_class + ( resolved_target_lbr.type ); + + xenidc_buffer_concrete_class * source_concrete_class = + xenidc_local_buffer_reference_find_concrete_class + ( resolved_source_lbr.type ); + + xenidc_local_buffer_reference temp_target = + resolved_target_lbr; + xenidc_local_buffer_reference temp_source = + resolved_source_lbr; + + xenidc_buffer_byte_count nested_remainder_count = this_go; + + do + { + xenidc_buffer_byte_count nested_this_go = + MIN( 64, nested_remainder_count ); + + source_concrete_class->copy_in_or_out + ( + source_concrete_class, + &temp_source, + buffer, + nested_this_go, + 1 + ); + + target_concrete_class->copy_in_or_out + ( + target_concrete_class, + &temp_target, + buffer, + nested_this_go, + 0 + ); + + xenidc_local_buffer_reference_advance + ( &temp_target, nested_this_go ); + + xenidc_local_buffer_reference_advance + ( &temp_source, nested_this_go ); + + nested_remainder_count -= nested_this_go; + } + while( nested_remainder_count != 0 ); + } + + xenidc_local_buffer_reference_advance( &top_target_lbr, this_go ); + xenidc_local_buffer_reference_advance( &top_source_lbr, this_go ); + + remainder_count -= this_go; + } + + return byte_count; + } +} + +xenidc_buffer_byte_count xenidc_local_buffer_reference_virtual_advance + ( xenidc_local_buffer_reference * lbr, xenidc_buffer_byte_count byte_count ) +{ + trace(); + + { + xenidc_buffer_byte_count actual_byte_count = + MIN( lbr->byte_count, byte_count ); + + xenidc_buffer_virtual_class * class = + xenidc_local_buffer_reference_find_virtual_class( lbr->type ); + + class->advance( class, lbr, actual_byte_count ); + + return actual_byte_count; + } +} + +EXPORT_SYMBOL( xenidc_local_buffer_reference_register_buffer_concrete_class ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_register_buffer_copy_class ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_register_buffer_virtual_class ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_copy_in_or_out ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_zero ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_copy ); +EXPORT_SYMBOL( xenidc_local_buffer_reference_virtual_advance ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_rbr_mapper_pool.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_rbr_mapper_pool.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* Xen remote buffer reference mapper pool. */ +/* */ +/* 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 "xenidc_trace.h" + +xenidc_rbr_mapper_pool * xenidc_allocate_rbr_mapper_pool + ( u32 anti_deadlock_page_count ) +{ + trace(); + + /* FIXME */ + + return 1; +} + +void xenidc_free_rbr_mapper_pool( xenidc_rbr_mapper_pool * pool ) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_mapper_pool_reserve_and_map_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_mapper_pool_abort_reserve_and_map_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_mapper_pool_unmap_and_unreserve_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +EXPORT_SYMBOL( xenidc_allocate_rbr_mapper_pool ); +EXPORT_SYMBOL( xenidc_free_rbr_mapper_pool ); +EXPORT_SYMBOL( xenidc_rbr_mapper_pool_reserve_and_map_rbrs ); +EXPORT_SYMBOL( xenidc_rbr_mapper_pool_abort_reserve_and_map_rbrs ); +EXPORT_SYMBOL( xenidc_rbr_mapper_pool_unmap_and_unreserve_rbrs ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_rbr_provider_pool.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_rbr_provider_pool.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,80 @@ +/*****************************************************************************/ +/* Xen remote buffer reference provider pool. */ +/* */ +/* 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 "xenidc_trace.h" + +xenidc_rbr_provider_pool * xenidc_allocate_rbr_provider_pool + ( u32 anti_deadlock_page_count ) +{ + trace(); + + /* FIXME */ + + return 1; +} + +void xenidc_free_rbr_provider_pool( xenidc_rbr_provider_pool * pool ) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_provider_pool_reserve_and_create_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_provider_pool_abort_reserve_and_create_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +void xenidc_rbr_provider_pool_revoke_and_unreserve_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +) +{ + trace(); + + /* FIXME */ +} + +EXPORT_SYMBOL( xenidc_allocate_rbr_provider_pool ); +EXPORT_SYMBOL( xenidc_free_rbr_provider_pool ); +EXPORT_SYMBOL( xenidc_rbr_provider_pool_reserve_and_create_rbrs ); +EXPORT_SYMBOL( xenidc_rbr_provider_pool_abort_reserve_and_create_rbrs ); +EXPORT_SYMBOL( xenidc_rbr_provider_pool_revoke_and_unreserve_rbrs ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_trace.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_trace.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,54 @@ +/*****************************************************************************/ +/* Simple trace macros. */ +/* */ +/* 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 XENIDC_TRACE_H +#define XENIDC_TRACE_H + +#include +#include + +#ifdef CONFIG_XEN_IDC_TRACE + +#define trace0( format ) \ +printk( KERN_INFO "xenidc %s:" format "\n", __PRETTY_FUNCTION__ ) + +#define trace1( format, a0 ) \ +printk( KERN_INFO "xenidc %s:" format "\n", __PRETTY_FUNCTION__, a0 ) + +#define trace2( format, a0, a1 ) \ +printk( KERN_INFO "xenidc %s:" format "\n", __PRETTY_FUNCTION__, a0, a1 ) + +#define trace3( format, a0, a1, a2 ) \ +printk( KERN_INFO "xenidc %s:" format "\n", __PRETTY_FUNCTION__, a0, a1, a2 ) + +#define trace() trace0( "" ) + +#else + +#define trace0( format ) +#define trace1( format,a0 ) +#define trace2( format,a0, a1 ) +#define trace3( format,a0, a1, a2 ) +#define trace() + +#endif + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_vaddress.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_vaddress.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,129 @@ +/*****************************************************************************/ +/* Xen inter-domain communication vaddress local buffer reference type. */ +/* */ +/* 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 "xenidc_trace.h" + +static xenidc_buffer_type xenidc_vaddress_type; + +static void xenidc_vaddress_copy_in_or_out +( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count byte_count, + int out +) +{ + trace(); + + if( out ) + { + memcpy( buffer, ( (char *)lbr->base ) + lbr->byte_offset, byte_count ); + } + else + { + memcpy( ( (char *)lbr->base ) + lbr->byte_offset, buffer, byte_count ); + } +} + +static void xenidc_vaddress_zero +( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr +) +{ + trace(); + + memset( ( (char *)lbr->base ) + lbr->byte_offset, 0, lbr->byte_count ); +} + +static void xenidc_vaddress_init( void ) +{ + trace(); + + { + static DEFINE_RWLOCK( xenidc_vaddress_lock ); + + unsigned long flags; + + read_lock_irqsave( &xenidc_vaddress_lock, flags ); + + { + static int initialised = 0; + + if( !initialised ) + { + read_unlock_irqrestore + ( &xenidc_vaddress_lock, flags ); + + write_lock_irqsave + ( &xenidc_vaddress_lock, flags ); + + if( !initialised ) + { + static xenidc_buffer_concrete_class + xenidc_vaddress_concrete_class; + + xenidc_vaddress_type = + xenidc_local_buffer_reference_register_buffer_concrete_class + ( + &xenidc_vaddress_concrete_class, + xenidc_vaddress_copy_in_or_out, + xenidc_vaddress_zero + ); + + initialised = 1; + } + + write_unlock_irqrestore + ( &xenidc_vaddress_lock, flags ); + + read_lock_irqsave + ( &xenidc_vaddress_lock, flags ); + } + } + + read_unlock_irqrestore( &xenidc_vaddress_lock, flags ); + } +} + +xenidc_local_buffer_reference xenidc_vaddress_create_lbr + ( void * address, xenidc_buffer_byte_count byte_count ) +{ + trace(); + + xenidc_vaddress_init(); + + { + xenidc_local_buffer_reference lbr; + + lbr.type = xenidc_vaddress_type; + lbr.base = address; + lbr.byte_offset = 0; + lbr.byte_count = byte_count; + + return lbr; + } +} + +EXPORT_SYMBOL( xenidc_vaddress_create_lbr ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_work.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_work.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,110 @@ +/*****************************************************************************/ +/* Enhanced work queue service */ +/* 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 "xenidc_work.h" + +DEFINE_SPINLOCK( xenidc_work_list_lock ); + +LIST_HEAD( xenidc_work_list ); + +static void xenidc_work_function( void * ignored ); + +DECLARE_WORK( xenidc_work_work, xenidc_work_function, NULL ); + +DECLARE_WAIT_QUEUE_HEAD( xenidc_work_waitqueue ); + +LIST_HEAD( xenidc_work_condition ); + +void xenidc_work_wake_up( void ) +{ + unsigned long flags; + + spin_lock_irqsave( &xenidc_work_list_lock, flags ); + + while( !list_empty( &xenidc_work_condition ) ) + { + list_del_init( xenidc_work_condition.next ); + } + + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); + + wake_up( &xenidc_work_waitqueue ); +} + +int xenidc_work_schedule( xenidc_work * work ) +{ + int scheduled = 0; + + unsigned long flags; + + spin_lock_irqsave( &xenidc_work_list_lock, flags ); + + if( list_empty( &work->link ) ) + { + list_add_tail( &work->link, &xenidc_work_list ); + + scheduled = 1; + } + + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); + + if( scheduled ) + { + xenidc_work_wake_up(); + + schedule_work( &xenidc_work_work ); + } + + return scheduled; +} + +static void xenidc_work_function( void * ignored ) +{ + unsigned long flags; + + spin_lock_irqsave( &xenidc_work_list_lock, flags ); + + while( !list_empty( &xenidc_work_list ) ) + { + xenidc_work * work = list_entry + ( + xenidc_work_list.next, + xenidc_work, + link + ); + + list_del_init( &work->link ); + + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); + + xenidc_work_perform_synchronously( work ); + + spin_lock_irqsave( &xenidc_work_list_lock, flags ); + } + + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); +} + +EXPORT_SYMBOL( xenidc_work_schedule ); +EXPORT_SYMBOL( xenidc_work_list ); +EXPORT_SYMBOL( xenidc_work_condition ); +EXPORT_SYMBOL( xenidc_work_waitqueue ); +EXPORT_SYMBOL( xenidc_work_wake_up ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_wrapping.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_wrapping.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,165 @@ +/*****************************************************************************/ +/* Xen inter-domain communication wrapping local buffer reference type. */ +/* */ +/* 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 "xenidc_trace.h" + +#ifdef MIN +#undef MIN +#endif + +#define MIN( X, Y ) ( ( (X) < (Y) ) ? (X) : (Y) ) + +static xenidc_buffer_type xenidc_wrapping_type; + +static xenidc_local_buffer_reference xenidc_wrapping_resolve +( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr +) +{ + trace(); + + { + xenidc_local_buffer_reference resolved_lbr = + *(xenidc_local_buffer_reference *)lbr->base; + + xenidc_local_buffer_reference_subrange + ( + &resolved_lbr, + lbr->byte_offset, + MIN( lbr->byte_count, resolved_lbr.byte_count - lbr->byte_offset ) + ); + + return resolved_lbr; + } +} + +static void xenidc_wrapping_advance +( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_count +) +{ + trace(); + + lbr->byte_offset += byte_count; + lbr->byte_offset %= + ( (xenidc_local_buffer_reference *)lbr->base )->byte_count; + lbr->byte_count -= byte_count; +} + +static void xenidc_wrapping_init( void ) +{ + trace(); + + { + static DEFINE_RWLOCK( xenidc_wrapping_lock ); + + unsigned long flags; + + read_lock_irqsave( &xenidc_wrapping_lock, flags ); + + { + static int initialised = 0; + + if( !initialised ) + { + read_unlock_irqrestore + ( &xenidc_wrapping_lock, flags ); + + write_lock_irqsave + ( &xenidc_wrapping_lock, flags ); + + if( !initialised ) + { + static xenidc_buffer_virtual_class + xenidc_wrapping_virtual_class; + + xenidc_wrapping_type = + xenidc_local_buffer_reference_register_buffer_virtual_class + ( + &xenidc_wrapping_virtual_class, + xenidc_wrapping_resolve, + xenidc_wrapping_advance + ); + + initialised = 1; + } + + write_unlock_irqrestore + ( &xenidc_wrapping_lock, flags ); + + read_lock_irqsave + ( &xenidc_wrapping_lock, flags ); + } + } + + read_unlock_irqrestore( &xenidc_wrapping_lock, flags ); + } +} + +xenidc_local_buffer_reference xenidc_wrapping_create_lbr +( + xenidc_local_buffer_reference * underlying_buffer, + xenidc_buffer_byte_count start_offset, + xenidc_buffer_byte_count end_offset, + xenidc_wrapping_client_type client_type +) +{ + trace(); + + xenidc_wrapping_init(); + + { + xenidc_local_buffer_reference lbr; + + lbr.type = xenidc_wrapping_type; + lbr.base = underlying_buffer; + lbr.byte_offset = start_offset; + lbr.byte_count = + ( + ( start_offset < end_offset ) + ? + ( end_offset - start_offset ) + : + ( + ( start_offset > end_offset ) + ? + ( end_offset + underlying_buffer->byte_count - start_offset ) + : + ( + ( client_type == xenidc_wrapping_client_type_producer ) + ? + underlying_buffer->byte_count + : + 0 + ) + ) + ); + + return lbr; + } +} + +EXPORT_SYMBOL( xenidc_wrapping_create_lbr ); diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_xbgt_channel.c --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_xbgt_channel.c Sun Oct 30 16:03:34 2005 @@ -0,0 +1,1737 @@ +/*****************************************************************************/ +/* This is a class which uses a xenidc_gnttab_channel (grant-tables based */ +/* message channel class) and xenbus to implement an interdomain message */ +/* channel with grant-tables based message transfer and xenbus based */ +/* bring-up and tear-down handshaking. */ +/* This class is used in the implementation of the xenidc_endpoint class. */ +/* */ +/* 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 */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* The state machine below makes the following assumptions: */ +/* */ +/* 1) The store might contain some stale cruft from the last time our device */ +/* driver failed. This is a handy assumption for testing new versions of */ +/* the driver but isn't strictly necessary. */ +/* */ +/* 2) If the other side generates a protocol error on the inter-domain */ +/* connection then we attempt to disconnect and reconnect. An alternative */ +/* behaviour would be to wait for our interface to be called to disconnect */ +/* and then reconnect before retrying. */ +/* */ +/* 3) If we experience an internal failure (fail to register watch for */ +/* example) then we attempt to disconnect and wait for our interface to be */ +/* called to disconnect (by module unload for example) and reconnect before */ +/* retrying. */ +/* */ +/* 4) Connection and disconnection of the channel is done in two phases: the */ +/* first phase makes the local resources available to the remote side, the */ +/* second phase uses the resources of the remote side to complete the */ +/* connection. */ +/* */ +/* The key for the stimuli is as follows: */ +/* */ +/* cn: interface called to connect the channel when interface state is */ +/* disconnected (for example on module load). */ +/* */ +/* pe: protocol error detected when channel is in a state between phase two */ +/* connected and the completion of the phase two disconnect callback. */ +/* */ +/* dn: interface called to disconnect the channel when the interface state */ +/* is connected (for example on module unload). */ +/* */ +/* ou: a synchronous stimulus from the response test_other_state which */ +/* indicates that the other state is still unknown because the watch */ +/* callback hasn't happened yet. Can only happen when making the response */ +/* test_other_state. */ +/* */ +/* od: other state is disconnected. This is both a synchronous stimulus */ +/* from test_other_state and an asynchronous stimulus from the watch */ +/* function. Disconnected means that we can't read at least the other side's */ +/* ready node from the store. */ +/* */ +/* or: other state is ready. This is both a synchronous stimulus from */ +/* test_other_state and an asynchronous stimulus from the watch function. */ +/* Ready means that we can see the other side's ready node but not the */ +/* ring-reference and event-channel information. */ +/* */ +/* oc: other state is connected. This is both a synchronous stimulus from */ +/* test_other_state and an asynchronous stimulus from the watch function. */ +/* Connected means that we found both the ready node and the connected */ +/* information in the store. */ +/* */ +/* If the values of the connected information change when the other side is */ +/* connected then we generate the 'oc' stimulus again which forces a */ +/* reconnect. */ +/* */ +/* rs: An asynchronous response was successful (we only make one response at */ +/* a time so all asynchronous responses have the same completion stimuli). */ +/* */ +/* rf: An asynchronous response failed. Only register_watch, clear_store, */ +/* write_ready, write_connected, phase_two_connect can fail. Only */ +/* phase_two_connect has a good reason for failure: the other side might */ +/* have passed bogus parameters; the other failures are poor API design and */ +/* ought to be promoted to domain failures. */ +/* */ +/* The state machine responses are as follows: */ +/* */ +/* test_other_state: what state do we currently think the other side is in */ +/* as reflected by the last watch event. Synchronous (called with the lock */ +/* held) completes with ou/od/or/oc. */ +/* */ +/* register_watch: register a watch on the other side. */ +/* */ +/* unregister_watch: unregister the watch. */ +/* */ +/* clear_store: remove the ready node and connected information. */ +/* */ +/* write_ready: write the ready node to the store. */ +/* */ +/* write_connected: write the connected information to the store. */ +/* */ +/* phase_one_connect: grant the remote side access to the local page etc. */ +/* */ +/* phase_two_connect: map the remote page etc. */ +/* */ +/* phase_two_disconnect: unmap the remote page. */ +/* */ +/* phase_one_disconnect: revoke the access of the remote side. */ +/* */ +/* complete_disconnect: When our interface is called to get us to disconnect */ +/* the channel we quiesce and disconnect and then call this to indicate we */ +/* are done. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include "xenidc_trace.h" + +typedef enum +{ + xenidc_xbgt_channel_stimulus_cn, /* Connect: disconnected */ + xenidc_xbgt_channel_stimulus_pe, /* Protocol error: ph2c->ph2dc inclusive*/ + xenidc_xbgt_channel_stimulus_dn, /* Disconnect: connected */ + xenidc_xbgt_channel_stimulus_ou, /* Other unknown: test other state only */ + xenidc_xbgt_channel_stimulus_od, /* Other disconnected: watch / test */ + xenidc_xbgt_channel_stimulus_or, /* Other ready: watch / test */ + xenidc_xbgt_channel_stimulus_oc, /* Other connected: watch / test */ + xenidc_xbgt_channel_stimulus_rs, /* Response successful: making response */ + xenidc_xbgt_channel_stimulus_rf /* Response failed: r. watch, tra, ph2c */ +} +xenidc_xbgt_channel_stimulus; + +static void xenidc_xbgt_channel_handle_stimulus + ( xenidc_xbgt_channel * channel, xenidc_xbgt_channel_stimulus stimulus ); + +static int xenidc_xbgt_channel_init_or_exit + ( xenidc_xbgt_channel * channel, int exit ); + +int xenidc_xbgt_channel_init( xenidc_xbgt_channel * channel ) +{ + return xenidc_xbgt_channel_init_or_exit( channel, 0 ); +} + +void xenidc_xbgt_channel_connect +( + xenidc_xbgt_channel * channel, + char * local_path, + char * remote_path, + domid_t remote_domain_id +) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->local_path = local_path; + channel->remote_path = remote_path; + channel->remote_domain_id = remote_domain_id; + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_cn ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_xbgt_channel_watch + ( struct xenbus_watch * watch, const char ** vec, unsigned int len ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = + container_of( watch, xenidc_xbgt_channel, watch ); + + struct xenbus_transaction * transaction = xenbus_transaction_start(); + + if( IS_ERR( transaction ) ) + { + trace0( "error starting transaction" ); + + goto DISCONNECTED; + } + + { + unsigned int event_channel; + unsigned int ring_reference; + unsigned int ready; + + int error = xenbus_gather + ( + transaction, + watch->node, + "ready", "%u", &ready, + "event-channel", "%u", &event_channel, + "ring-reference", "%u", &ring_reference, + NULL + ); + + if( error == 0 ) + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if + ( + ( + channel->other_state + != + xenidc_xbgt_channel_other_state_connected + ) + || + ( channel->ready_event_channel != event_channel ) + || + ( channel->ready_ring_reference != ring_reference ) + ) + { + channel->other_state = + xenidc_xbgt_channel_other_state_connected; + + channel->ready_event_channel = event_channel; + channel->ready_ring_reference = ring_reference; + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_oc ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + return; + } + } + + { + unsigned int ready; + + int error = xenbus_gather + ( + transaction, + watch->node, + "ready", "%u", &ready, + NULL + ); + + if( error == 0 ) + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if + ( + ( + channel->other_state + != + xenidc_xbgt_channel_other_state_ready + ) + ) + { + channel->other_state = + xenidc_xbgt_channel_other_state_ready; + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_or ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + return; + } + } + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + DISCONNECTED: + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if + ( + channel->other_state + != + xenidc_xbgt_channel_other_state_disconnected + ) + { + channel->other_state = + xenidc_xbgt_channel_other_state_disconnected; + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_od ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_protocol_error + ( xenidc_gnttab_channel * gnttab_channel ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = + xenidc_xbgt_channel_gnttab_channel_to( gnttab_channel ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_pe ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_xbgt_channel_disconnect + ( xenidc_xbgt_channel * channel, xenidc_callback * callback ) +{ + trace(); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + channel->disconnect_callback = callback; + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_dn ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +void xenidc_xbgt_channel_exit( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_xbgt_channel_init_or_exit( channel, 1 ); +} + +static void xenidc_xbgt_channel_register_watch_1( void * data ); +static void xenidc_xbgt_channel_unregister_watch_1( void * data ); +static void xenidc_xbgt_channel_clear_store_1( void * data ); +static void xenidc_xbgt_channel_write_ready_1( void * data ); +static void xenidc_xbgt_channel_write_connected_1( void * data ); +static void xenidc_xbgt_channel_phase_one_connect_1( void * data ); +static void xenidc_xbgt_channel_phase_one_connect_2 + ( xenidc_callback * callback ); +static void xenidc_xbgt_channel_phase_two_connect_1( void * data ); +static void xenidc_xbgt_channel_phase_two_connect_2 + ( xenidc_callback * callback ); +static void xenidc_xbgt_channel_phase_two_disconnect_1( void * data ); +static void xenidc_xbgt_channel_phase_two_disconnect_2 + ( xenidc_callback * callback ); +static void xenidc_xbgt_channel_phase_one_disconnect_1( void * data ); +static void xenidc_xbgt_channel_phase_one_disconnect_2 + ( xenidc_callback * callback ); + +static int xenidc_xbgt_channel_init_or_exit + ( xenidc_xbgt_channel * channel, int exit ) +{ + trace(); + + { + int return_value = 0; + + if( exit ) + { + goto EXIT; + } + + if + ( + ( + return_value = xenidc_gnttab_channel_init + ( &channel->channel, xenidc_xbgt_channel_protocol_error ) + ) + != + 0 + ) + { + goto EXIT_NO_GNTTAB_CHANNEL; + } + + xenidc_work_init + ( + &channel->register_watch_1_work, + xenidc_xbgt_channel_register_watch_1, + channel + ); + + xenidc_work_init + ( + &channel->unregister_watch_1_work, + xenidc_xbgt_channel_unregister_watch_1, + channel + ); + + xenidc_work_init + ( + &channel->clear_store_1_work, + xenidc_xbgt_channel_clear_store_1, + channel + ); + + xenidc_work_init + ( + &channel->write_ready_1_work, + xenidc_xbgt_channel_write_ready_1, + channel + ); + + xenidc_work_init + ( + &channel->write_connected_1_work, + xenidc_xbgt_channel_write_connected_1, + channel + ); + + xenidc_work_init + ( + &channel->phase_one_connect_1_work, + xenidc_xbgt_channel_phase_one_connect_1, + channel + ); + + xenidc_work_init + ( + &channel->phase_two_connect_1_work, + xenidc_xbgt_channel_phase_two_connect_1, + channel + ); + + xenidc_work_init + ( + &channel->phase_two_disconnect_1_work, + xenidc_xbgt_channel_phase_two_disconnect_1, + channel + ); + + xenidc_work_init + ( + &channel->phase_one_disconnect_1_work, + xenidc_xbgt_channel_phase_one_disconnect_1, + channel + ); + + xenidc_callback_init + ( + &channel->phase_one_connect_request.callback, + xenidc_xbgt_channel_phase_one_connect_2 + ); + + xenidc_callback_init + ( + &channel->phase_two_connect_request.callback, + xenidc_xbgt_channel_phase_two_connect_2 + ); + + xenidc_callback_init + ( + &channel->phase_two_disconnect_callback, + xenidc_xbgt_channel_phase_two_disconnect_2 + ); + + xenidc_callback_init + ( + &channel->phase_one_disconnect_callback, + xenidc_xbgt_channel_phase_one_disconnect_2 + ); + + return 0; + + EXIT: + + xenidc_gnttab_channel_exit( &channel->channel ); + + EXIT_NO_GNTTAB_CHANNEL: + + return return_value; + } +} + +static void xenidc_xbgt_channel_invalid_stimulus + ( xenidc_xbgt_channel * channel, xenidc_xbgt_channel_stimulus stimulus ); + +static void xenidc_xbgt_channel_test_other_state + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_register_watch + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_unregister_watch + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_clear_store + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_write_ready + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_write_connected + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_phase_one_connect + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_phase_two_connect + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_phase_two_disconnect + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_phase_one_disconnect + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_complete_disconnect + ( xenidc_xbgt_channel * channel ); + +static void xenidc_xbgt_channel_handle_stimulus + ( xenidc_xbgt_channel * channel, xenidc_xbgt_channel_stimulus stimulus ) +{ + trace3 + ( + "xbgt channel %p in state %d received stimulus %d", + channel, + channel->state, + stimulus + ); + + switch( channel->state ) + { + case xenidc_xbgt_channel_state_i: + /* Interface disconnected. */ + /* Gnttab channel disconnected. */ + /* Local store unknown. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_cn: + channel->state = xenidc_xbgt_channel_state_i_cn; + xenidc_xbgt_channel_phase_one_connect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn: + /* Interface connected. */ + /* Gnttab channel phase one connecting. */ + /* Local store unknown. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_dn; + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs; + xenidc_xbgt_channel_register_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_dn: + /* Interface disconnecting. */ + /* Gnttab channel phase one connecting */ + /* Local store unknown. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs: + /* Interface connected. */ + /* Gnttab channel phase one connected and registering watch or */ + /* Watch registered and gnttab channel phase two disconnecting */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + break; + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rf; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_dn_rs: + /* Interface disconnecting. */ + /* Gnttab channel phase one connected */ + /* Clearing store. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_rs: + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs_rs; + xenidc_xbgt_channel_phase_one_disconnect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_dn: + /* Interface disconnecting. */ + /* Gnttab channel phase one connected. */ + /* Local store unknown. */ + /* Registering watch. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Clearing Store. */ + /* Watch Registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rs; + xenidc_xbgt_channel_test_other_state( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rf; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rf: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Clearing store. */ + /* Something failed. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs; + break; + case xenidc_xbgt_channel_stimulus_rs: + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rf_rs; + xenidc_xbgt_channel_phase_one_disconnect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_dn_rs_rs: + /* Interface disconnecting. */ + /* Gnttab channel phase one disconnecting */ + /* Attempted to clear store. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i; + xenidc_xbgt_channel_complete_disconnect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_dn_rs: + /* Interface disconnecting. */ + /* Gnttab channel phase one connected. */ + /* Local store unknown. */ + /* Unregistering watch. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_dn: + /* Interface disconnecting. */ + /* Gnttab channel phase one connected unless ph2 disconnecting below.*/ + /* Clearing Store / writing ready / writing connected / ... */ + /* ... ph2 disconnecting */ + /* Watch Registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Store clear. */ + /* Watch Registered. */ + /* Testing other state / other state unknown/connected */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + case xenidc_xbgt_channel_stimulus_ou: + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od; + xenidc_xbgt_channel_write_ready( channel ); + break; + case xenidc_xbgt_channel_stimulus_oc: + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rf: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Unregistering watch. */ + /* Something failed. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rf; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rf_rs: + /* Interface connected. */ + /* Gnttab channel phase one disconnecting. */ + /* Attempted to clear store. */ + /* Something failed. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_dn_rs_rs; + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rf_rs_rs; + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Writing Ready. */ + /* Watch Registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs; + xenidc_xbgt_channel_test_other_state( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rf; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rf_rs_rs: + /* Interface connected. */ + /* Gnttab channel phase one disconnected. */ + /* Attempted to clear store. */ + /* Something failed. */ + /* Watch not registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i; + xenidc_xbgt_channel_complete_disconnect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Ready. */ + /* Watch registered. */ + /* Testing other state or other state disconnected */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + case xenidc_xbgt_channel_stimulus_od: + break; + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or; + xenidc_xbgt_channel_write_connected( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Writing Connected. */ + /* Watch registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs; + xenidc_xbgt_channel_test_other_state( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs_rf; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs: + /* Interface connected. */ + /* Gnttab channel phase one connected. */ + /* Connected. */ + /* Watch registered. */ + /* Testing other state / other state ready. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_dn: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + case xenidc_xbgt_channel_stimulus_od: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + case xenidc_xbgt_channel_stimulus_or: + break; + case xenidc_xbgt_channel_stimulus_oc: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc; + xenidc_xbgt_channel_phase_two_connect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc: + /* Interface connected. */ + /* Gnttab channel phase two connecting. */ + /* Connected. */ + /* Watch registered. */ + /* Other side connected. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + /* Phase two disconnect then go around. */ + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_pe; + break; + case xenidc_xbgt_channel_stimulus_dn: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_pe; + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_rs; + break; + case xenidc_xbgt_channel_stimulus_rf: + /* Maybe we picked up stale state from the store. Go around. */ + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_pe: + /* Interface connected. */ + /* Gnttab channel phase two connecting. */ + /* Connected. */ + /* Watch registered. */ + /* Protocol error or glitch. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + break; + case xenidc_xbgt_channel_stimulus_dn: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_dn; + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs; + xenidc_xbgt_channel_phase_two_disconnect( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_rs; + xenidc_xbgt_channel_clear_store( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_dn: + /* Interface disconnecting. */ + /* Gnttab channel phase two connecting. */ + /* Connected. */ + /* Watch registered. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + break; + case xenidc_xbgt_channel_stimulus_rs: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_dn; + xenidc_xbgt_channel_phase_two_disconnect( channel ); + break; + case xenidc_xbgt_channel_stimulus_rf: + channel->state = xenidc_xbgt_channel_state_i_cn_rs_dn_rs; + xenidc_xbgt_channel_unregister_watch( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + case xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_rs: + /* Interface connected. */ + /* Gnttab channel phase two connected. */ + /* Connected. */ + /* Watch registered. */ + /* Other side connected. */ + /* Totally happy. */ + switch( stimulus ) + { + case xenidc_xbgt_channel_stimulus_pe: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs; + xenidc_xbgt_channel_phase_two_disconnect( channel ); + break; + case xenidc_xbgt_channel_stimulus_dn: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs_rs_dn; + xenidc_xbgt_channel_phase_two_disconnect( channel ); + break; + case xenidc_xbgt_channel_stimulus_od: + case xenidc_xbgt_channel_stimulus_or: + case xenidc_xbgt_channel_stimulus_oc: + channel->state = + xenidc_xbgt_channel_state_i_cn_rs; + xenidc_xbgt_channel_phase_two_disconnect( channel ); + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } + break; + default: + xenidc_xbgt_channel_invalid_stimulus( channel, stimulus ); + break; + } +} + +static void xenidc_xbgt_channel_invalid_stimulus + ( xenidc_xbgt_channel * channel, xenidc_xbgt_channel_stimulus stimulus ) +{ + trace(); + + printk + ( + KERN_ERR "xenidc: xbgt channel %p in state %d" + "received invalid stimulus %d", + channel, + channel->state, + stimulus + ); +} + +static void xenidc_xbgt_channel_test_other_state + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + switch( channel->other_state ) + { + case xenidc_xbgt_channel_other_state_unknown: + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_ou ); + break; + case xenidc_xbgt_channel_other_state_disconnected: + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_od ); + break; + case xenidc_xbgt_channel_other_state_ready: + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_or ); + break; + case xenidc_xbgt_channel_other_state_connected: + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_oc ); + break; + } +} + +static void xenidc_xbgt_channel_register_watch + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + channel->other_state = xenidc_xbgt_channel_other_state_unknown; + + (void)xenidc_work_schedule( &channel->register_watch_1_work ); +} + +static void xenidc_xbgt_channel_register_watch_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + channel->watch.node = channel->remote_path; + channel->watch.callback = xenidc_xbgt_channel_watch; + + { + int return_value = register_xenbus_watch( &channel->watch ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if( return_value == 0 ) + { + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + } + else + { + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rf ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_unregister_watch + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->unregister_watch_1_work ); +} + +static void xenidc_xbgt_channel_unregister_watch_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + unregister_xenbus_watch( &channel->watch ); + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_clear_store( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->clear_store_1_work ); +} + +static void xenidc_xbgt_channel_clear_store_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + struct xenbus_transaction * transaction; + + AGAIN: + + transaction = xenbus_transaction_start(); + + if( IS_ERR( transaction ) ) + { + trace0( "error starting transaction" ); + + goto ERROR; + } + + { + int error = + xenbus_rm( transaction, channel->local_path, "ready" ); + + if( error ) + { + trace0( "error removing ready field from store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = + xenbus_rm( transaction, channel->local_path, "event-channel" ); + + if( error ) + { + trace0( "error removing event-channel field from store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = + xenbus_rm( transaction, channel->local_path, "ring-reference" ); + + if( error ) + { + trace0( "error removing ring-reference field from store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = xenbus_transaction_end( transaction, 0 /* commit */ ); + + if( error != 0 ) + { + if( error == -EAGAIN ) + { + goto AGAIN; + } + else + { + trace0( "error committing transaction" ); + + goto ERROR; + } + } + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return; + + ABORT_TRANSACTION: + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + ERROR: + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rf ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_write_ready( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->write_ready_1_work ); +} + +static void xenidc_xbgt_channel_write_ready_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + struct xenbus_transaction * transaction; + + AGAIN: + + transaction = xenbus_transaction_start(); + + if( IS_ERR( transaction ) ) + { + trace0( "error starting transaction" ); + + goto ERROR; + } + + { + int error = xenbus_write + ( + transaction, + channel->local_path, + "ready", + "1" + ); + + if( error ) + { + trace0( "error writing ready to store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = xenbus_transaction_end( transaction, 0 /* commit */ ); + + if( error != 0 ) + { + if( error == -EAGAIN ) + { + goto AGAIN; + } + else + { + trace0( "error committing transaction" ); + + goto ERROR; + } + } + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return; + + ABORT_TRANSACTION: + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + ERROR: + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rf ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_write_connected + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->write_connected_1_work ); +} + +static void xenidc_xbgt_channel_write_connected_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + struct xenbus_transaction * transaction; + + xenidc_gnttab_channel_reset_ring( &channel->channel ); + + AGAIN: + + transaction = xenbus_transaction_start(); + + if( IS_ERR( transaction ) ) + { + trace0( "error starting transaction" ); + + goto ERROR; + } + + { + int error = xenbus_printf + ( + transaction, + channel->local_path, + "ring-reference", + "%u", + channel->phase_one_connect_request.send_ring_ref + ); + + if( error ) + { + trace0( "error writing ring-reference to store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = xenbus_printf + ( + transaction, + channel->local_path, + "event-channel", + "%u", + channel->phase_one_connect_request.send_event_channel + ); + + if( error ) + { + trace0( "error writing event-channel to store" ); + + goto ABORT_TRANSACTION; + } + } + + { + int error = xenbus_transaction_end( transaction, 0 /* commit */ ); + + if( error != 0 ) + { + if( error == -EAGAIN ) + { + goto AGAIN; + } + else + { + trace0( "error committing transaction" ); + + goto ERROR; + } + } + } + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + + return; + + ABORT_TRANSACTION: + + xenbus_transaction_end( transaction, 1 /* abort */ ); + + ERROR: + + { + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rf ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } + } +} + +static void xenidc_xbgt_channel_phase_one_connect + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + channel->phase_one_connect_request.remote_domain_id = + channel->remote_domain_id; + + (void)xenidc_work_schedule( &channel->phase_one_connect_1_work ); +} + +static void xenidc_xbgt_channel_phase_one_connect_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + xenidc_gnttab_channel_phase_one_connect + ( &channel->channel, &channel->phase_one_connect_request ); + } +} + +static void xenidc_xbgt_channel_phase_one_connect_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = container_of + ( + xenidc_gnttab_channel_phase_one_connect_request_callback_to + ( callback ), + xenidc_xbgt_channel, + phase_one_connect_request + ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_xbgt_channel_phase_two_connect + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + channel->phase_two_connect_request.remote_domain_id = + channel->remote_domain_id; + channel->phase_two_connect_request.recv_ring_ref = + channel->ready_ring_reference; + channel->phase_two_connect_request.recv_event_channel = + channel->ready_event_channel; + + (void)xenidc_work_schedule( &channel->phase_two_connect_1_work ); +} + +static void xenidc_xbgt_channel_phase_two_connect_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + xenidc_gnttab_channel_phase_two_connect + ( &channel->channel, &channel->phase_two_connect_request ); + } +} + +static void xenidc_xbgt_channel_phase_two_connect_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = container_of + ( + xenidc_gnttab_channel_phase_two_connect_request_callback_to + ( callback ), + xenidc_xbgt_channel, + phase_two_connect_request + ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + if( xenidc_callback_query_error( callback ) == XENIDC_ERROR_SUCCESS ) + { + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + } + else + { + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rf ); + } + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_xbgt_channel_phase_two_disconnect + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->phase_two_disconnect_1_work ); +} + +static void xenidc_xbgt_channel_phase_two_disconnect_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + xenidc_gnttab_channel_phase_two_disconnect + ( &channel->channel, &channel->phase_two_disconnect_callback ); + } +} + +static void xenidc_xbgt_channel_phase_two_disconnect_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = container_of + ( callback, xenidc_xbgt_channel, phase_two_disconnect_callback ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_xbgt_channel_phase_one_disconnect + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + (void)xenidc_work_schedule( &channel->phase_one_disconnect_1_work ); +} + +static void xenidc_xbgt_channel_phase_one_disconnect_1( void * data ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = (xenidc_xbgt_channel *)data; + + xenidc_gnttab_channel_phase_one_disconnect + ( &channel->channel, &channel->phase_one_disconnect_callback ); + } +} + +static void xenidc_xbgt_channel_phase_one_disconnect_2 + ( xenidc_callback * callback ) +{ + trace(); + + { + xenidc_xbgt_channel * channel = container_of + ( callback, xenidc_xbgt_channel, phase_one_disconnect_callback ); + + unsigned long flags; + + spin_lock_irqsave( &channel->lock, flags ); + + xenidc_xbgt_channel_handle_stimulus + ( channel, xenidc_xbgt_channel_stimulus_rs ); + + spin_unlock_irqrestore( &channel->lock, flags ); + } +} + +static void xenidc_xbgt_channel_complete_disconnect + ( xenidc_xbgt_channel * channel ) +{ + trace(); + + xenidc_callback_success( channel->disconnect_callback ); +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_xbgt_channel_enumeration.dot --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/drivers/xen/xenidc/xenidc_xbgt_channel_enumeration.dot Sun Oct 30 16:03:34 2005 @@ -0,0 +1,109 @@ +digraph enumeration { +size="7,7" + +i[style=filled,fillcolor=green] +i->i_cn[label="cn\nphase_one_connect"]; + +i_cn[style=filled,fillcolor=green] +i_cn->i_cn_dn[label="dn"]; +i_cn->i_cn_rs[label="rs\nregister_watch"]; + +i_cn_dn[style=filled,fillcolor=orange] +i_cn_dn->i_cn_dn_rs[label="rs\nclear_store"]; + +i_cn_rs[style=filled,fillcolor=green] +i_cn_rs->i_cn_rs_dn[label="dn"]; +i_cn_rs->i_cn_rs[label="pe/od/or/oc"]; +i_cn_rs->i_cn_rs_rs[label="rs\nclear_store"]; +i_cn_rs->i_cn_rs_rf[label="rf\nclear_store"]; + +i_cn_dn_rs[style=filled,fillcolor=orange] +i_cn_dn_rs->i_cn_dn_rs_rs[label="rs/rf\nphase_one_disconnect"]; + +i_cn_rs_dn[style=filled,fillcolor=orange] +i_cn_rs_dn->i_cn_rs_dn[label="od/or/oc"]; +i_cn_rs_dn->i_cn_rs_dn_rs[label="rs\nunregister_watch"]; +i_cn_rs_dn->i_cn_dn_rs[label="rf\nclear_store"]; + +i_cn_rs_rs[style=filled,fillcolor=green] +i_cn_rs_rs->i_cn_rs_rs_dn[label="dn"]; +i_cn_rs_rs->i_cn_rs_rs[label="od/or/oc"]; +i_cn_rs_rs->i_cn_rs_rs_rs[label="rs\ntest_other_state"]; +i_cn_rs_rs->i_cn_rs_rs_rf[label="rf\nunregister_watch"]; + +i_cn_rs_rf[style=filled,fillcolor=red] +i_cn_rs_rf->i_cn_dn_rs[label="dn"]; +i_cn_rs_rf->i_cn_rs_rf_rs[label="rs/rf\nphase_one_disconnect"]; + +i_cn_dn_rs_rs[style=filled,fillcolor=orange] +i_cn_dn_rs_rs->i[label="rs\ncomplete_disconnect"]; + +i_cn_rs_dn_rs[style=filled,fillcolor=orange] +i_cn_rs_dn_rs->i_cn_rs_dn_rs[label="od/or/oc"]; +i_cn_rs_dn_rs->i_cn_dn_rs[label="rs\nclear_store"]; + +i_cn_rs_rs_dn[style=filled,fillcolor=orange] +i_cn_rs_rs_dn->i_cn_rs_rs_dn[label="od/or/oc"]; +i_cn_rs_rs_dn->i_cn_rs_dn_rs[label="rs/rf\nunregister_watch"]; + +i_cn_rs_rs_rs[style=filled,fillcolor=green] +i_cn_rs_rs_rs->i_cn_rs_dn_rs[label="dn\nunregister_watch"]; +i_cn_rs_rs_rs->i_cn_rs_rs_rs[label="ou/oc"]; +i_cn_rs_rs_rs->i_cn_rs_rs_rs_od[label="od/or\nwrite_ready"]; + +i_cn_rs_rs_rf[style=filled,fillcolor=red] +i_cn_rs_rs_rf->i_cn_rs_dn_rs[label="dn"]; +i_cn_rs_rs_rf->i_cn_rs_rs_rf[label="od/or/oc"]; +i_cn_rs_rs_rf->i_cn_rs_rf[label="rs\nclear_store"]; + +i_cn_rs_rf_rs[style=filled,fillcolor=red] +i_cn_rs_rf_rs->i_cn_dn_rs_rs[label="dn"]; +i_cn_rs_rf_rs->i_cn_rs_rf_rs_rs[label="rs"]; + +i_cn_rs_rs_rs_od[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od->i_cn_rs_rs_dn[label="dn"]; +i_cn_rs_rs_rs_od->i_cn_rs_rs_rs_od[label="od/or/oc"]; +i_cn_rs_rs_rs_od->i_cn_rs_rs_rs_od_rs[label="rs\ntest_other_state"]; +i_cn_rs_rs_rs_od->i_cn_rs_rs_rf[label="rf\nunregister_watch"]; + +i_cn_rs_rf_rs_rs[style=filled,fillcolor=red] +i_cn_rs_rf_rs_rs->i[label="dn\ncomplete_disconnect"]; + +i_cn_rs_rs_rs_od_rs[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od_rs->i_cn_rs_dn_rs[label="dn\nunregister_watch"]; +i_cn_rs_rs_rs_od_rs->i_cn_rs_rs_rs_od_rs[label="od"]; +i_cn_rs_rs_rs_od_rs->i_cn_rs_rs_rs_od_rs_or[label="or/oc\nwrite_connected"]; + +i_cn_rs_rs_rs_od_rs_or[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od_rs_or->i_cn_rs_rs_dn[label="dn"]; +i_cn_rs_rs_rs_od_rs_or->i_cn_rs_rs_rs_od_rs_or[label="od/or/oc"]; +i_cn_rs_rs_rs_od_rs_or->i_cn_rs_rs_rs_od_rs_or_rs[label="rs\ntest_other_state"]; +i_cn_rs_rs_rs_od_rs_or->i_cn_rs_rs_rf[label="rf\nunregister_watch"]; + +i_cn_rs_rs_rs_od_rs_or_rs[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od_rs_or_rs->i_cn_rs_dn_rs[label="dn\nunregister_watch"]; +i_cn_rs_rs_rs_od_rs_or_rs->i_cn_rs_rs[label="od\nclear_store"]; +i_cn_rs_rs_rs_od_rs_or_rs->i_cn_rs_rs_rs_od_rs_or_rs[label="or"]; +i_cn_rs_rs_rs_od_rs_or_rs->i_cn_rs_rs_rs_od_rs_or_rs_oc[label="oc\nphase_two_connect"]; + +i_cn_rs_rs_rs_od_rs_or_rs_oc[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od_rs_or_rs_oc->i_cn_rs_rs_rs_od_rs_or_rs_oc_pe[label="pe/od/or/oc"]; +i_cn_rs_rs_rs_od_rs_or_rs_oc->i_cn_rs_rs_rs_od_rs_or_rs_oc_dn[label="dn"]; +i_cn_rs_rs_rs_od_rs_or_rs_oc->i_cn_rs_rs_rs_od_rs_or_rs_oc_rs[label="rs"]; +i_cn_rs_rs_rs_od_rs_or_rs_oc->i_cn_rs_rs[label="rf\nclear_store"]; + +i_cn_rs_rs_rs_od_rs_or_rs_oc_pe[style=filled,fillcolor=blue] +i_cn_rs_rs_rs_od_rs_or_rs_oc_pe->i_cn_rs_rs_rs_od_rs_or_rs_oc_pe[label="pe/od/or/oc"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_pe->i_cn_rs_rs_rs_od_rs_or_rs_oc_dn[label="dn"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_pe->i_cn_rs[label="rs\nphase_two_disconnect"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_pe->i_cn_rs_rs[label="rf\nclear_store"] + +i_cn_rs_rs_rs_od_rs_or_rs_oc_dn[style=filled,fillcolor=orange] +i_cn_rs_rs_rs_od_rs_or_rs_oc_dn->i_cn_rs_rs_rs_od_rs_or_rs_oc_dn[label="pe/od/or/oc"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_dn->i_cn_rs_rs_dn[label="rs\nphase_two_disconnect"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_dn->i_cn_rs_dn_rs[label="rf\nunregister_watch"] + +i_cn_rs_rs_rs_od_rs_or_rs_oc_rs[style=filled,fillcolor=green] +i_cn_rs_rs_rs_od_rs_or_rs_oc_rs->i_cn_rs[label="pe/od/or/oc\nphase_two_disconnect"] +i_cn_rs_rs_rs_od_rs_or_rs_oc_rs->i_cn_rs_rs_dn[label="dn\nphase_two_disconnect"] +} diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,122 @@ +/*****************************************************************************/ +/* Xen inter-domain communication API. */ +/* */ +/* 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 __ASM_XEN_XENIDC_H__ +#define __ASM_XEN_XENIDC_H__ + +/*****************************************************************************/ +/* RESOURCE MANAGEMENT */ +/* */ +/* The bulk data transfer mechanism uses the following strategy for resource */ +/* management: */ +/* */ +/* 1) Create an anti-deadlock resource pool. This pool of resources is used */ +/* to guarantee that your subsystem can make progress independent of all */ +/* other subsystems using the xenidc API. The subsystem operations using */ +/* the resource pool must be independent and the resource pool must contain */ +/* enough resources to service the worst case individual operation. This */ +/* guarantees progress because, in the worst case, an operation can wait for */ +/* all other operations to complete (they are independent so they will */ +/* complete eventually) at which time the resource pool will be replenished */ +/* such that it contains enough resources to start the waiting operation. */ +/* */ +/* 2) Make a single atomic request to reserve and create/reserve and open */ +/* all of the local/remote buffers required for the operation to proceed to */ +/* completion. This request is guaranteed to complete because it is atomic */ +/* and so will eventually get a chance to acquire all the resources it needs */ +/* which by design will be less than the anti-deadlock reservation. */ +/* */ +/* 3) Dependent operations using independent anti-deadlock pools acquire */ +/* resources from pools in a defined order. */ +/* */ +/* An alternative implementation could support an additional API for the */ +/* following strategy which might be more convenient for some clients: */ +/* */ +/* 1) Create an anti-deadlock resource pool as above. */ +/* */ +/* 2) Make a single atomic reservation to reserve all the resources required */ +/* for an operation to proceed to completion. This request is guaranteed to */ +/* complete because it is atomic and so will eventually get a chance to */ +/* acquire all the resources it needs which by design will be less than the */ +/* anti-deadlock reservation. */ +/* */ +/* 3) Make an arbitrary number of create/open requests using the resources */ +/* reserved in step 2. These are guaranteed to complete because the */ +/* resources reserved in step 2 are sufficient by design. */ +/* */ +/* 4) Dependent operations using independent anti-deadlock pools acquire */ +/* resources from pools in a defined order. */ +/* */ +/* The following strategies are examples which in general DO NOT WORK: */ +/* */ +/* First incorrect strategy: */ +/* */ +/* 1) Have no anti-deadlock resource pool. */ +/* */ +/* This doesn't work because all the xenidc resources might be allocated to */ +/* an operation that is dependent on your subsystem for completion. A */ +/* deadlock results where your subsystem is waiting on xenidc which is */ +/* waiting on the client of your subsystem to free resources which is */ +/* waiting on your subsystem. */ +/* */ +/* Second incorrect strategy: */ +/* */ +/* 1) Create an anti-deadlock resource pool. */ +/* */ +/* 2) Make an arbitrary number of non-atomic reserve_and_create and */ +/* reserve_and_open requests for a single subsystem operation. */ +/* */ +/* In general, this doesn't work because your subsystem will be processing */ +/* multiple operations concurrently which means that the anti-deadlock */ +/* resources might get fully used by multiple concurrent operations in such */ +/* a way that none of the operations have enough resources to proceed so */ +/* they all end up waiting indefinitely on each other. */ +/* */ +/* Third incorrect strategy: */ +/* */ +/* 1) Create an anti-deadlock resource pool. */ +/* */ +/* 2) Use the same anti-deadlock pool for dependent operations. */ +/* */ +/* This doesn't work because an operation might acquire all the */ +/* anti-deadlock reservation and then initiate a second operation. The */ +/* second operation goes to the same anti-deadlock pool and waits forever */ +/* for the dependent operation to free the resources back. The dependent */ +/* operation never frees the resources because it is dependent on the second */ +/* operation. */ +/* */ +/* Fourth incorrect strategy: */ +/* */ +/* 1) Create anti-deadlock resource pools for different dependent */ +/* operations. */ +/* */ +/* 2) Different operations acquire resources from the pools in different */ +/* order. */ +/* */ +/* This doesn't work because of the potential for another cyclic deadlock. */ +/* */ +/*****************************************************************************/ + +#include "xenidc_endpoint.h" +#include "xenidc_rbr_provider_pool.h" +#include "xenidc_rbr_mapper_pool.h" + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_address.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_address.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,60 @@ +/*****************************************************************************/ +/* Xen inter-domain communication address. */ +/* */ +/* 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 __ASM_XEN_XENIDC_ADDRESS_H__ +#define __ASM_XEN_XENIDC_ADDRESS_H__ + +typedef struct xenidc_address_struct xenidc_address; + +struct xenidc_address_struct +{ + char * local; + char * remote; + int remote_id; +}; + +static inline void xenidc_address_init + ( xenidc_address * address, char * local, char * remote, int remote_id ) +{ + address->local = local; + address->remote = remote; + address->remote_id = remote_id; +} + +static inline char * xenidc_address_query_local_domain + ( xenidc_address * address ) +{ + return address->local; +} + +static inline char * xenidc_address_query_remote_domain + ( xenidc_address * address ) +{ + return address->remote; +} + +static inline int xenidc_address_query_remote_domain_id + ( xenidc_address * address ) +{ + return address->remote_id; +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_callback.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_callback.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,166 @@ +/*****************************************************************************/ +/* 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 XENIDC_CALLBACK_H +#define XENIDC_CALLBACK_H + +#include +#include "xenidc_error.h" +#include "xenidc_work.h" + +/* Service parameter blocks contain callbacks for asynchronous completions. */ + +typedef struct xenidc_callback_struct xenidc_callback; + +struct xenidc_callback_struct +{ + xenidc_work work; + xenidc_error error; +}; + +#define XENIDC_CALLBACK_LINK work.XENIDC_WORK_LINK + +/* Client of service initialises callback with its callback function. */ + +typedef void ( xenidc_callback_function )( xenidc_callback * callback ); + +static inline void xenidc_callback_init + ( xenidc_callback * callback, xenidc_callback_function * function ) +{ + xenidc_work_init( &callback->work, (void(*)(void*))function, callback ); + + callback->error = XENIDC_ERROR_SUCCESS; +} + +/* Client may use link whilst it owns parameter block. Service may use link */ +/* whilst it owns parameter block. Link is reserved whilst callback is */ +/* scheduled for completion. */ + +static inline struct list_head * xenidc_callback_to_link + ( xenidc_callback * callback ) +{ + return xenidc_work_to_link( &callback->work ); +} + +static inline xenidc_callback * xenidc_callback_link_to + ( struct list_head * link ) +{ + return container_of + ( xenidc_work_link_to( link ), xenidc_callback, work ); +} + +/* Service which completes requests concurrently may call */ +/* xenidc_callback_complete or xenidc_callback_success to complete the */ +/* callback. */ + +static inline void xenidc_callback_complete + ( xenidc_callback * callback, xenidc_error error ) +{ + callback->error = error; + + xenidc_work_schedule( &callback->work ); +} + +static inline void xenidc_callback_success + ( xenidc_callback * callback ) +{ + xenidc_callback_complete( callback, 0 ); +} + +/* These functions used by serialiser below. */ + +static inline void xenidc_callback_set_error + ( xenidc_callback * callback, xenidc_error error ) +{ + callback->error = error; +} + +static inline void xenidc_callback_complete_synchronously + ( xenidc_callback * callback ) +{ + xenidc_work_perform_synchronously( &callback->work ); +} + +/* When callback completes, client may call xenidc_callback_query_error to */ +/* get the error code. */ + +static inline xenidc_error xenidc_callback_query_error + ( xenidc_callback * callback ) +{ + return callback->error; +} + +/* Services which must serialise completions to preserve submission order */ +/* can use one of these. */ + +typedef struct xenidc_callback_serialiser_struct xenidc_callback_serialiser; + +struct xenidc_callback_serialiser_struct +{ + spinlock_t lock; + struct list_head list; + xenidc_work work; + int running; +}; + +void xenidc_callback_serialiser_function( void * context ); + +#define XENIDC_CALLBACK_SERIALISER_INIT( name ) \ +{ \ + SPIN_LOCK_UNLOCKED, \ + LIST_HEAD_INIT( name.list ), \ + XENIDC_WORK_INIT \ + ( name.work, xenidc_callback_serialiser_function, &name ), \ + 0 \ +} + +#define XENIDC_CALLBACK_SERIALISER( name ) \ +xenidc_callback_serialiser name = \ + XENIDC_CALLBACK_SERIALISER_INIT( name ) + +/* The service completes the callback to the serialiser which serialises the */ +/* completions to the client, performing them in submission order. */ + +static inline void xenidc_callback_serialiser_complete_callback +( + xenidc_callback_serialiser * serialiser, + xenidc_callback * callback, + xenidc_error error +) +{ + xenidc_callback_set_error( callback, error ); + + { + unsigned long flags; + + spin_lock_irqsave( &serialiser->lock, flags ); + + list_add_tail + ( xenidc_callback_to_link( callback ), &serialiser->list ); + + spin_unlock_irqrestore( &serialiser->lock, flags ); + } + + xenidc_work_schedule( &serialiser->work ); +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_channel.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_channel.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,146 @@ +/*****************************************************************************/ +/* Xen inter-domain communication channel abstract base class. */ +/* */ +/* 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 _XENIDC_CHANNEL_H +#define _XENIDC_CHANNEL_H + +#include +#include + +typedef struct xenidc_channel_message_struct xenidc_channel_message; + +struct xenidc_channel_message_struct +{ + xenidc_callback callback; + xenidc_local_buffer_reference message_lbr; +}; + +#define XENIDC_CHANNEL_MESSAGE_LINK callback.XENIDC_CALLBACK_LINK + +static inline struct list_head * xenidc_channel_message_to_link + ( xenidc_channel_message * message ) +{ + return &message->XENIDC_CHANNEL_MESSAGE_LINK; +} + +static inline xenidc_callback * xenidc_channel_message_to_callback + ( xenidc_channel_message * message ) +{ + return &message->callback; +} + +static inline xenidc_channel_message * xenidc_channel_message_callback_to + ( xenidc_callback * callback ) +{ + return container_of( callback, xenidc_channel_message, callback ); +} + +static inline void xenidc_channel_message_init + ( xenidc_channel_message * message, xenidc_callback_function * callback ) +{ + xenidc_callback_init + ( xenidc_channel_message_to_callback( message ), callback ); +} + +static inline void xenidc_channel_message_set_message_lbr + ( xenidc_channel_message * message, xenidc_local_buffer_reference lbr ) +{ + message->message_lbr = lbr; +} + +typedef struct xenidc_channel_struct xenidc_channel; + +struct xenidc_channel_struct +{ + void ( * submit_message ) + ( xenidc_channel * channel, xenidc_channel_message * message ); + void * client_context; + void ( * connect )( void * client_context ); + void ( * handle_message ) + ( void * client_context, xenidc_channel_message * message ); + void ( * disconnect ) + ( void * client_context, xenidc_callback * callback ); +}; + +/* Called by a derived class to initialise the base class. */ + +static inline void xenidc_channel_init +( + xenidc_channel * channel, + void ( * submit_message ) + ( xenidc_channel * channel, xenidc_channel_message * message ) +) +{ + channel->submit_message = submit_message; +} + +/* Called by a derived class to notify the client. */ + +static inline void xenidc_channel_connect( xenidc_channel * channel ) +{ + channel->connect( channel->client_context ); +} + +/* Called by a derived class to notify the client. */ + +static inline void xenidc_channel_handle_message + ( xenidc_channel * channel, xenidc_channel_message * message ) +{ + channel->handle_message( channel->client_context, message ); +} + +/* Called by a derived class to notify the client. */ + +static inline void xenidc_channel_disconnect + ( xenidc_channel * channel, xenidc_callback * callback ) +{ + channel->disconnect( channel->client_context, callback ); +} + +/* Called by the client class before derived class first calls connect. */ + +static inline void xenidc_channel_install_client +( + xenidc_channel * channel, + void * client_context, + void ( * connect )( void * client_context ), + void ( * handle_message ) + ( void * client_context, xenidc_channel_message * message ), + void ( * disconnect ) + ( void * client_context, xenidc_callback * callback ) +) +{ + channel->client_context = client_context; + channel->connect = connect; + channel->handle_message = handle_message; + channel->disconnect = disconnect; +} + +/* Called by the client class between connect and disconnect callback to */ +/* submit a message. */ + +static inline void xenidc_channel_submit_message + ( xenidc_channel * channel, xenidc_channel_message * message ) +{ + channel->submit_message( channel, message ); +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_concatenate.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_concatenate.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,47 @@ +/*****************************************************************************/ +/* Support for concatenated local buffer references */ +/* */ +/* 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 XENIDC_CONCATENATE_H +#define XENIDC_CONCATENATE_H + +#include "xenidc.h" + +typedef struct xenidc_concatenate_base_struct xenidc_concatenate_base; + +struct xenidc_concatenate_base_struct +{ + xenidc_local_buffer_reference * head; + xenidc_local_buffer_reference * tail; +}; + +/* To create a concatenated lbr, you need a base resource which will persist */ +/* for the lifetime of the created lbr. Pass a pointer to this resource and */ +/* pointers to the underlying lbrs to concatenate. These must also persist */ +/* for the lifetime of the created lbr. */ + +extern xenidc_local_buffer_reference xenidc_concatenate_create_lbr +( + xenidc_concatenate_base * base, + xenidc_local_buffer_reference * head, + xenidc_local_buffer_reference * tail +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_endpoint.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_endpoint.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,203 @@ +/*****************************************************************************/ +/* Xen inter-domain communication endpoint. */ +/* */ +/* 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 __ASM_XEN_XENIDC_ENDPOINT_H__ +#define __ASM_XEN_XENIDC_ENDPOINT_H__ + +#include "xenidc_address.h" +#include "xenidc_gateway.h" +#include "xenidc_xbgt_channel.h" + +typedef struct xenidc_gateway_message_struct xenidc_endpoint_message; + +#define XENIDC_ENDPOINT_MESSAGE_LINK XENIDC_GATEWAY_MESSAGE_LINK + +static inline xenidc_callback * xenidc_endpoint_message_to_callback + ( xenidc_endpoint_message * message ) +{ + return xenidc_gateway_message_to_callback( message ); +} + +static inline xenidc_endpoint_message * xenidc_endpoint_message_callback_to + ( xenidc_callback * callback ) +{ + return xenidc_gateway_message_callback_to( callback ); +} + +static inline struct list_head * xenidc_endpoint_message_to_link + ( xenidc_endpoint_message * message ) +{ + return xenidc_callback_to_link + ( xenidc_endpoint_message_to_callback( message ) ); +} + +static inline xenidc_endpoint_message * xenidc_endpoint_message_link_to + ( struct list_head * link ) +{ + return xenidc_endpoint_message_callback_to + ( xenidc_callback_link_to( link ) ); +} + +static inline void xenidc_endpoint_message_init + ( xenidc_endpoint_message * message, xenidc_callback_function * callback ) +{ + xenidc_gateway_message_init( message, callback ); +} + +static inline void xenidc_endpoint_message_set_message_lbr + ( xenidc_endpoint_message * message, xenidc_local_buffer_reference lbr ) +{ + message->message_lbr = lbr; +} + +static inline xenidc_local_buffer_reference * + xenidc_endpoint_message_to_message_lbr( xenidc_endpoint_message * message ) +{ + return &message->message_lbr; +} + +typedef struct xenidc_gateway_transaction_struct xenidc_endpoint_transaction; + +#define XENIDC_ENDPOINT_TRANSACTION_LINK XENIDC_GATEWAY_TRANSACTION_LINK + +static inline xenidc_callback * xenidc_endpoint_transaction_to_callback + ( xenidc_endpoint_transaction * transaction ) +{ + return xenidc_gateway_transaction_to_callback( transaction ); +} + +static inline xenidc_endpoint_transaction * + xenidc_endpoint_transaction_callback_to( xenidc_callback * callback ) +{ + return xenidc_gateway_transaction_callback_to( callback ); +} + +static inline struct list_head * xenidc_endpoint_transaction_to_link + ( xenidc_endpoint_transaction * transaction ) +{ + return xenidc_callback_to_link + ( xenidc_endpoint_transaction_to_callback( transaction ) ); +} + +static inline xenidc_endpoint_transaction * xenidc_endpoint_transaction_link_to + ( struct list_head * link ) +{ + return xenidc_endpoint_transaction_callback_to + ( xenidc_callback_link_to( link ) ); +} + +static inline void xenidc_endpoint_transaction_init +( + xenidc_endpoint_transaction * transaction, + xenidc_callback_function * callback +) +{ + xenidc_gateway_transaction_init( transaction, callback ); +} + +static inline void xenidc_endpoint_transaction_set_parameters_lbr +( + xenidc_endpoint_transaction * transaction, + xenidc_local_buffer_reference lbr +) +{ + transaction->parameters_lbr = lbr; +} + +static inline void xenidc_endpoint_transaction_set_status_lbr +( + xenidc_endpoint_transaction * transaction, + xenidc_local_buffer_reference lbr +) +{ + transaction->status_lbr = lbr; +} + +static inline xenidc_local_buffer_reference * + xenidc_endpoint_transaction_to_parameters_lbr + ( xenidc_endpoint_transaction * transaction ) +{ + return &transaction->parameters_lbr; +} + +static inline xenidc_local_buffer_reference * + xenidc_endpoint_transaction_to_status_lbr + ( xenidc_endpoint_transaction * transaction ) +{ + return &transaction->status_lbr; +} + +static inline void xenidc_endpoint_transaction_complete + ( xenidc_endpoint_transaction * transaction, xenidc_error error ) +{ + xenidc_callback_complete + ( xenidc_endpoint_transaction_to_callback( transaction ), error ); +} + +static inline xenidc_error xenidc_endpoint_transaction_query_error + ( xenidc_endpoint_transaction * transaction ) +{ + return xenidc_callback_query_error + ( xenidc_endpoint_transaction_to_callback( transaction ) ); +} + +typedef struct xenidc_endpoint_struct xenidc_endpoint; + +struct xenidc_endpoint_struct +{ + xenidc_gateway gateway; + xenidc_xbgt_channel channel; +}; + +extern int xenidc_endpoint_init +( + xenidc_endpoint * endpoint, + + void ( * connect )( xenidc_endpoint * endpoint ), + void ( * handle_message ) + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ), + void ( * handle_transaction ) + ( + xenidc_endpoint * endpoint, + xenidc_endpoint_transaction * transaction + ), + void ( * disconnect ) + ( xenidc_endpoint * endpoint, xenidc_callback * callback ), + u32 initiator_quota, + xenidc_buffer_byte_count initiator_maximum_byte_count, + u32 target_quota, + xenidc_buffer_byte_count target_maximum_byte_count +); + +extern void xenidc_endpoint_create + ( xenidc_endpoint * endpoint, xenidc_address address ); + +extern void xenidc_endpoint_submit_message + ( xenidc_endpoint * endpoint, xenidc_endpoint_message * message ); + +extern void xenidc_endpoint_submit_transaction + ( xenidc_endpoint * endpoint, xenidc_endpoint_transaction * transaction ); + +extern void xenidc_endpoint_destroy( xenidc_endpoint * endpoint ); + +extern void xenidc_endpoint_exit( xenidc_endpoint * endpoint ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_error.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_error.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,56 @@ +#ifndef __ASM_XEN_XENIDC_ERROR_H__ +#define __ASM_XEN_XENIDC_ERROR_H__ + +#include + +typedef u32 xenidc_error; + +/* The first few error numbers are reserved for success and transport errors */ +/* which are common to all IDC protocols. */ + +#define XENIDC_ERROR_SUCCESS ( (xenidc_error)0 ) +#define XENIDC_ERROR_ABORTED ( (xenidc_error)1 ) +#define XENIDC_ERROR_FAILURE ( (xenidc_error)2 ) +#define XENIDC_ERROR_DISCONNECT ( (xenidc_error)3 ) +/* Initiator did everything right, target software needs to be fixed: */ +#define XENIDC_ERROR_IMPLEMENTATION_DEFICIENCY ( (xenidc_error)4 ) +/* Unexpected transaction/transaction in wrong sequence etc: */ +#define XENIDC_ERROR_INVALID_PROTOCOL ( (xenidc_error)5 ) +/* Underlength parameters/status etc: */ +#define XENIDC_ERROR_INVALID_FORMAT ( (xenidc_error)6 ) +/* Parameter value wrong: */ +#define XENIDC_ERROR_INVALID_PARAMETER ( (xenidc_error)7 ) +/* Something about a request exceeded a hard-coded limit: */ +#define XENIDC_ERROR_TOO_BIG ( (xenidc_error)8 ) + +static inline xenidc_error xenidc_error_map_local_to( int error ) +{ + /* FIXME */ + switch( error ) + { + case 0: + return XENIDC_ERROR_SUCCESS; + default: + return XENIDC_ERROR_FAILURE; + } +} + +static inline int xenidc_error_map_to_local( xenidc_error error ) +{ + /* FIXME */ + switch( error ) + { + case XENIDC_ERROR_SUCCESS: + return 0; + default: + return -1; + } +} + +/* Protocols can define their own set of protocol specific errors starting */ +/* at this one. The driver code on either side must translate between the */ +/* local OS specific error codes and the protocol specific error codes. */ + +#define XENIDC_ERROR_PROTOCOL_SPECIFIC_FIRST ( (xenidc_error)256 ) + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_gateway.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_gateway.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,253 @@ +/*****************************************************************************/ +/* Xen inter-domain communication gateway class. This class uses the message */ +/* channel service provided by a xenidc_channel to implement a gateway */ +/* service which allows the client to send both messages and transactions */ +/* (consisting of parameters and status) between domains. */ +/* */ +/* 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 _XENIDC_GATEWAY_H +#define _XENIDC_GATEWAY_H + +#include +#include +#include + +typedef struct xenidc_gateway_initiator_resource_struct + xenidc_gateway_initiator_resource; + +typedef struct xenidc_gateway_target_resource_struct + xenidc_gateway_target_resource; + +typedef struct xenidc_gateway_message_and_transaction_header_struct + xenidc_gateway_message_and_transaction_header; + +struct xenidc_gateway_message_and_transaction_header_struct +{ + xenidc_callback callback; + int transaction_not_message; +}; + +#define XENIDC_GATEWAY_MESSAGE_AND_TRANSACTION_HEADER_LINK \ +callback.XENIDC_CALLBACK_LINK + +static inline struct list_head * + xenidc_gateway_message_and_transaction_header_to_link + ( xenidc_gateway_message_and_transaction_header * header ) +{ + return xenidc_callback_to_link( &header->callback ); +} + +static inline xenidc_callback * + xenidc_gateway_message_and_transaction_header_to_callback + ( xenidc_gateway_message_and_transaction_header * header ) +{ + return &header->callback; +} + +typedef struct xenidc_gateway_message_struct xenidc_gateway_message; + +struct xenidc_gateway_message_struct +{ + xenidc_gateway_message_and_transaction_header header; + xenidc_local_buffer_reference message_lbr; +}; + +static inline xenidc_callback * xenidc_gateway_message_to_callback + ( xenidc_gateway_message * message ) +{ + return &message->header.callback; +} + +static inline xenidc_gateway_message * xenidc_gateway_message_callback_to + ( xenidc_callback * callback ) +{ + return container_of( callback, xenidc_gateway_message, header.callback ); +} + +static inline struct list_head * xenidc_gateway_message_to_link + ( xenidc_gateway_message * message ) +{ + return xenidc_callback_to_link( &message->header.callback ); +} + +static inline xenidc_gateway_message * xenidc_gateway_message_link_to + ( struct list_head * link ) +{ + return xenidc_gateway_message_callback_to + ( xenidc_callback_link_to( link ) ); +} + +static inline xenidc_gateway_message * xenidc_gateway_message_header_to + ( xenidc_gateway_message_and_transaction_header * header ) +{ + return container_of( header, xenidc_gateway_message, header ); +} + +static inline void xenidc_gateway_message_init + ( xenidc_gateway_message * message, xenidc_callback_function * callback ) +{ + xenidc_callback_init + ( xenidc_gateway_message_to_callback( message ), callback ); + + message->header.transaction_not_message = 0; +} + +typedef struct xenidc_gateway_transaction_struct xenidc_gateway_transaction; + +struct xenidc_gateway_transaction_struct +{ + xenidc_gateway_message_and_transaction_header header; + xenidc_local_buffer_reference parameters_lbr; + xenidc_local_buffer_reference status_lbr; +}; + +#define XENIDC_GATEWAY_TRANSACTION_LINK header.callback.XENIDC_CALLBACK_LINK + +static inline xenidc_callback * xenidc_gateway_transaction_to_callback + ( xenidc_gateway_transaction * transaction ) +{ + return &transaction->header.callback; +} + +static inline xenidc_gateway_transaction * + xenidc_gateway_transaction_callback_to( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_gateway_transaction, header.callback ); +} + +static inline struct list_head * xenidc_gateway_transaction_to_link + ( xenidc_gateway_transaction * transaction ) +{ + return xenidc_callback_to_link + ( xenidc_gateway_transaction_to_callback( transaction ) ); +} + +static inline xenidc_gateway_transaction * xenidc_gateway_transaction_link_to + ( struct list_head * link ) +{ + return xenidc_gateway_transaction_callback_to + ( xenidc_callback_link_to( link ) ); +} + +static inline xenidc_gateway_transaction * xenidc_gateway_transaction_header_to + ( xenidc_gateway_message_and_transaction_header * header ) +{ + return container_of( header, xenidc_gateway_transaction, header ); +} + +static inline void xenidc_gateway_transaction_init +( + xenidc_gateway_transaction * transaction, + xenidc_callback_function * callback +) +{ + xenidc_callback_init + ( xenidc_gateway_transaction_to_callback( transaction ), callback ); + + transaction->header.transaction_not_message = 1; +} + +typedef enum +{ + xenidc_gateway_state_i, + xenidc_gateway_state_i_cc, + xenidc_gateway_state_i_cc_cd, + xenidc_gateway_state_i_cc_lc, + xenidc_gateway_state_i_cc_cd_lc, + xenidc_gateway_state_i_cc_lc_cd, + xenidc_gateway_state_i_cc_lc_cd_km, + xenidc_gateway_state_i_cc_cd_lc_lg, + xenidc_gateway_state_i_cc_cd_lc_ld, + xenidc_gateway_state_i_cc_cd_lc_lg_ld, + xenidc_gateway_state_i_cc_cd_lc_lg_ld_ti +} +xenidc_gateway_state; + +typedef struct xenidc_gateway_struct xenidc_gateway; + +struct xenidc_gateway_struct +{ + xenidc_channel * channel; + void ( * connect )( xenidc_gateway * gateway ); + void ( * handle_message ) + ( xenidc_gateway * gateway, xenidc_gateway_message * message ); + void ( * handle_transaction ) + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ); + void ( * disconnect ) + ( xenidc_gateway * gateway, xenidc_callback * callback ); + + u32 initiator_quota; + u32 target_quota; + xenidc_buffer_byte_count target_maximum_byte_count; + + struct list_head initiator_resource_list; + xenidc_gateway_initiator_resource * initiator_resources; + + struct list_head target_resource_list; + xenidc_gateway_target_resource * target_resources; + + spinlock_t lock; + + xenidc_gateway_state state; + + struct list_head message_and_transaction_list; + struct list_head channel_message_list; + + xenidc_work kick_messages_and_transactions_1_work; + xenidc_work kick_channel_messages_1_work; + xenidc_work connect_client_1_work; + xenidc_work disconnect_client_1_work; + xenidc_callback disconnect_client_2_callback; + + int kick_messages_and_transactions_out : 1; + int kick_channel_messages_out : 1; + u32 initiator_resources_out; + u32 target_resources_out; + + xenidc_callback * channel_disconnect_callback; +}; + +extern int xenidc_gateway_init +( + xenidc_gateway * gateway, + xenidc_channel * channel, + void ( * connect )( xenidc_gateway * gateway ), + void ( * handle_message ) + ( xenidc_gateway * gateway, xenidc_gateway_message * message ), + void ( * handle_transaction ) + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ), + void ( * disconnect ) + ( xenidc_gateway * gateway, xenidc_callback * callback ), + u32 initiator_quota, + xenidc_buffer_byte_count initiator_maximum_byte_count, + u32 target_quota, + xenidc_buffer_byte_count target_maximum_byte_count +); + +extern void xenidc_gateway_submit_message + ( xenidc_gateway * gateway, xenidc_gateway_message * message ); + +extern void xenidc_gateway_submit_transaction + ( xenidc_gateway * gateway, xenidc_gateway_transaction * transaction ); + +extern void xenidc_gateway_exit( xenidc_gateway * gateway ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_gnttab_channel.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_gnttab_channel.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,222 @@ +/*****************************************************************************/ +/* This is a class which implements a grant-tables based inter-domain */ +/* message channel. The implementation of the bring-up and tear-down */ +/* handshaking is left to a derived class. */ +/* This class is used by xenidc_xbgt_channel (which implements bring-up and */ +/* teardown using xenbus) which is in turn used to implement the */ +/* xenidc_endpoint class. */ +/* */ +/* 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 _XENIDC_GNTTAB_CHANNEL_H +#define _XENIDC_GNTTAB_CHANNEL_H + +#include +#include + +typedef struct xenidc_gnttab_channel_struct xenidc_gnttab_channel; + +typedef struct xenidc_gnttab_channel_target_resource_struct + xenidc_gnttab_channel_target_resource; + +struct xenidc_gnttab_channel_target_resource_struct +{ + xenidc_channel_message message; + xenidc_gnttab_channel * channel; +}; + +#define XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_LINK \ +message.XENIDC_CHANNEL_MESSAGE_LINK + +typedef enum +{ + xenidc_gnttab_channel_state_i, + xenidc_gnttab_channel_state_i_c1r, + xenidc_gnttab_channel_state_i_c1r_c1c, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r, + xenidc_gnttab_channel_state_i_c1r_c1c_d1r, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc, + xenidc_gnttab_channel_state_i_c1r_c1c_c2r_c2s_ccc_d2r_ksc_krc_dcc_tri +} +xenidc_gnttab_channel_state; + +#define XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT 16 + +struct xenidc_gnttab_channel_struct +{ + xenidc_channel channel; + void ( * protocol_error )( xenidc_gnttab_channel * channel ); + + xenidc_gnttab_channel * send_irq_context; + xenidc_gnttab_channel * recv_irq_context; + + void * send_ring; + struct vm_struct * recv_ring_area; + grant_ref_t grant_ref_pool; + + spinlock_t lock; + + xenidc_gnttab_channel_state state; + + struct list_head message_list; + + xenidc_work do_phase_one_connect_1_work; + xenidc_work do_phase_two_connect_1_work; + xenidc_work connect_client_1_work; + xenidc_work disconnect_client_1_work; + xenidc_callback disconnect_client_2_callback; + xenidc_work do_phase_two_disconnect_1_work; + xenidc_work do_phase_one_disconnect_1_work; + xenidc_work kick_send_ring_1_work; + xenidc_work kick_recv_ring_1_work; + + xenidc_gnttab_channel_target_resource target_resources + [ XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT ]; + u8 target_resource_free + [ XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT ]; + u16 target_resource_offset + [ XENIDC_GNTTAB_CHANNEL_TARGET_RESOURCE_COUNT ]; + + int first_target_resource; + int next_target_resource; + + int send_ring_kick_out : 1; + int recv_ring_kick_out : 1; + + xenidc_callback * current_callback; + + unsigned int send_event_channel; + grant_ref_t send_ring_ref; + int send_irq; + + unsigned int recv_event_channel; + u16 recv_ring_handle; + xenidc_local_buffer_reference recv_ring_lbr; + int recv_irq; + u16 recv_ring_offset; +}; + +/* Cast to base class. */ + +static inline xenidc_channel * xenidc_gnttab_channel_to_channel + ( xenidc_gnttab_channel * channel ) +{ + return &channel->channel; +} + +/* Cast from base class. */ + +static inline xenidc_gnttab_channel * xenidc_gnttab_channel_channel_to + ( xenidc_channel * channel ) +{ + return container_of( channel, xenidc_gnttab_channel, channel ); +} + +/* Called by derived class. */ + +extern int xenidc_gnttab_channel_init +( + xenidc_gnttab_channel * channel, + void ( * protocol_error )( xenidc_gnttab_channel * channel ) +); + +/* xenidc_gnttab_channel_phase_one_connect should never fail because we are */ +/* supposed to have reserved all required resources during initialisation. */ +/* Called by derived class. */ + +typedef struct xenidc_gnttab_channel_phase_one_connect_request_struct + xenidc_gnttab_channel_phase_one_connect_request; + +struct xenidc_gnttab_channel_phase_one_connect_request_struct +{ + xenidc_callback callback; + domid_t remote_domain_id; /* IN */ + grant_ref_t send_ring_ref; /* OUT */ + unsigned int send_event_channel; /* OUT */ +}; + +static inline xenidc_gnttab_channel_phase_one_connect_request * + xenidc_gnttab_channel_phase_one_connect_request_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_gnttab_channel_phase_one_connect_request, callback ); +} + +extern void xenidc_gnttab_channel_phase_one_connect +( + xenidc_gnttab_channel * channel, + xenidc_gnttab_channel_phase_one_connect_request * request +); + +/* Called by derived class whilst phase one connected to reset the ring */ +/* before (re)establishing the connection with the other side. */ + +extern void xenidc_gnttab_channel_reset_ring + ( xenidc_gnttab_channel * channel ); + +/* xenidc_gnttab_channel_phase_two_connect should only fail if the remote */ +/* domain provided incorrect parameters. */ +/* Called by derived class. */ + +typedef struct xenidc_gnttab_channel_phase_two_connect_request_struct + xenidc_gnttab_channel_phase_two_connect_request; + +struct xenidc_gnttab_channel_phase_two_connect_request_struct +{ + xenidc_callback callback; + domid_t remote_domain_id; /* IN */ + grant_ref_t recv_ring_ref; /* IN */ + unsigned int recv_event_channel; /* IN */ +}; + +static inline xenidc_gnttab_channel_phase_two_connect_request * + xenidc_gnttab_channel_phase_two_connect_request_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_gnttab_channel_phase_two_connect_request, callback ); +} + +extern void xenidc_gnttab_channel_phase_two_connect +( + xenidc_gnttab_channel * channel, + xenidc_gnttab_channel_phase_two_connect_request * request +); + +/* Called by derived class. */ + +extern void xenidc_gnttab_channel_phase_two_disconnect + ( xenidc_gnttab_channel * channel, xenidc_callback * callback ); + +/* Called by derived class. */ + +extern void xenidc_gnttab_channel_phase_one_disconnect + ( xenidc_gnttab_channel * channel, xenidc_callback * callback ); + +/* Called by derived class. */ + +extern void xenidc_gnttab_channel_exit( xenidc_gnttab_channel * channel ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_local_buffer_reference.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_local_buffer_reference.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,283 @@ +/*****************************************************************************/ +/* Xen inter-domain communication local buffer reference object. */ +/* */ +/* 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 __ASM_XEN_XENIDC_LOCAL_BUFFER_REFERENCE_H__ +#define __ASM_XEN_XENIDC_LOCAL_BUFFER_REFERENCE_H__ + +#include +#include "xenidc_callback.h" + +#define XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL 0xC0000000 +#define XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL_ADVANCE 0x80000000 + +typedef u32 xenidc_buffer_type; + +typedef u32 xenidc_buffer_byte_count; + +typedef struct xenidc_local_buffer_reference_struct + xenidc_local_buffer_reference; + +struct xenidc_local_buffer_reference_struct +{ + xenidc_buffer_type type; + void * base; + xenidc_buffer_byte_count byte_offset; + xenidc_buffer_byte_count byte_count; +}; + +static inline xenidc_buffer_byte_count + xenidc_local_buffer_reference_query_byte_offset + ( xenidc_local_buffer_reference * lbr ) +{ + return lbr->byte_offset; +} + +static inline xenidc_buffer_byte_count + xenidc_local_buffer_reference_query_byte_count + ( xenidc_local_buffer_reference * lbr ) +{ + return lbr->byte_count; +} + +typedef struct xenidc_buffer_concrete_class_struct + xenidc_buffer_concrete_class; + +struct xenidc_buffer_concrete_class_struct +{ + struct list_head link; + xenidc_buffer_type type; + void ( * copy_in_or_out ) + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count byte_count, + int out + ); + void ( * zero ) + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr + ); +}; + +extern xenidc_buffer_type + xenidc_local_buffer_reference_register_buffer_concrete_class +( + xenidc_buffer_concrete_class * class, + void ( * copy_in_or_out ) + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count byte_count, + int out + ), + void ( * zero ) /* NULL for the inefficient default */ + ( + xenidc_buffer_concrete_class * class, + xenidc_local_buffer_reference * lbr + ) +); + +typedef struct xenidc_buffer_copy_class_struct xenidc_buffer_copy_class; + +struct xenidc_buffer_copy_class_struct +{ + struct list_head link; + xenidc_buffer_type target_type; + xenidc_buffer_type source_type; + void ( * copy ) + ( + xenidc_buffer_copy_class * class, + xenidc_local_buffer_reference * target, + xenidc_local_buffer_reference * source, + xenidc_buffer_byte_count byte_count + ); +}; + +extern void xenidc_local_buffer_reference_register_buffer_copy_class +( + xenidc_buffer_copy_class * class, + xenidc_buffer_type target_type, + xenidc_buffer_type source_type, + void ( * copy ) + ( + xenidc_buffer_copy_class * class, + xenidc_local_buffer_reference * target, + xenidc_local_buffer_reference * source, + xenidc_buffer_byte_count byte_count + ) +); + +typedef struct xenidc_buffer_virtual_class_struct xenidc_buffer_virtual_class; + +struct xenidc_buffer_virtual_class_struct +{ + struct list_head link; + xenidc_buffer_type type; + xenidc_local_buffer_reference ( * resolve ) + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr + ); + void ( * advance ) + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_count + ); +}; + +extern xenidc_buffer_type + xenidc_local_buffer_reference_register_buffer_virtual_class +( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference ( * resolve ) + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr + ), + void ( * advance ) /* NULL for the efficient default */ + ( + xenidc_buffer_virtual_class * class, + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_count + ) +); + +/* Copy between an lbr and a buffer. Returns the number of bytes copied */ +/* which is the minimum of the source and target byte_counts. */ + +extern xenidc_buffer_byte_count xenidc_local_buffer_reference_copy_in_or_out +( + xenidc_local_buffer_reference * lbr, + void * buffer, + xenidc_buffer_byte_count buffer_byte_count, + int out +); + +static inline xenidc_buffer_byte_count xenidc_local_buffer_reference_copy_out +( + xenidc_local_buffer_reference * lbr, + void * target, + xenidc_buffer_byte_count target_byte_count +) +{ + return xenidc_local_buffer_reference_copy_in_or_out + ( lbr, target, target_byte_count, 1 ); +} + +static inline xenidc_buffer_byte_count xenidc_local_buffer_reference_copy_in +( + xenidc_local_buffer_reference * lbr, + void * source, + xenidc_buffer_byte_count source_byte_count +) +{ + return xenidc_local_buffer_reference_copy_in_or_out + ( lbr, source, source_byte_count, 0 ); +} + +extern void xenidc_local_buffer_reference_zero + ( xenidc_local_buffer_reference * lbr ); + +extern xenidc_buffer_byte_count xenidc_local_buffer_reference_copy +( + xenidc_local_buffer_reference * target, + xenidc_local_buffer_reference * source +); + +extern xenidc_buffer_byte_count xenidc_local_buffer_reference_virtual_advance + ( xenidc_local_buffer_reference * lbr, xenidc_buffer_byte_count byte_count ); + +/* Advance increments the offset and decrements the length by the amount */ +/* specified which is useful to advance the reference after having copied a */ +/* chunk of data into the start of the buffer. */ +/* Advance returns the remaining length. */ + +static inline xenidc_buffer_byte_count xenidc_local_buffer_reference_advance + ( xenidc_local_buffer_reference * lbr, xenidc_buffer_byte_count byte_count ) +{ + if + ( + ( lbr->type & XENIDC_LOCAL_BUFFER_REFERENCE_TYPE_FLAG_VIRTUAL_ADVANCE ) + == + 0 + ) + { + if( lbr->byte_count > byte_count ) + { + lbr->byte_offset += byte_count; + lbr->byte_count -= byte_count; + } + else + { + lbr->byte_offset += lbr->byte_count; + lbr->byte_count = 0; + } + + return lbr->byte_count; + } + else + { + return + xenidc_local_buffer_reference_virtual_advance( lbr, byte_count ); + } +} + +/* Truncate reduces the length of the buffer which results in a reference to */ +/* the first byte_count bytes of the buffer (or the whole buffer, whichever */ +/* is less). */ +/* Truncate returns the resulting length of the buffer. */ + +static inline xenidc_buffer_byte_count xenidc_local_buffer_reference_truncate +( + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_count +) +{ + if( lbr->byte_count > byte_count ) + { + lbr->byte_count = byte_count; + } + + return lbr->byte_count; +} + +/* Subrange reduces the range of the buffer by advancing the start */ +/* byte_offset bytes and reducing the length to the minimum of byte_count */ +/* and the remaining length. Subrange returns the resulting length of the */ +/* buffer. */ + +static inline xenidc_buffer_byte_count xenidc_local_buffer_reference_subrange +( + xenidc_local_buffer_reference * lbr, + xenidc_buffer_byte_count byte_offset, + xenidc_buffer_byte_count byte_count +) +{ + (void)xenidc_local_buffer_reference_advance( lbr, byte_offset ); + + return xenidc_local_buffer_reference_truncate( lbr, byte_count ); +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_rbr_mapper_pool.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_rbr_mapper_pool.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,150 @@ +/*****************************************************************************/ +/* Xen inter-domain communication remote buffer reference mapper pool. */ +/* */ +/* TO MAP REMOTE BUFFERS INTO THE LOCAL ADDRESS SPACE. */ +/* */ +/* 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 __ASM_XEN_XENIDC_RBR_MAPPER_POOL_H__ +#define __ASM_XEN_XENIDC_RBR_MAPPER_POOL_H__ + +typedef struct xenidc_rbr_mapper_pool_struct xenidc_rbr_mapper_pool; + +/* xenidc_allocate_rbr_mapper_pool returns NULL if the pool cannot be */ +/* allocated or the anti-deadlock reservation fails. */ + +extern xenidc_rbr_mapper_pool * xenidc_allocate_rbr_mapper_pool + ( u32 anti_deadlock_page_count ); + +extern void xenidc_free_rbr_mapper_pool( xenidc_rbr_mapper_pool * pool ); + +typedef struct xenidc_map_rbr_request_element_struct + xenidc_map_rbr_request_element; + +struct xenidc_map_rbr_request_element_struct +{ + struct list_head link; + xenidc_remote_buffer_reference rbr; + void * mapping; +}; + +static inline void xenidc_map_rbr_request_element_init + ( xenidc_map_rbr_request_element * element ) +{ + memset( element, 0, sizeof( *element ) ); + + INIT_LIST_HEAD( &element->link ); +} + +static inline void xenidc_map_rbr_request_element_set_rbr +( + xenidc_map_rbr_request_element * element, + xenidc_remote_buffer_reference rbr +) +{ + element->rbr = rbr; +} + +static inline void xenidc_map_rbr_request_element_ensure_removed + ( xenidc_map_rbr_request_element * element ) +{ + list_del_init( &element->link ); +} + +typedef struct xenidc_reserve_and_map_rbr_request_struct + xenidc_reserve_and_map_rbr_request; + +struct xenidc_reserve_and_map_rbr_request_struct +{ + xenidc_callback map_callback; + xenidc_callback unmap_callback; + struct list_head request_elements; +}; + +static inline xenidc_callback * + xenidc_reserve_and_map_rbr_request_to_map_callback + ( xenidc_reserve_and_map_rbr_request * request ) +{ + return &request->map_callback; +} + +static inline xenidc_reserve_and_map_rbr_request * + xenidc_reserve_and_map_rbr_request_map_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_reserve_and_map_rbr_request, map_callback ); +} + +static inline xenidc_callback * + xenidc_reserve_and_map_rbr_request_to_unmap_callback + ( xenidc_reserve_and_map_rbr_request * request ) +{ + return &request->unmap_callback; +} + +static inline xenidc_reserve_and_map_rbr_request * + xenidc_reserve_and_map_rbr_request_unmap_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_reserve_and_map_rbr_request, unmap_callback ); +} + +static inline void xenidc_reserve_and_map_rbr_request_init +( + xenidc_reserve_and_map_rbr_request * request, + xenidc_callback_function * map_callback, + xenidc_callback_function * unmap_callback +) +{ + xenidc_callback_init( &request->map_callback, map_callback ); + xenidc_callback_init( &request->unmap_callback, unmap_callback ); + + INIT_LIST_HEAD( &request->request_elements ); +} + +static inline void xenidc_reserve_and_map_rbr_request_add_element +( + xenidc_reserve_and_map_rbr_request * request, + xenidc_map_rbr_request_element * element +) +{ + list_add( &element->link, &request->request_elements ); +} + +extern void xenidc_rbr_mapper_pool_reserve_and_map_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +); + +extern void xenidc_rbr_mapper_pool_abort_reserve_and_map_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +); + +extern void xenidc_rbr_mapper_pool_unmap_and_unreserve_rbrs +( + xenidc_rbr_mapper_pool * pool, + xenidc_reserve_and_map_rbr_request * request +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_rbr_provider_pool.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_rbr_provider_pool.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,160 @@ +/*****************************************************************************/ +/* Xen inter-domain communication remote buffer reference provider pool. */ +/* */ +/* TO CREATE REMOTE BUFFER REFERENCES TO GRANT OTHER DOMAINS ACCESS TO LOCAL */ +/* BUFFERS. */ +/* */ +/* 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 __ASM_XEN_XENIDC_RBR_PROVIDER_POOL_H__ +#define __ASM_XEN_XENIDC_RBR_PROVIDER_POOL_H__ + +#include "xenidc_remote_buffer_reference.h" + +typedef struct xenidc_rbr_provider_pool_struct xenidc_rbr_provider_pool; + +/* xenidc_allocate_rbr_provider_pool returns NULL if the pool cannot be */ +/* allocated or the anti-deadlock reservation fails. */ + +extern xenidc_rbr_provider_pool * xenidc_allocate_rbr_provider_pool + ( u32 anti_deadlock_page_count ); + +extern void xenidc_free_rbr_provider_pool( xenidc_rbr_provider_pool * pool ); + +typedef struct xenidc_create_rbr_request_element_struct + xenidc_create_rbr_request_element; + +struct xenidc_create_rbr_request_element_struct +{ + struct list_head link; + xenidc_local_buffer_reference lbr; + xenidc_remote_buffer_reference rbr; +}; + +static inline void xenidc_create_rbr_request_element_init + ( xenidc_create_rbr_request_element * element ) +{ + memset( element, 0, sizeof( *element ) ); + + INIT_LIST_HEAD( &element->link ); +} + +static inline void xenidc_create_rbr_request_element_set_lbr +( + xenidc_create_rbr_request_element * element, + xenidc_local_buffer_reference lbr +) +{ + element->lbr = lbr; +} + +static inline xenidc_remote_buffer_reference + xenidc_create_rbr_request_element_query_rbr + ( xenidc_create_rbr_request_element * element ) +{ + return element->rbr; +} + +static inline void xenidc_create_rbr_request_element_ensure_removed + ( xenidc_create_rbr_request_element * element ) +{ + list_del_init( &element->link ); +} + +typedef struct xenidc_reserve_and_create_rbr_request_struct + xenidc_reserve_and_create_rbr_request; + +struct xenidc_reserve_and_create_rbr_request_struct +{ + xenidc_callback create_callback; + xenidc_callback revoke_callback; + struct list_head request_elements; +}; + +static inline xenidc_callback * + xenidc_reserve_and_create_rbr_request_to_create_callback + ( xenidc_reserve_and_create_rbr_request * request ) +{ + return &request->create_callback; +} + +static inline xenidc_reserve_and_create_rbr_request * + xenidc_reserve_and_create_rbr_request_create_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_reserve_and_create_rbr_request, create_callback ); +} + +static inline xenidc_callback * + xenidc_reserve_and_create_rbr_request_to_revoke_callback + ( xenidc_reserve_and_create_rbr_request * request ) +{ + return &request->revoke_callback; +} + +static inline xenidc_reserve_and_create_rbr_request * + xenidc_reserve_and_create_rbr_request_revoke_callback_to + ( xenidc_callback * callback ) +{ + return container_of + ( callback, xenidc_reserve_and_create_rbr_request, revoke_callback ); +} + +static inline void xenidc_reserve_and_create_rbr_request_init +( + xenidc_reserve_and_create_rbr_request * request, + xenidc_callback_function * create_callback, + xenidc_callback_function * revoke_callback +) +{ + xenidc_callback_init( &request->create_callback, create_callback ); + xenidc_callback_init( &request->revoke_callback, revoke_callback ); + + INIT_LIST_HEAD( &request->request_elements ); +} + +static inline void xenidc_reserve_and_create_rbr_request_add_element +( + xenidc_reserve_and_create_rbr_request * request, + xenidc_create_rbr_request_element * element +) +{ + list_add( &element->link, &request->request_elements ); +} + +extern void xenidc_rbr_provider_pool_reserve_and_create_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +); + +extern void xenidc_rbr_provider_pool_abort_reserve_and_create_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +); + +extern void xenidc_rbr_provider_pool_revoke_and_unreserve_rbrs +( + xenidc_rbr_provider_pool * pool, + xenidc_reserve_and_create_rbr_request * request +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_remote_buffer_reference.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_remote_buffer_reference.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,42 @@ +/*****************************************************************************/ +/* Xen inter-domain communication remote buffer references. */ +/* */ +/* 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 __ASM_XEN_XENIDC_REMOTE_BUFFER_REFERENCE_H__ +#define __ASM_XEN_XENIDC_REMOTE_BUFFER_REFERENCE_H__ + +#include "xenidc_local_buffer_reference.h" + +typedef struct xenidc_remote_buffer_reference_struct + xenidc_remote_buffer_reference; + +struct xenidc_remote_buffer_reference_struct +{ + xenidc_buffer_byte_count byte_count; +}; + +static inline xenidc_buffer_byte_count + xenidc_remote_buffer_reference_query_byte_count + ( xenidc_remote_buffer_reference * rbr ) +{ + return rbr->byte_count; +} + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_vaddress.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_vaddress.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,30 @@ +/*****************************************************************************/ +/* Support for local buffer references of type vaddress. */ +/* */ +/* 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 XENIDC_VADDRESS_H +#define XENIDC_VADDRESS_H + +#include "xenidc.h" + +extern xenidc_local_buffer_reference xenidc_vaddress_create_lbr + ( void * address, xenidc_buffer_byte_count byte_count ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_work.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_work.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,160 @@ +/*****************************************************************************/ +/* Enhanced work queue service */ +/* 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 */ +/* */ +/*****************************************************************************/ + +/* This work queue service is a lot like the linux work queue service except */ +/* that it provides the interface xenidc_work_until which allows the client */ +/* to wait for a condition to be met even when the client is executing on a */ +/* work thread and the condition will only be satisfied by execution of */ +/* another work item. */ + +#ifndef XENIDC_WORK_H +#define XENIDC_WORK_H + +#include +#include +#include +#include +#include + +typedef struct xenidc_work_struct xenidc_work; + +struct xenidc_work_struct +{ + struct list_head link; + void ( * function )( void * context ); + void * context; +}; + +#define XENIDC_WORK_LINK link + +#define XENIDC_WORK_INIT( name, fn, ctx ) \ +{ LIST_HEAD_INIT( name.link ), fn, ctx } + +#define XENIDC_WORK( name, fn, ctx ) \ +xenidc_work name = XENIDC_WORK_INIT( name, fn, ctx ) + +static inline void xenidc_work_init +( + xenidc_work * work, + void ( * function )( void * context ), + void * context +) +{ + INIT_LIST_HEAD( &work->link ); + + work->function = function; + work->context = context; +} + +static inline struct list_head * xenidc_work_to_link( xenidc_work * work ) +{ + return &work->link; +} + +static inline xenidc_work * xenidc_work_link_to( struct list_head * link ) +{ + return container_of( link, xenidc_work, link ); +} + +/* Returns 1 if scheduled this time or 0 if already scheduled. */ + +int xenidc_work_schedule( xenidc_work * work ); + +static inline void xenidc_work_perform_synchronously( xenidc_work * work ) +{ + work->function( work->context ); +} + +/* Don't use any of these, they're just exposed for the macro below. */ + +extern spinlock_t xenidc_work_list_lock; + +extern struct list_head xenidc_work_list; + +extern wait_queue_head_t xenidc_work_waitqueue; + +extern struct list_head xenidc_work_condition; + +/* Wait for a condition to be met. This works whether or not you call it */ +/* from a work item and works even when the condition will only be satisfied */ +/* by another work item. */ + +#define xenidc_work_until( condition ) \ +do \ +{ \ + unsigned long flags; \ + \ + spin_lock_irqsave( &xenidc_work_list_lock, flags ); \ + \ + for( ; ; ) \ + { \ + while \ + ( \ + ( !list_empty( &xenidc_work_list ) ) \ + && \ + ( !( condition ) ) \ + ) \ + { \ + xenidc_work * work = list_entry \ + ( \ + xenidc_work_list.next, \ + xenidc_work, \ + link \ + ); \ + \ + list_del_init( &work->link ); \ + \ + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); \ + \ + xenidc_work_perform_synchronously( work ); \ + \ + spin_lock_irqsave( &xenidc_work_list_lock, flags ); \ + } \ + \ + if( condition ) \ + { \ + break; \ + } \ + \ + { \ + struct list_head link; \ + \ + INIT_LIST_HEAD( &link ); \ + \ + list_add_tail( &link, &xenidc_work_condition ); \ + \ + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); \ + \ + wait_event( xenidc_work_waitqueue, list_empty( &link ) ); \ + \ + spin_lock_irqsave( &xenidc_work_list_lock, flags ); \ + } \ + } \ + \ + spin_unlock_irqrestore( &xenidc_work_list_lock, flags ); \ +} \ +while( 0 ) + +/* When you satisfy a condition, you should call this to kick any threads */ +/* waiting on the condition. */ + +void xenidc_work_wake_up( void ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_wrapping.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_wrapping.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,48 @@ +/*****************************************************************************/ +/* Support for wrapping buffer local buffer references */ +/* */ +/* 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 XENIDC_WRAPPING_H +#define XENIDC_WRAPPING_H + +#include "xenidc.h" + +typedef enum +{ + xenidc_wrapping_client_type_producer, + xenidc_wrapping_client_type_consumer +} +xenidc_wrapping_client_type; + +/* Create a reference to a wrapping buffer. Pass the start offset and the */ +/* end offset of the section of interest of the wrapping buffer. If the */ +/* start and end offsets are the same then a producer will get a reference */ +/* to the whole buffer to fill whereas a consumer will get a reference to a */ +/* zero length buffer. */ + +extern xenidc_local_buffer_reference xenidc_wrapping_create_lbr +( + xenidc_local_buffer_reference * underlying_buffer, + xenidc_buffer_byte_count start_offset, + xenidc_buffer_byte_count end_offset, + xenidc_wrapping_client_type client_type +); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 linux-2.6-xen-sparse/include/asm-xen/xenidc_xbgt_channel.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/linux-2.6-xen-sparse/include/asm-xen/xenidc_xbgt_channel.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,142 @@ +/*****************************************************************************/ +/* This is a class which uses a xenidc_gnttab_channel (grant-tables based */ +/* message channel class) and xenbus to implement an interdomain message */ +/* channel with grant-tables based message transfer and xenbus based */ +/* bring-up and tear-down handshaking. */ +/* This class is used in the implementation of the xenidc_endpoint class. */ +/* */ +/* 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 _XENIDC_XBGT_CHANNEL_H +#define _XENIDC_XBGT_CHANNEL_H + +#include +#include + +typedef struct xenidc_xbgt_channel_struct xenidc_xbgt_channel; + +typedef enum +{ + xenidc_xbgt_channel_state_i, + xenidc_xbgt_channel_state_i_cn, + xenidc_xbgt_channel_state_i_cn_dn, + xenidc_xbgt_channel_state_i_cn_rs, + xenidc_xbgt_channel_state_i_cn_dn_rs, + xenidc_xbgt_channel_state_i_cn_rs_dn, + xenidc_xbgt_channel_state_i_cn_rs_rs, + xenidc_xbgt_channel_state_i_cn_rs_rf, + xenidc_xbgt_channel_state_i_cn_dn_rs_rs, + xenidc_xbgt_channel_state_i_cn_rs_dn_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_dn, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_rf, + xenidc_xbgt_channel_state_i_cn_rs_rf_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od, + xenidc_xbgt_channel_state_i_cn_rs_rf_rs_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_pe, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_dn, + xenidc_xbgt_channel_state_i_cn_rs_rs_rs_od_rs_or_rs_oc_rs +} +xenidc_xbgt_channel_state; + +typedef enum +{ + xenidc_xbgt_channel_other_state_unknown, + xenidc_xbgt_channel_other_state_disconnected, + xenidc_xbgt_channel_other_state_ready, + xenidc_xbgt_channel_other_state_connected +} +xenidc_xbgt_channel_other_state; + +struct xenidc_xbgt_channel_struct +{ + xenidc_gnttab_channel channel; + + struct xenbus_watch watch; + + spinlock_t lock; + + xenidc_xbgt_channel_state state; + xenidc_xbgt_channel_other_state other_state; + + char * local_path; + char * remote_path; + domid_t remote_domain_id; + + xenidc_callback * disconnect_callback; + + int ready_ring_reference; + int ready_event_channel; + + xenidc_work register_watch_1_work; + xenidc_work unregister_watch_1_work; + xenidc_work clear_store_1_work; + xenidc_work write_ready_1_work; + xenidc_work write_connected_1_work; + xenidc_work phase_one_connect_1_work; + xenidc_work phase_two_connect_1_work; + xenidc_work phase_two_disconnect_1_work; + xenidc_work phase_one_disconnect_1_work; + + xenidc_gnttab_channel_phase_one_connect_request phase_one_connect_request; + xenidc_gnttab_channel_phase_two_connect_request phase_two_connect_request; + + xenidc_callback phase_two_disconnect_callback; + xenidc_callback phase_one_disconnect_callback; +}; + +static inline xenidc_channel * xenidc_xbgt_channel_to_channel + ( xenidc_xbgt_channel * channel ) +{ + return xenidc_gnttab_channel_to_channel( &channel->channel ); +} + +static inline xenidc_xbgt_channel * xenidc_xbgt_channel_gnttab_channel_to + ( xenidc_gnttab_channel * gnttab_channel ) +{ + return container_of( gnttab_channel, xenidc_xbgt_channel, channel ); +} + +static inline xenidc_xbgt_channel * xenidc_xbgt_channel_channel_to + ( xenidc_channel * channel ) +{ + return xenidc_xbgt_channel_gnttab_channel_to + ( xenidc_gnttab_channel_channel_to( channel ) ); +} + +extern int xenidc_xbgt_channel_init( xenidc_xbgt_channel * channel ); + +extern void xenidc_xbgt_channel_connect +( + xenidc_xbgt_channel * channel, + char * local_path, + char * remote_path, + domid_t remote_domain_id +); + +extern void xenidc_xbgt_channel_disconnect + ( xenidc_xbgt_channel * channel, xenidc_callback * callback ); + +extern void xenidc_xbgt_channel_exit( xenidc_xbgt_channel * channel ); + +#endif diff -r b5903c9aeda5 -r e93b9c54edb3 patches/linux-2.6.12/usb.patch --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/patches/linux-2.6.12/usb.patch Sun Oct 30 16:03:34 2005 @@ -0,0 +1,10 @@ +diff -Naur linux-2.6.12/drivers/usb/core/usb.c linux-2.6.12-usb/drivers/usb/core/usb.c +--- linux-2.6.12/drivers/usb/core/usb.c 2005-06-17 20:48:29.000000000 +0100 ++++ linux-2.6.12-usb/drivers/usb/core/usb.c 2005-09-28 13:01:29.000000000 +0100 +@@ -1561,4 +1561,6 @@ + #endif + EXPORT_SYMBOL (usb_buffer_unmap_sg); + ++EXPORT_SYMBOL (usb_bus_type); ++ + MODULE_LICENSE("GPL"); diff -r b5903c9aeda5 -r e93b9c54edb3 xen/include/public/io/usbif.h --- /dev/null Sun Oct 30 09:45:49 2005 +++ b/xen/include/public/io/usbif.h Sun Oct 30 16:03:34 2005 @@ -0,0 +1,357 @@ +/*****************************************************************************/ +/* FE->BE transactions for the USB split driver interface. */ +/* */ +/* 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 __SHARED_USBIF_H__ +#define __SHARED_USBIF_H__ + +#include + +/* USBIF_MAX_PAGES_PER_REQUEST is the maximum number of pages a request is */ +/* allowed to require to construct the mapping in the back-end. */ + +#define USBIF_MAX_PAGES_PER_REQUEST 128 + +/* All transactions have the same header which specifies the type of the */ +/* transaction. */ + +typedef struct usbif_transaction_parameters_header_struct + usbif_transaction_parameters_header; + +struct usbif_transaction_parameters_header_struct +{ + u8 transaction_type; + u8 reserved[ 7 ]; +}; + +/* There are three types of transaction: */ + +#define USBIF_TRANSACTION_TYPE_PROBE 0 +#define USBIF_TRANSACTION_TYPE_RESET 1 +#define USBIF_TRANSACTION_TYPE_IO 2 + +/* The probe transaction is used to test whether a USB device is connected */ +/* to a port. */ +/* Port numbers start at 1. */ + +typedef struct usbif_probe_transaction_parameters_struct + usbif_probe_transaction_parameters; + +struct usbif_probe_transaction_parameters_struct +{ + usbif_transaction_parameters_header header; + u32 port; + u8 reserved[ 4 ]; +}; + +typedef struct usbif_probe_transaction_status_struct + usbif_probe_transaction_status; + +struct usbif_probe_transaction_status_struct +{ + u8 result; + u8 reserved[ 7 ]; +}; + +#define USBIF_PROBE_RESULT_NO_DEVICE 0 +#define USBIF_PROBE_RESULT_DEVICE_PRESENT 1 + +/* The reset transaction is used to reset a port. On success, the speed the */ +/* port is operating at after the reset is returned. */ + +typedef struct usbif_reset_transaction_parameters_struct + usbif_reset_transaction_parameters; + +struct usbif_reset_transaction_parameters_struct +{ + usbif_transaction_parameters_header header; + u32 port; + u8 reserved[ 4 ]; +}; + +/* On success, the reset returns status... */ + +typedef struct usbif_reset_transaction_status_struct + usbif_reset_transaction_status; + +struct usbif_reset_transaction_status_struct +{ + u8 result; + u8 reserved[ 7 ]; +}; + +#define USBIF_RESET_RESULT_FULL_SPEED 0 +#define USBIF_RESET_RESULT_LOW_SPEED 1 +#define USBIF_RESET_RESULT_HIGH_SPEED 2 + +/* Aside from the usual transport errors, the reset transaction might fail */ +/* if there is no device. */ + +/* All IO transactions have a header which specifies the type of IO */ +/* transaction and contains parameters common to all types of IO transaction.*/ + +typedef struct usbif_io_transaction_parameters_header_struct + usbif_io_transaction_parameters_header; + +struct usbif_io_transaction_parameters_header_struct +{ + usbif_transaction_parameters_header header; + u8 io_transaction_type; + u8 device_number; + u8 endpoint; + u8 direction; + u32 unlink_id; + u32 flags; + u32 reserved; + xenidc_remote_buffer_reference rbr; +}; + +#define USBIF_IO_TRANSACTION_TYPE_CONTROL 0 +#define USBIF_IO_TRANSACTION_TYPE_BULK 1 +#define USBIF_IO_TRANSACTION_TYPE_INTERRUPT 2 +#define USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS 3 +/* Not a transaction type. For array bounds: */ +#define USBIF_IO_TRANSACTION_TYPE_LIMIT 4 + +#define USBIF_IO_TRANSACTION_DIRECTION_OUT 0 +#define USBIF_IO_TRANSACTION_DIRECTION_IN 1 + +#define USBIF_IO_FLAGS_SHORT_NOT_OK 0x00000001 +#define USBIF_IO_FLAGS_ZERO_PACKET 0x00000002 + +typedef struct usbif_control_io_transaction_parameters_struct + usbif_control_io_transaction_parameters; + +struct usbif_control_io_transaction_parameters_struct +{ + usbif_io_transaction_parameters_header header; + u8 setup[ 8 ]; +}; + +typedef struct usbif_bulk_io_transaction_parameters_struct + usbif_bulk_io_transaction_parameters; + +struct usbif_bulk_io_transaction_parameters_struct +{ + usbif_io_transaction_parameters_header header; +}; + +typedef struct usbif_interrupt_io_transaction_parameters_struct + usbif_interrupt_io_transaction_parameters; + +struct usbif_interrupt_io_transaction_parameters_struct +{ + usbif_io_transaction_parameters_header header; + u32 interval; +}; + +typedef struct usbif_isochronous_io_transaction_parameters_struct + usbif_isochronous_io_transaction_parameters; + +struct usbif_isochronous_io_transaction_parameters_struct +{ + usbif_io_transaction_parameters_header header; + u32 interval; + xenidc_remote_buffer_reference schedule_rbr; + u32 packet_count; +}; + +typedef struct usbif_isochronous_io_schedule_element_struct + usbif_isochronous_io_schedule_element; + +struct usbif_isochronous_io_schedule_element_struct +{ + xenidc_buffer_byte_count offset; /* IN offset into header's rbr */ + xenidc_buffer_byte_count length; /* IN = expected, OUT = actual */ + xenidc_error error; /* OUT for this packet. */ +}; + +typedef struct usbif_io_transaction_status_struct + usbif_io_transaction_status; + +struct usbif_io_transaction_status_struct +{ + xenidc_buffer_byte_count length; +}; + +/* USBIF specific transaction error codes: */ + +#define USBIF_XENIDC_ERROR_FAILURE \ + ( XENIDC_ERROR_PROTOCOL_SPECIFIC_FIRST + 0 ) +#define USBIF_XENIDC_ERROR_NO_DEVICE \ + ( XENIDC_ERROR_PROTOCOL_SPECIFIC_FIRST + 1 ) +#define USBIF_XENIDC_ERROR_UNLINKED \ + ( XENIDC_ERROR_PROTOCOL_SPECIFIC_FIRST + 2 ) + +static inline xenidc_error usbif_error_map_local_to( int error ) +{ + switch( error ) + { + /* FIXME: handle USB specific error codes. */ + default: + return xenidc_error_map_local_to( error ); + } +} + +static inline int usbif_error_map_to_local( xenidc_error error ) +{ + switch( error ) + { + /* FIXME: handle USB specific error codes. */ + default: + return xenidc_error_map_to_local( error ); + } +} + +/* Lookup table for sizes of io parameters structures. */ + +static const xenidc_buffer_byte_count usbif_io_parameters_byte_count + [ USBIF_IO_TRANSACTION_TYPE_LIMIT ] = +{ + [ USBIF_IO_TRANSACTION_TYPE_CONTROL ] = + sizeof( usbif_control_io_transaction_parameters ), + [ USBIF_IO_TRANSACTION_TYPE_BULK ] = + sizeof( usbif_bulk_io_transaction_parameters ), + [ USBIF_IO_TRANSACTION_TYPE_INTERRUPT ] = + sizeof( usbif_interrupt_io_transaction_parameters ), + [ USBIF_IO_TRANSACTION_TYPE_ISOCHRONOUS ] = + sizeof( usbif_isochronous_io_transaction_parameters ) +}; + +typedef union +{ + usbif_io_transaction_parameters_header header; + usbif_control_io_transaction_parameters control; + usbif_bulk_io_transaction_parameters bulk; + usbif_interrupt_io_transaction_parameters interrupt; + usbif_isochronous_io_transaction_parameters isochronous; +} +usbif_io_transaction_parameters; + +typedef union usbif_max_transaction_union usbif_max_transaction; + +union usbif_max_transaction_union +{ + struct + { + usbif_probe_transaction_parameters parameters; + usbif_probe_transaction_status status; + } probe; + struct + { + usbif_reset_transaction_parameters parameters; + usbif_reset_transaction_status status; + } reset; + struct + { + usbif_control_io_transaction_parameters parameters; + usbif_io_transaction_status status; + } control_io; + struct + { + usbif_bulk_io_transaction_parameters parameters; + usbif_io_transaction_status status; + } bulk_io; + struct + { + usbif_interrupt_io_transaction_parameters parameters; + usbif_io_transaction_status status; + } interrupt_io; + struct + { + usbif_isochronous_io_transaction_parameters parameters; + usbif_io_transaction_status status; + } isochronous_io; +}; + +/* All messages have the same header which specifies the type of the */ +/* message. */ + +typedef struct usbif_message_header_struct usbif_message_header; + +struct usbif_message_header_struct +{ + u8 message_type; + u8 reserved[ 7 ]; +}; + +/* There is only one type of message: */ + +#define USBIF_MESSAGE_TYPE_UNLINK 0 + +/* The unlink message is sent to unlink an IO operation. The backend will */ +/* ensure it completes or is failed out as quickly as possible. */ +/* This is a send and forget message: the client should wait for the */ +/* completion of the IO transaction as normal. */ + +typedef struct usbif_unlink_message_body_struct usbif_unlink_message_body; + +struct usbif_unlink_message_body_struct +{ + usbif_message_header header; + u32 unlink_id; + u32 reserved; +}; + +typedef union usbif_max_message_union usbif_max_message; + +union usbif_max_message_union +{ + usbif_unlink_message_body unlink; +}; + +typedef union usbif_max_transaction_or_message_union + usbif_max_transaction_or_message; + +union usbif_max_transaction_or_message_union +{ + usbif_max_transaction transaction; + usbif_max_message message; +}; + +#define USBIF_BE_INITIATOR_QUOTA 0 +#define USBIF_BE_INITIATOR_MAXIMUM_BYTE_COUNT 0 + +#define USBIF_BE_TARGET_QUOTA 32 +#define USBIF_BE_TARGET_MAXIMUM_BYTE_COUNT \ +( sizeof( usbif_max_transaction_or_message ) ) + +#define USBIF_FE_INITIATOR_QUOTA USBIF_BE_TARGET_QUOTA +#define USBIF_FE_INITIATOR_MAXIMUM_BYTE_COUNT \ +USBIF_BE_TARGET_MAXIMUM_BYTE_COUNT + +#define USBIF_FE_TARGET_QUOTA USBIF_BE_INITIATOR_QUOTA +#define USBIF_FE_TARGET_MAXIMUM_BYTE_COUNT \ +USBIF_BE_INITIATOR_MAXIMUM_BYTE_COUNT + +#endif