* [PATCH][15/17] USB virt 2.6 split driver---USB split driver back-end
@ 2005-11-21 13:19 harry
0 siblings, 0 replies; only message in thread
From: harry @ 2005-11-21 13:19 UTC (permalink / raw)
To: xen-devel
[-- Attachment #1: Type: text/plain, Size: 117 bytes --]
This patch implements the back end of the USB split driver.
Signed-off-by: Harry Butterworth <butterwo@uk.ibm.com>
[-- Attachment #2: p15-usb-usbback.patch --]
[-- Type: text/x-patch, Size: 138210 bytes --]
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/arch/xen/Kconfig
--- a/linux-2.6-xen-sparse/arch/xen/Kconfig Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/arch/xen/Kconfig Mon Nov 21 11:10:19 2005
@@ -77,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 n
+ help
+ This option causes the driver to output a continual trace of its
+ activity.
+ Say N here unless you are trying to debug the driver.
config XEN_TPMDEV_FRONTEND
bool "TPM-device frontend driver"
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/Makefile
--- a/linux-2.6-xen-sparse/drivers/xen/Makefile Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Mon Nov 21 11:10:19 2005
@@ -12,6 +12,7 @@
obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/
obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/
obj-$(CONFIG_XEN_TPMDEV_BACKEND) += tpmback/
+obj-$(CONFIG_XEN_USBDEV_BACKEND) += usbback/
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/
obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/
obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/Makefile
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/Makefile Mon Nov 21 11:10:19 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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_assert.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_assert.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,38 @@
+/*****************************************************************************/
+/* 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 <linux/kernel.h>
+
+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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,450 @@
+/*****************************************************************************/
+/* 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 <rusty@rustcorp.com.au>
+
+ 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 <asm-xen/xen-public/io/usbif.h>
+#include <asm-xen/xenbus.h>
+#include <asm-xen/xenidc.h>
+#include <linux/err.h>
+#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;
+ char *path;
+ xenidc_address address;
+ 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;
+}
+
+xenidc_address usbback_device_query_address(struct usbback_device * device)
+{
+ trace();
+
+ return device->address;
+}
+
+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_endpoint_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. */
+
+ /* We don't issue any messages or transactions from the BE to the FE but */
+ /* we hang onto the callback until we are internally quiesced. This */
+ /* stops the endpoint from reconnecting whilst we are quiescing which */
+ /* helps to keep the state machines a little bit simpler. */
+
+ usbback_driver_endpoint_disconnect
+ (usbback_device_endpoint_to(endpoint), callback);
+}
+
+static void usbback_device_endpoint_message
+ (xenidc_endpoint * endpoint, xenidc_endpoint_message * message) {
+ trace();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ usbback_driver_message_handler
+ (usbback_device_endpoint_to(endpoint), message);
+}
+
+static void usbback_device_endpoint_transaction
+ (xenidc_endpoint * endpoint, xenidc_endpoint_transaction * transaction) {
+ /* trace(); */
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ usbback_driver_transaction_handler
+ (usbback_device_endpoint_to(endpoint), transaction);
+}
+
+static int __usbback_device_resume_or_suspend
+ (struct usbback_device *device, int suspend);
+
+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) {
+ trace0("Failed to probe driver.");
+
+ goto EXIT_NO_DRIVER;
+ }
+
+ {
+ struct xenbus_transaction *transaction =
+ xenbus_transaction_start();
+
+ if (IS_ERR(transaction)) {
+ trace0("Error starting transaction.");
+
+ return_value = PTR_ERR(transaction);
+
+ goto EXIT_NO_PATH;
+ }
+
+ return_value = xenbus_gather
+ (transaction,
+ dev->nodename, "path", NULL, &device->path, NULL);
+
+ (void)xenbus_transaction_end(transaction, 1);
+ }
+
+ if (XENBUS_EXIST_ERR(return_value)) {
+ trace0("Frontend doesn't seem to exist.");
+
+ goto EXIT_NO_PATH;
+ }
+
+ if (return_value < 0) {
+ trace0("Failed to gather configuration parameters.");
+
+ xenbus_dev_error
+ (dev,
+ return_value, "reading %s/path", dev->nodename);
+
+ goto EXIT_NO_PATH;
+ }
+
+ trace1("Gathered configuration: path:%s", device->path);
+
+ 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) {
+ trace0
+ ("Failed to initialise endpoint for frontend connection.");
+
+ goto EXIT_NO_ENDPOINT;
+ }
+
+ usbback_driver_claim_port(device, 1, device->path);
+
+ return_value = __usbback_device_resume_or_suspend(device, 0);
+
+ if (return_value != 0) {
+ goto EXIT_NO_RESUME;
+ }
+
+ return 0;
+
+ EXIT:
+
+ (void)__usbback_device_resume_or_suspend(device, 1);
+
+ EXIT_NO_RESUME:
+
+ usbback_driver_release_port(device, 1);
+
+ xenidc_endpoint_exit(&device->endpoint);
+
+ EXIT_NO_ENDPOINT:
+
+ kfree(device->path);
+
+ EXIT_NO_PATH:
+
+ 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 int __usbback_device_resume_or_suspend
+ (struct usbback_device *device, int suspend) {
+ trace();
+
+ {
+ int return_value = 0;
+
+ if (suspend) {
+ goto SUSPEND;
+ }
+
+ xenidc_address_init
+ (&device->address,
+ device->dev->nodename,
+ device->dev->otherend, device->dev->otherend_id);
+
+ xenidc_endpoint_create(&device->endpoint, device->address);
+
+ return 0;
+
+ SUSPEND:
+
+ xenidc_endpoint_destroy(&device->endpoint);
+
+ return return_value;
+ }
+}
+
+static int usbback_device_resume(struct xenbus_device *dev)
+{
+ trace();
+
+ {
+ struct usbback_device *device = dev->data;
+
+ (void)__usbback_device_resume_or_suspend(device, 1);
+
+ return __usbback_device_resume_or_suspend(device, 0);
+ }
+}
+
+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,
+ .resume = usbback_device_resume,
+};
+
+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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_device.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,42 @@
+/*****************************************************************************/
+/* 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
+
+#include <asm-xen/xenidc.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);
+
+extern xenidc_address usbback_device_query_address
+ (struct usbback_device *device);
+
+#endif
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,240 @@
+/*****************************************************************************/
+/* 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 <linux/device.h>
+#include <linux/usb.h>
+#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_endpoint_connect(struct usbback_device *device)
+{
+ trace();
+
+ {
+ struct usbback_driver_backend *backend =
+ usbback_device_get_drvdata(device);
+
+ usbback_driver_backend_endpoint_connect(backend);
+ }
+}
+
+void usbback_driver_message_handler
+ (struct usbback_device *device, xenidc_endpoint_message * message) {
+ trace();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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(); */
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ struct usbback_driver_backend *backend =
+ usbback_device_get_drvdata(device);
+
+ usbback_driver_backend_transaction_handler(backend,
+ transaction);
+ }
+}
+
+void usbback_driver_endpoint_disconnect
+ (struct usbback_device *device, xenidc_callback * callback) {
+ trace();
+
+ {
+ struct usbback_driver_backend *backend =
+ usbback_device_get_drvdata(device);
+
+ usbback_driver_backend_endpoint_disconnect(backend, callback);
+ }
+}
+
+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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,53 @@
+/*****************************************************************************/
+/* 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_endpoint_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_endpoint_disconnect
+ (struct usbback_device *device, xenidc_callback * callback);
+
+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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,554 @@
+/*****************************************************************************/
+/* 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 <asm-xen/xen-public/io/usbif.h>
+#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
+
+typedef struct usbback_driver_backend_callback_struct
+ usbback_driver_backend_callback;
+
+struct usbback_driver_backend_callback_struct {
+ xenidc_callback callback;
+ struct usbback_driver_backend *backend;
+};
+
+struct usbback_driver_backend {
+ struct usbback_device *device;
+ spinlock_t lock;
+ xenidc_callback *endpoint_disconnect_callback;
+ int port_disconnect_callbacks_out;
+ usbback_driver_backend_callback port_disconnect_callback
+ [USBBACK_DRIVER_PORT_COUNT];
+ struct usbback_driver_port port[USBBACK_DRIVER_PORT_COUNT];
+};
+
+static void usbback_driver_backend_endpoint_disconnect_1
+ (xenidc_callback * callback);
+
+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;
+
+ spin_lock_init(&backend->lock);
+
+ {
+ int i;
+
+ for (i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++) {
+ xenidc_callback_init
+ (&backend->port_disconnect_callback[i].
+ callback,
+ usbback_driver_backend_endpoint_disconnect_1);
+
+ backend->port_disconnect_callback[i].backend =
+ backend;
+ }
+ }
+
+ {
+ 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_endpoint_connect
+ (struct usbback_driver_backend *backend) {
+ trace();
+
+ {
+ int i;
+
+ for (i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++) {
+ usbback_driver_port_endpoint_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(); */
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ 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_PROTOCOL);
+ }
+}
+
+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_PROTOCOL;
+
+ 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_PROTOCOL;
+
+ 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_PROTOCOL;
+
+ 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_PROTOCOL;
+
+ 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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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_PROTOCOL;
+
+ 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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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_endpoint_disconnect
+ (struct usbback_driver_backend *backend, xenidc_callback * callback) {
+ trace();
+
+ backend->endpoint_disconnect_callback = callback;
+
+ backend->port_disconnect_callbacks_out = USBBACK_DRIVER_PORT_COUNT;
+
+ {
+ int i;
+
+ for (i = 0; i < USBBACK_DRIVER_PORT_COUNT; i++) {
+ usbback_driver_port_endpoint_disconnect
+ (&backend->port[i],
+ &backend->port_disconnect_callback[i].callback);
+ }
+ }
+}
+
+static void usbback_driver_backend_endpoint_disconnect_1
+ (xenidc_callback * callback) {
+ trace();
+
+ {
+ struct usbback_driver_backend *backend = container_of
+ (callback, usbback_driver_backend_callback,
+ callback)->backend;
+
+ spin_lock(&backend->lock);
+
+ if (--backend->port_disconnect_callbacks_out == 0) {
+ xenidc_callback_success(backend->
+ endpoint_disconnect_callback);
+ }
+
+ spin_unlock(&backend->lock);
+ }
+}
+
+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);
+}
+
+struct usbback_device *usbback_driver_backend_query_device
+ (struct usbback_driver_backend *backend) {
+ trace();
+
+ return backend->device;
+}
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_backend.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,61 @@
+/*****************************************************************************/
+/* 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_endpoint_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_endpoint_disconnect
+ (struct usbback_driver_backend *backend, xenidc_callback * callback);
+
+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);
+
+extern struct usbback_device *usbback_driver_backend_query_device
+ (struct usbback_driver_backend *backend);
+
+#endif
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,1369 @@
+/*****************************************************************************/
+/* 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 <linux/rwsem.h>
+#include <asm-xen/xen-public/io/usbif.h>
+#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_perform_release_1(void *data);
+
+static int usbback_driver_port_init_or_exit
+ (struct usbback_driver_port *port, int exit) {
+ trace();
+
+ {
+ int return_value = 0;
+
+ if (exit) {
+ goto EXIT;
+ }
+
+ 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->free_resource_list);
+
+ {
+ int i;
+
+ for (i = 0; i < USBBACK_DRIVER_PORT_RESOURCE_COUNT; i++) {
+ struct usbback_driver_port_resource *resource =
+ &port->resources[i];
+
+ return_value =
+ usbback_driver_port_resource_init(resource,
+ port);
+
+ if (return_value != 0) {
+ goto EXIT_NO_RESOURCE;
+ }
+
+ list_add(&resource->link,
+ &port->free_resource_list);
+ }
+ }
+
+ xenidc_work_init
+ (&port->perform_release_1_work,
+ usbback_driver_port_perform_release_1, port);
+
+ return 0;
+
+ EXIT:
+
+ EXIT_NO_RESOURCE:
+
+ while (!list_empty(&port->free_resource_list)) {
+ struct usbback_driver_port_resource *resource =
+ list_entry(port->free_resource_list.next,
+ struct usbback_driver_port_resource,
+ link);
+
+ list_del_init(&resource->link);
+
+ usbback_driver_port_resource_exit(resource);
+ }
+
+ return return_value;
+ }
+}
+
+int usbback_driver_port_init
+ (struct usbback_driver_port *port, struct usbback_driver_backend *backend) {
+ trace();
+
+ memset(port, 0, sizeof(*port));
+
+ port->backend = backend;
+
+ return usbback_driver_port_init_or_exit(port, 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_endpoint_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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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_endpoint_disconnect
+ (struct usbback_driver_port *port, xenidc_callback * callback) {
+ trace();
+
+ {
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ port->endpoint_disconnect_callback = callback;
+
+ 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();
+
+ (void)usbback_driver_port_init_or_exit(port, 1);
+}
+
+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_endpoint_disconnect
+ (struct usbback_driver_port *port);
+
+static void usbback_driver_port_complete_shutdown
+ (struct usbback_driver_port *port);
+
+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:
+ /* No claimed port */
+ /* USB disconnected */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pc:
+ port->state = usbback_driver_port_state_i_pc;
+ break;
+ case usbback_driver_port_stimulus_cn:
+ port->state = usbback_driver_port_state_i_cn;
+ break;
+ case usbback_driver_port_stimulus_sd:
+ port->state = usbback_driver_port_state_i_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 port */
+ /* USB disconnected */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ 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_cn:
+ port->state = usbback_driver_port_state_i_pc_cn;
+ break;
+ case usbback_driver_port_stimulus_sd:
+ port->state = usbback_driver_port_state_i_sd;
+ usbback_driver_port_complete_shutdown(port);
+ break;
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_cn:
+ /* No claimed port */
+ /* USB disconnected */
+ /* Endpoint connected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pc:
+ port->state = usbback_driver_port_state_i_pc_cn;
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state = usbback_driver_port_state_i;
+ usbback_driver_port_complete_endpoint_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_sd:
+ /* Removed from port list. */
+ /* USB disconnected */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ /* Shutdown */
+ switch (stimulus) {
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_pc_up:
+ /* Claimed port */
+ /* USB probed */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state = usbback_driver_port_state_i_pc_up_pr;
+ usbback_driver_port_perform_release(port);
+ break;
+ case usbback_driver_port_stimulus_ud:
+ port->state = usbback_driver_port_state_i_pc;
+ usbback_driver_port_complete_usb_disconnect(port);
+ break;
+ case usbback_driver_port_stimulus_cn:
+ port->state = usbback_driver_port_state_i_pc_up_cn;
+ break;
+ case usbback_driver_port_stimulus_sd:
+ port->state = usbback_driver_port_state_i_pc_up_sd;
+ usbback_driver_port_perform_release(port);
+ break;
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_pc_cn:
+ /* Claimed port */
+ /* USB disconnected */
+ /* Endpoint connected */
+ /* I/Os idle */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state = usbback_driver_port_state_i_cn;
+ usbback_driver_port_complete_release(port);
+ break;
+ case usbback_driver_port_stimulus_up:
+ port->state = usbback_driver_port_state_i_pc_up_cn;
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state = usbback_driver_port_state_i_pc;
+ usbback_driver_port_complete_endpoint_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:
+ /* Releasing port. */
+ /* USB probed */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_ud:
+ port->state = usbback_driver_port_state_i_pc_up_pr_ud;
+ usbback_driver_port_complete_usb_disconnect(port);
+ break;
+ case usbback_driver_port_stimulus_cn:
+ port->state = usbback_driver_port_state_i_pc_up_pr_cn;
+ break;
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_pc_up_cn:
+ /* Claimed port */
+ /* USB probed */
+ /* Endpoint connected */
+ /* Maybe I/Os in progress */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state = usbback_driver_port_state_i_pc_up_cn_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_cn_ud;
+ usbback_driver_port_purge_io(port);
+ usbback_driver_port_test_io(port);
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state = usbback_driver_port_state_i_pc_up_cn_dn;
+ 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;
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_pc_up_sd:
+ /* Releasing port */
+ /* USB probed */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_ud:
+ port->state = usbback_driver_port_state_i_pc_up_sd_ud;
+ usbback_driver_port_complete_usb_disconnect(port);
+ break;
+ default:
+ usbback_driver_port_invalid_stimulus(port, stimulus);
+ break;
+ }
+ break;
+ case usbback_driver_port_state_i_pc_up_pr_ud:
+ /* Releasing port. */
+ /* USB disconnected */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_cn:
+ port->state =
+ usbback_driver_port_state_i_pc_up_pr_ud_cn;
+ 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_pr_cn:
+ /* Releasing port. */
+ /* USB probed */
+ /* Endpoint connected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_ud:
+ port->state =
+ usbback_driver_port_state_i_pc_up_pr_ud_cn;
+ usbback_driver_port_complete_usb_disconnect(port);
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state = usbback_driver_port_state_i_pc_up_pr;
+ usbback_driver_port_complete_endpoint_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_cn_pr:
+ /* Claimed port */
+ /* USB probed */
+ /* Endpoint connected */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_ud:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_ud;
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_dn;
+ 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_cn;
+ 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_cn_ud:
+ /* Claimed port */
+ /* USB disconnecting */
+ /* Endpoint connected */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_ud;
+ break;
+ case usbback_driver_port_stimulus_dn:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_ud_dn;
+ 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_cn;
+ 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_cn_dn:
+ /* Claimed port */
+ /* USB probed */
+ /* Endpoint disconnecting */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_dn;
+ break;
+ case usbback_driver_port_stimulus_ud:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_ud_dn;
+ 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;
+ usbback_driver_port_complete_endpoint_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_ud:
+ /* Releasing port */
+ /* USB disconnected */
+ /* Endpoint disconnected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release not outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_rp:
+ port->state = usbback_driver_port_state_i_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_up_pr_ud_cn:
+ /* Releasing port. */
+ /* USB disconnected */
+ /* Endpoint connected */
+ /* I/Os idle */
+ /* Performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_dn:
+ port->state = usbback_driver_port_state_i_pc_up_pr_ud;
+ usbback_driver_port_complete_endpoint_disconnect(port);
+ break;
+ 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_cn;
+ 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_cn_pr_ud:
+ /* Claimed port */
+ /* USB disconnecting */
+ /* Endpoint connected */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release outstanding */
+ /* USB disconnect outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_dn:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_ud_dn;
+ 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_cn;
+ usbback_driver_port_complete_usb_disconnect(port);
+ usbback_driver_port_complete_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_cn_pr_dn:
+ /* Claimed port */
+ /* USB probed */
+ /* Endpoint disconnecting */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release outstanding */
+ /* USB disconnect not outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_ud:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_ud_dn;
+ 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;
+ usbback_driver_port_perform_release(port);
+ usbback_driver_port_complete_endpoint_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_cn_ud_dn:
+ /* Claimed port */
+ /* USB disconnecting */
+ /* Endpoint disconnecting */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release not outstanding */
+ /* USB disconnect outstanding */
+ /* Endpoint disconnect not outstanding */
+ /* Shutdown not outstanding */
+ switch (stimulus) {
+ case usbback_driver_port_stimulus_pr:
+ port->state =
+ usbback_driver_port_state_i_pc_up_cn_pr_ud_dn;
+ 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;
+ usbback_driver_port_complete_usb_disconnect(port);
+ usbback_driver_port_complete_endpoint_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_cn_pr_ud_dn:
+ /* Claimed port */
+ /* USB disconnecting */
+ /* Endpoint disconnecting */
+ /* I/Os in progress/testing I/Os */
+ /* Not performing release */
+ /* Release outstanding */
+ /* USB disconnect outstanding */
+ /* Endpoint disconnect outstanding */
+ /* Shutdown not outstanding */
+ 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;
+ usbback_driver_port_complete_usb_disconnect(port);
+ usbback_driver_port_complete_endpoint_disconnect(port);
+ usbback_driver_port_complete_release(port);
+ break;
+ case usbback_driver_port_stimulus_rr:
+ 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));
+
+ xenidc_endpoint_transaction_complete
+ (transaction, USBIF_XENIDC_ERROR_UNLINKED);
+ }
+
+ {
+ 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);
+ }
+ }
+}
+
+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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ 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_endpoint_disconnect
+ (struct usbback_driver_port *port) {
+ trace();
+
+ xenidc_callback_success(port->endpoint_disconnect_callback);
+}
+
+static void usbback_driver_port_complete_shutdown
+ (struct usbback_driver_port *port) {
+ trace();
+
+ port->shutdown = 1;
+
+ xenidc_work_wake_up();
+}
+
+struct usbback_driver_backend *usbback_driver_port_query_backend
+ (struct usbback_driver_port *port) {
+ trace();
+
+ return port->backend;
+}
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,144 @@
+/*****************************************************************************/
+/* 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 <asm-xen/xenidc.h>
+#include <linux/usb.h>
+#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_endpoint_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_endpoint_disconnect
+ (struct usbback_driver_port *port, xenidc_callback * callback);
+
+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_cn,
+ usbback_driver_port_state_i_sd,
+ usbback_driver_port_state_i_pc_up,
+ usbback_driver_port_state_i_pc_cn,
+ usbback_driver_port_state_i_pc_up_pr,
+ usbback_driver_port_state_i_pc_up_cn,
+ 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_cn,
+ usbback_driver_port_state_i_pc_up_cn_pr,
+ usbback_driver_port_state_i_pc_up_cn_ud,
+ usbback_driver_port_state_i_pc_up_cn_dn,
+ usbback_driver_port_state_i_pc_up_sd_ud,
+ usbback_driver_port_state_i_pc_up_pr_ud_cn,
+ usbback_driver_port_state_i_pc_up_cn_pr_ud,
+ usbback_driver_port_state_i_pc_up_cn_pr_dn,
+ usbback_driver_port_state_i_pc_up_cn_ud_dn,
+ usbback_driver_port_state_i_pc_up_cn_pr_ud_dn
+} 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 free_resource_list;
+ unsigned long current_transactions;
+ struct usbback_driver_port_resource resources
+ [USBBACK_DRIVER_PORT_RESOURCE_COUNT];
+ xenidc_work perform_release_1_work;
+ xenidc_callback *endpoint_disconnect_callback;
+ 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);
+
+struct usbback_driver_backend *usbback_driver_port_query_backend
+ (struct usbback_driver_port *port);
+
+#endif
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,1156 @@
+/*****************************************************************************/
+/* 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 <asm-xen/xen-public/io/usbif.h>
+#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();
+
+ {
+ xenidc_buffer_resource_list sum =
+ xenidc_buffer_resource_list_null();
+
+ xenidc_buffer_resource_list bulk_list =
+ xenidc_grant_table_calculate_buffer_resource_list
+ (USBIF_MAX_BULK_BYTE_COUNT, USBIF_BULK_BYTE_ALIGNMENT);
+
+ xenidc_buffer_resource_list schedule_list =
+ xenidc_grant_table_calculate_buffer_resource_list
+ (USBIF_MAX_SCHEDULE_BYTE_COUNT,
+ USBIF_SCHEDULE_BYTE_ALIGNMENT);
+
+ xenidc_buffer_resource_list_plus_equals(&sum, &bulk_list);
+ xenidc_buffer_resource_list_plus_equals(&sum, &schedule_list);
+
+ usbback_driver_port_resource_rbr_mapper_pool =
+ xenidc_allocate_rbr_mapper_pool(sum);
+ }
+
+ 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);
+
+int 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);
+
+ resource->urb =
+ usb_alloc_urb(USBIF_MAX_SCHEDULE_PACKET_COUNT, GFP_KERNEL);
+
+ return (resource->urb != NULL) ? 0 : -ENOMEM;
+}
+
+void usbback_driver_port_resource_exit
+ (struct usbback_driver_port_resource *resource) {
+ trace();
+
+ usb_free_urb(resource->urb);
+}
+
+void usbback_driver_port_resource_start_io
+ (struct usbback_driver_port_resource *resource,
+ xenidc_endpoint_transaction * transaction) {
+ trace();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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_unlinked_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_unlinked_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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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 PROTOCOL_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,
+ (header.direction == USBIF_IO_TRANSACTION_DIRECTION_OUT) ?
+ XENIDC_MAP_RBR_REQUEST_ELEMENT_ACCESS_FLAGS_READ :
+ XENIDC_MAP_RBR_REQUEST_ELEMENT_ACCESS_FLAGS_WRITE);
+
+ 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 PROTOCOL_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_MAP_RBR_REQUEST_ELEMENT_ACCESS_FLAGS_READ |
+ XENIDC_MAP_RBR_REQUEST_ELEMENT_ACCESS_FLAGS_WRITE);
+ }
+
+ xenidc_rbr_mapper_pool_reserve_and_map_rbrs
+ (usbback_driver_port_resource_rbr_mapper_pool,
+ &resource->reserve_and_map_rbr_request,
+ usbback_device_query_address
+ (usbback_driver_backend_query_device
+ (usbback_driver_port_query_backend(resource->port))
+ )
+ );
+ }
+
+ return;
+
+ PROTOCOL_ERROR:
+
+ resource->transaction_error = XENIDC_ERROR_INVALID_PROTOCOL;
+
+ 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
+ (&resource->reserve_and_map_rbr_request,
+ USBIF_XENIDC_ERROR_UNLINKED);
+}
+
+static void usbback_driver_port_resource_map_callback
+ (xenidc_callback * callback) {
+ trace();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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 {
+ trace0("failed to map FE buffer");
+
+ 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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ xenidc_callback_serialiser_complete_callback
+ (&usbback_driver_port_resource_submit_urb_serialiser,
+ &resource->submit_urb_1_callback, XENIDC_ERROR_SUCCESS);
+}
+
+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();
+
+ /* MUST MAINTAIN RELATIVE REQUEST ORDER ON THE SUBMISSION PATH */
+
+ {
+ 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_PROTOCOL;
+
+ goto PROTOCOL_ERROR;
+ }
+
+ if (io_parameters.header.io_transaction_type
+ >= USBIF_IO_TRANSACTION_TYPE_LIMIT) {
+ resource->transaction_error =
+ XENIDC_ERROR_INVALID_PARAMETER;
+
+ goto PROTOCOL_ERROR;
+ }
+
+ if (io_parameters_byte_count
+ <
+ usbif_io_parameters_byte_count
+ [io_parameters.header.io_transaction_type]
+ ) {
+ resource->transaction_error =
+ XENIDC_ERROR_INVALID_PROTOCOL;
+
+ goto PROTOCOL_ERROR;
+ }
+ }
+
+ trace1("io type:%d",
+ (int)io_parameters.header.io_transaction_type);
+ trace1("dev num:%d", (int)io_parameters.header.device_number);
+ trace1("endpoint:%d", (int)io_parameters.header.endpoint);
+ trace1("direction:%d", (int)io_parameters.header.direction);
+ trace1("unlink_id:%d", (int)io_parameters.header.unlink_id);
+ trace1("flags:%d", (int)io_parameters.header.flags);
+ trace1("rbr type:%d", (int)io_parameters.header.rbr.type);
+ trace1("rbr offset:%d",
+ (int)io_parameters.header.rbr.byte_offset);
+ trace1("rbr count:%d",
+ (int)io_parameters.header.rbr.byte_count);
+
+ 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) {
+ trace1("Set address:%d",
+ (int)le16_to_cpu(ctrl->wValue));
+
+ 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;
+
+ 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)
+ ? (unsigned int)USB_DIR_OUT : (unsigned int)
+ USB_DIR_IN)
+ | ((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)
+ );
+
+ trace1("pipe:%x", (int)pipe);
+
+ 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);
+
+ trace0("control URB");
+
+ trace1("setup[0]:%x", (int)resource->setup[0]);
+ trace1("setup[1]:%x", (int)resource->setup[1]);
+ trace1("setup[2]:%x", (int)resource->setup[2]);
+ trace1("setup[3]:%x", (int)resource->setup[3]);
+ trace1("setup[4]:%x", (int)resource->setup[4]);
+ trace1("setup[5]:%x", (int)resource->setup[5]);
+ trace1("setup[6]:%x", (int)resource->setup[6]);
+ trace1("setup[7]:%x", (int)resource->setup[7]);
+
+ trace1
+ ("mapping:%x",
+ (int)resource->request_element[0].mapping);
+
+ trace1
+ ("count:%x",
+ (int)
+ xenidc_remote_buffer_reference_query_byte_count
+ (&resource->request_element[0].rbr)
+ );
+ } 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);
+
+ trace0("bulk URB");
+ } 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;
+
+ trace0("interrupt URB");
+
+ trace1("interval:%d",
+ 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 (io_parameters.isochronous.packet_count
+ > USBIF_MAX_SCHEDULE_PACKET_COUNT) {
+ resource->transaction_error =
+ XENIDC_ERROR_TOO_BIG;
+
+ goto SCHEDULE_ERROR;
+ }
+
+ 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_PARAMETER;
+
+ 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;
+ }
+
+ trace0("isochronous URB");
+
+ trace1("interval:%d",
+ io_parameters.isochronous.interval);
+
+ trace1("packet_count:%d",
+ io_parameters.isochronous.packet_count);
+ }
+
+ /* 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;
+ }
+
+ resource->transaction_error =
+ usbif_error_map_local_to(usb_submit_urb
+ (urb, GFP_KERNEL));
+
+ if (resource->transaction_error != XENIDC_ERROR_SUCCESS) {
+ printk(KERN_WARNING
+ "URB %p submission failed.\n", urb);
+
+ 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:
+
+ HANDLED_SPECIAL_CASE:
+
+ PROTOCOL_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;
+
+ resource->transaction_error =
+ usbif_error_map_local_to(urb->status);
+
+ if (resource->transaction_error != XENIDC_ERROR_SUCCESS) {
+ printk
+ (KERN_WARNING "URB %p failed. Status %d\n", urb,
+ urb->status);
+ }
+
+ 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 =
+ usbif_error_map_local_to(urb->
+ iso_frame_desc[i].
+ status);
+
+ 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);
+ }
+ }
+}
+
+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_unlinked_error
+ (struct usbback_driver_port_resource *resource) {
+ trace();
+
+ resource->transaction_error = USBIF_XENIDC_ERROR_UNLINKED;
+}
+
+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
+ (&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_PROTOCOL;
+ }
+
+ xenidc_endpoint_transaction_complete
+ (resource->completing_transaction,
+ resource->transaction_error);
+
+ usbback_driver_port_resource_completed_io(resource->port,
+ resource);
+ }
+}
diff -r 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_driver_port_resource.h Mon Nov 21 11:10:19 2005
@@ -0,0 +1,83 @@
+/*****************************************************************************/
+/* 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 <asm-xen/xenidc.h>
+#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;
+
+int usbback_driver_port_resource_init
+ (struct usbback_driver_port_resource *resource,
+ struct usbback_driver_port *port);
+
+void usbback_driver_port_resource_exit
+ (struct usbback_driver_port_resource *resource);
+
+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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_module.c
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_module.c Mon Nov 21 11:10:19 2005
@@ -0,0 +1,80 @@
+/*****************************************************************************/
+/* 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 <linux/module.h>
+#include <linux/usb.h>
+#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 941c7dbc1353 -r 5a90f01cb37e linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/linux-2.6-xen-sparse/drivers/xen/usbback/usbback_trace.h Mon Nov 21 11:10:19 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 <linux/config.h>
+#include <linux/kernel.h>
+
+#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 941c7dbc1353 -r 5a90f01cb37e patches/linux-2.6.12/usb.patch
--- /dev/null Mon Nov 21 11:09:54 2005
+++ b/patches/linux-2.6.12/usb.patch Mon Nov 21 11:10:19 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");
[-- Attachment #3: Type: text/plain, Size: 138 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2005-11-21 13:19 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-11-21 13:19 [PATCH][15/17] USB virt 2.6 split driver---USB split driver back-end harry
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.