From: harry <harry@hebutterworth.freeserve.co.uk>
To: xen-devel@lists.xensource.com
Subject: [PATCH][15/17] USB virt 2.6 split driver---USB split driver back-end
Date: Mon, 21 Nov 2005 13:19:21 +0000 [thread overview]
Message-ID: <1132579161.31295.126.camel@localhost.localdomain> (raw)
[-- 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
reply other threads:[~2005-11-21 13:19 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1132579161.31295.126.camel@localhost.localdomain \
--to=harry@hebutterworth.freeserve.co.uk \
--cc=xen-devel@lists.xensource.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.