ia64/linux-2.6.18-xen.hg

changeset 828:8f996719f2ff

PVUSB: frontend driver

Signed-off-by: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Mar 18 11:42:51 2009 +0000 (2009-03-18)
parents 71aedaa9e21c
children f799db0570f2
files drivers/xen/usbfront/Makefile drivers/xen/usbfront/usbfront-dbg.c drivers/xen/usbfront/usbfront-hcd.c drivers/xen/usbfront/usbfront-hub.c drivers/xen/usbfront/usbfront-q.c drivers/xen/usbfront/usbfront.h drivers/xen/usbfront/xenbus.c
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/drivers/xen/usbfront/Makefile	Wed Mar 18 11:42:51 2009 +0000
     1.3 @@ -0,0 +1,7 @@
     1.4 +obj-$(CONFIG_XEN_USB_FRONTEND) := xen-hcd.o
     1.5 +
     1.6 +xen-hcd-y   := usbfront-hcd.o xenbus.o
     1.7 +
     1.8 +ifeq ($(CONFIG_XEN_USB_FRONTEND_HCD_STATS),y)
     1.9 +EXTRA_CFLAGS += -DXENHCD_STATS
    1.10 +endif
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/drivers/xen/usbfront/usbfront-dbg.c	Wed Mar 18 11:42:51 2009 +0000
     2.3 @@ -0,0 +1,99 @@
     2.4 +/*
     2.5 + * usbfront-dbg.c
     2.6 + *
     2.7 + * Xen USB Virtual Host Controller - debugging
     2.8 + *
     2.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    2.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    2.11 + *
    2.12 + * This program is free software; you can redistribute it and/or modify
    2.13 + * it under the terms of the GNU General Public License as published by
    2.14 + * the Free Software Foundation; either version 2 of the License, or
    2.15 + * (at your option) any later version.
    2.16 + *
    2.17 + * This program is distributed in the hope that it will be useful,
    2.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.20 + * GNU General Public License for more details.
    2.21 + *
    2.22 + * You should have received a copy of the GNU General Public License
    2.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    2.24 + *
    2.25 + * or,
    2.26 + *
    2.27 + * When distributed separately from the Linux kernel or incorporated into
    2.28 + * other software packages, subject to the following license:
    2.29 + *
    2.30 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    2.31 + * of this software and associated documentation files (the "Software"), to
    2.32 + * deal in the Software without restriction, including without limitation the
    2.33 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    2.34 + * sell copies of the Software, and to permit persons to whom the Software is
    2.35 + * furnished to do so, subject to the following conditions:
    2.36 + *
    2.37 + * The above copyright notice and this permission notice shall be included in
    2.38 + * all copies or substantial portions of the Software.
    2.39 + *
    2.40 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    2.41 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    2.42 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    2.43 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    2.44 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    2.45 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    2.46 + * DEALINGS IN THE SOFTWARE.
    2.47 + */
    2.48 +
    2.49 +static ssize_t show_statistics(struct class_device *class_dev, char *buf)
    2.50 +{
    2.51 +	struct usb_bus *bus;
    2.52 +	struct usb_hcd *hcd;
    2.53 +	struct usbfront_info *info;
    2.54 +	unsigned long flags;
    2.55 +	unsigned temp, size;
    2.56 +	char *next;
    2.57 +
    2.58 +	bus = class_get_devdata(class_dev);
    2.59 +	hcd = bus->hcpriv;
    2.60 +	info = hcd_to_info(hcd);
    2.61 +	next = buf;
    2.62 +	size = PAGE_SIZE;
    2.63 +
    2.64 +	spin_lock_irqsave(&info->lock, flags);
    2.65 +
    2.66 +	temp = scnprintf (next, size,
    2.67 +			"bus %s, device %s\n"
    2.68 +			"%s\n"
    2.69 +			"xenhcd, hcd state %d\n",
    2.70 +			hcd->self.controller->bus->name,
    2.71 +			hcd->self.controller->bus_id,
    2.72 +			hcd->product_desc,
    2.73 +			hcd->state);
    2.74 +	size -= temp;
    2.75 +	next += temp;
    2.76 +
    2.77 +#ifdef XENHCD_STATS
    2.78 +	temp = scnprintf(next, size,
    2.79 +		"complete %ld unlink %ld ring_full %ld\n",
    2.80 +		info->stats.complete, info->stats.unlink, info->stats.ring_full);
    2.81 +	size -= temp;
    2.82 +	next += temp;
    2.83 +#endif
    2.84 +
    2.85 +	spin_unlock_irqrestore(&info->lock, flags);
    2.86 +
    2.87 +	return PAGE_SIZE - size;
    2.88 +}
    2.89 +
    2.90 +static CLASS_DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
    2.91 +
    2.92 +static inline void create_debug_file(struct usbfront_info *info)
    2.93 +{
    2.94 +	struct class_device *cldev = info_to_hcd(info)->self.class_dev;
    2.95 +	class_device_create_file(cldev, &class_device_attr_statistics);
    2.96 +}
    2.97 +
    2.98 +static inline void remove_debug_file(struct usbfront_info *info)
    2.99 +{
   2.100 +	struct class_device *cldev = info_to_hcd(info)->self.class_dev;
   2.101 +	class_device_remove_file(cldev, &class_device_attr_statistics);
   2.102 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/drivers/xen/usbfront/usbfront-hcd.c	Wed Mar 18 11:42:51 2009 +0000
     3.3 @@ -0,0 +1,279 @@
     3.4 +/*
     3.5 + * usbfront-hcd.c
     3.6 + *
     3.7 + * Xen USB Virtual Host Controller driver
     3.8 + *
     3.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    3.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    3.11 + *
    3.12 + * This program is free software; you can redistribute it and/or modify
    3.13 + * it under the terms of the GNU General Public License as published by
    3.14 + * the Free Software Foundation; either version 2 of the License, or
    3.15 + * (at your option) any later version.
    3.16 + *
    3.17 + * This program is distributed in the hope that it will be useful,
    3.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.20 + * GNU General Public License for more details.
    3.21 + *
    3.22 + * You should have received a copy of the GNU General Public License
    3.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    3.24 + *
    3.25 + * or,
    3.26 + *
    3.27 + * When distributed separately from the Linux kernel or incorporated into
    3.28 + * other software packages, subject to the following license:
    3.29 + *
    3.30 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    3.31 + * of this software and associated documentation files (the "Software"), to
    3.32 + * deal in the Software without restriction, including without limitation the
    3.33 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    3.34 + * sell copies of the Software, and to permit persons to whom the Software is
    3.35 + * furnished to do so, subject to the following conditions:
    3.36 + *
    3.37 + * The above copyright notice and this permission notice shall be included in
    3.38 + * all copies or substantial portions of the Software.
    3.39 + *
    3.40 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    3.41 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    3.42 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    3.43 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    3.44 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    3.45 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    3.46 + * DEALINGS IN THE SOFTWARE.
    3.47 + */
    3.48 +
    3.49 +#include "usbfront.h"
    3.50 +#include "usbfront-dbg.c"
    3.51 +#include "usbfront-hub.c"
    3.52 +#include "usbfront-q.c"
    3.53 +
    3.54 +static void xenhcd_watchdog(unsigned long param)
    3.55 +{
    3.56 +	struct usbfront_info *info = (struct usbfront_info *) param;
    3.57 +	unsigned long flags;
    3.58 +
    3.59 +	spin_lock_irqsave(&info->lock, flags);
    3.60 +	if (HC_IS_RUNNING(info_to_hcd(info)->state)) {
    3.61 +		timer_action_done(info, TIMER_RING_WATCHDOG);
    3.62 +		xenhcd_giveback_unlinked_urbs(info);
    3.63 +		xenhcd_kick_pending_urbs(info);
    3.64 +	}
    3.65 +	spin_unlock_irqrestore(&info->lock, flags);
    3.66 +}
    3.67 +
    3.68 +/*
    3.69 + * one-time HC init
    3.70 + */
    3.71 +static int xenhcd_setup(struct usb_hcd *hcd)
    3.72 +{
    3.73 +	struct usbfront_info *info = hcd_to_info(hcd);
    3.74 +
    3.75 +	spin_lock_init(&info->lock);
    3.76 +	INIT_LIST_HEAD(&info->pending_urbs);
    3.77 +	INIT_LIST_HEAD(&info->inprogress_urbs);
    3.78 +	INIT_LIST_HEAD(&info->unlinked_urbs);
    3.79 +	init_timer(&info->watchdog);
    3.80 +	info->watchdog.function = xenhcd_watchdog;
    3.81 +	info->watchdog.data = (unsigned long) info;
    3.82 +	return 0;
    3.83 +}
    3.84 +
    3.85 +/*
    3.86 + * start HC running
    3.87 + */
    3.88 +static int xenhcd_run(struct usb_hcd *hcd)
    3.89 +{
    3.90 +	hcd->uses_new_polling = 1;
    3.91 +	hcd->poll_rh = 0;
    3.92 +	hcd->state = HC_STATE_RUNNING;
    3.93 +	create_debug_file(hcd_to_info(hcd));
    3.94 +	return 0;
    3.95 +}
    3.96 +
    3.97 +/*
    3.98 + * stop running HC
    3.99 + */
   3.100 +static void xenhcd_stop(struct usb_hcd *hcd)
   3.101 +{
   3.102 +	struct usbfront_info *info = hcd_to_info(hcd);
   3.103 +
   3.104 +	del_timer_sync(&info->watchdog);
   3.105 +	remove_debug_file(info);
   3.106 +	spin_lock_irq(&info->lock);
   3.107 +	/*
   3.108 +	 * TODO: port power off, cancel all urbs.
   3.109 +	 */
   3.110 +
   3.111 +	if (HC_IS_RUNNING(hcd->state))
   3.112 +		hcd->state = HC_STATE_HALT;
   3.113 +	spin_unlock_irq(&info->lock);
   3.114 +}
   3.115 +
   3.116 +/*
   3.117 + * TODO: incomplete suspend/resume functions!
   3.118 + */
   3.119 +#if 0
   3.120 +#ifdef CONFIG_PM
   3.121 +/*
   3.122 + * suspend running HC
   3.123 + */
   3.124 +static int xenhcd_suspend(struct usb_hcd *hcd, pm_message_t message)
   3.125 +{
   3.126 +	struct usbfront_info *info = hcd_to_info(hcd);
   3.127 +	unsigned long flags;
   3.128 +	int ret = 0;
   3.129 +
   3.130 +	spin_lock_irqsave(&info->lock, flags);
   3.131 +	if (hcd->state != HC_STATE_SUSPENDED) {
   3.132 +		ret = -EINVAL;
   3.133 +		goto done;
   3.134 +	}
   3.135 +
   3.136 +	/*
   3.137 +	 * TODO:
   3.138 +	 * 	canceling all transfer, clear all hc queue,
   3.139 +	 * 	stop kthread,
   3.140 +	 */
   3.141 +
   3.142 +	clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
   3.143 +done:
   3.144 +	spin_unlock_irqrestore(&info->lock, flags);
   3.145 +
   3.146 +	return ret;
   3.147 +}
   3.148 +
   3.149 +/*
   3.150 + * resume HC
   3.151 + */
   3.152 +static int xenhcd_resume(struct usb_hcd *hcd)
   3.153 +{
   3.154 +	struct usbfront_info *info = hcd_to_info(hcd);
   3.155 +	int ret = -EINVAL;
   3.156 +
   3.157 +	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
   3.158 +
   3.159 +	/*
   3.160 +	 * TODO:
   3.161 +	 * 	re-init HC.
   3.162 +	 * 	resume all roothub ports.
   3.163 +	 */
   3.164 +
   3.165 +	return ret;
   3.166 +}
   3.167 +#endif
   3.168 +#endif
   3.169 +
   3.170 +/*
   3.171 + * called as .urb_enqueue()
   3.172 + * non-error returns are promise to giveback the urb later
   3.173 + */
   3.174 +static int xenhcd_urb_enqueue(struct usb_hcd *hcd,
   3.175 +				    struct usb_host_endpoint *ep,
   3.176 +				    struct urb *urb,
   3.177 +				    gfp_t mem_flags)
   3.178 +{
   3.179 +	struct usbfront_info *info = hcd_to_info(hcd);
   3.180 +	struct urb_priv *urbp;
   3.181 +	unsigned long flags;
   3.182 +	int ret = 0;
   3.183 +
   3.184 +	spin_lock_irqsave(&info->lock, flags);
   3.185 +
   3.186 +	urbp = alloc_urb_priv(urb);
   3.187 +	if (!urbp) {
   3.188 +		ret = -ENOMEM;
   3.189 +		goto done;
   3.190 +	}
   3.191 +
   3.192 +	ret = xenhcd_submit_urb(info, urbp);
   3.193 +	if (ret != 0)
   3.194 +		free_urb_priv(urbp);
   3.195 +
   3.196 +done:
   3.197 +	spin_unlock_irqrestore(&info->lock, flags);
   3.198 +	return ret;
   3.199 +}
   3.200 +
   3.201 +/*
   3.202 + * called as .urb_dequeue()
   3.203 + *
   3.204 + * just mark the urb as unlinked
   3.205 + * if the urb is in pending_urbs, move to unlinked_urbs
   3.206 + * TODO:
   3.207 + * 	canceling the urb transfer in backend
   3.208 + */
   3.209 +static int xenhcd_urb_dequeue(struct usb_hcd *hcd,
   3.210 +				    struct urb *urb)
   3.211 +{
   3.212 +	struct usbfront_info *info = hcd_to_info(hcd);
   3.213 +	struct urb_priv *urbp;
   3.214 +	unsigned long flags;
   3.215 +	int ret = 0;
   3.216 +
   3.217 +	spin_lock_irqsave(&info->lock, flags);
   3.218 +
   3.219 +	urbp = urb->hcpriv;
   3.220 +	if (!urbp)
   3.221 +		goto done;
   3.222 +
   3.223 +	ret = xenhcd_unlink_urb(info, urbp);
   3.224 +
   3.225 +done:
   3.226 +	spin_unlock_irqrestore(&info->lock, flags);
   3.227 +	return ret;
   3.228 +}
   3.229 +
   3.230 +/*
   3.231 + * called from usb_get_current_frame_number(),
   3.232 + * but, almost all drivers not use such function.
   3.233 + */
   3.234 +static int xenhcd_get_frame(struct usb_hcd *hcd)
   3.235 +{
   3.236 +	/* it means error, but probably no problem :-) */
   3.237 +	return 0;
   3.238 +}
   3.239 +
   3.240 +/*
   3.241 + * TODO:
   3.242 + * suspend/resume whole hcd and roothub
   3.243 + */
   3.244 +static const char hcd_name[] = "xen_hcd";
   3.245 +
   3.246 +struct hc_driver usbfront_hc_driver = {
   3.247 +	.description = hcd_name,
   3.248 +	.product_desc = DRIVER_DESC,
   3.249 +	.hcd_priv_size = sizeof(struct usbfront_info),
   3.250 +	.flags = HCD_USB2,
   3.251 +
   3.252 +	/*
   3.253 +	 * basic HC lifecycle operations
   3.254 +	 */
   3.255 +	.reset = xenhcd_setup,
   3.256 +	.start = xenhcd_run,
   3.257 +	.stop = xenhcd_stop,
   3.258 +#if 0
   3.259 +#ifdef CONFIG_PM
   3.260 +	.suspend = xenhcd_suspend,
   3.261 +	.resume = xenhcd_resume,
   3.262 +#endif
   3.263 +#endif
   3.264 +	/*
   3.265 +	 * managing urb I/O
   3.266 +	 */
   3.267 +	.urb_enqueue = xenhcd_urb_enqueue,
   3.268 +	.urb_dequeue = xenhcd_urb_dequeue,
   3.269 +	.get_frame_number = xenhcd_get_frame,
   3.270 +
   3.271 +	/*
   3.272 +	 * root hub operations
   3.273 +	 */
   3.274 +	.hub_status_data = xenhcd_hub_status_data,
   3.275 +	.hub_control = xenhcd_hub_control,
   3.276 +#if 0
   3.277 +#ifdef CONFIG_PM
   3.278 +	.bus_suspend = xenhcd_bus_suspend,
   3.279 +	.bus_resume = xenhcd_bus_resume,
   3.280 +#endif
   3.281 +#endif
   3.282 +};
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/drivers/xen/usbfront/usbfront-hub.c	Wed Mar 18 11:42:51 2009 +0000
     4.3 @@ -0,0 +1,479 @@
     4.4 +/*
     4.5 + * usbfront-hub.c
     4.6 + *
     4.7 + * Xen USB Virtual Host Controller - Root Hub Emulations
     4.8 + *
     4.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    4.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    4.11 + *
    4.12 + * This program is free software; you can redistribute it and/or modify
    4.13 + * it under the terms of the GNU General Public License as published by
    4.14 + * the Free Software Foundation; either version 2 of the License, or
    4.15 + * (at your option) any later version.
    4.16 + *
    4.17 + * This program is distributed in the hope that it will be useful,
    4.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.20 + * GNU General Public License for more details.
    4.21 + *
    4.22 + * You should have received a copy of the GNU General Public License
    4.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    4.24 + */
    4.25 +
    4.26 +/*
    4.27 + * set virtual port connection status
    4.28 + */
    4.29 +void set_connect_state(struct usbfront_info *info, int portnum)
    4.30 +{
    4.31 +	int port;
    4.32 +
    4.33 +	port = portnum -1;
    4.34 +	if (info->ports[port].status & USB_PORT_STAT_POWER) {
    4.35 +		switch (info->devices[port].speed) {
    4.36 +		case USB_SPEED_UNKNOWN:
    4.37 +			info->ports[port].status &= ~(USB_PORT_STAT_CONNECTION |
    4.38 +							USB_PORT_STAT_ENABLE |
    4.39 +							USB_PORT_STAT_LOW_SPEED |
    4.40 +							USB_PORT_STAT_HIGH_SPEED |
    4.41 +							USB_PORT_STAT_SUSPEND);
    4.42 +			break;
    4.43 +		case USB_SPEED_LOW:
    4.44 +			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
    4.45 +			info->ports[port].status |= USB_PORT_STAT_LOW_SPEED;
    4.46 +			break;
    4.47 +		case USB_SPEED_FULL:
    4.48 +			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
    4.49 +			break;
    4.50 +		case USB_SPEED_HIGH:
    4.51 +			info->ports[port].status |= USB_PORT_STAT_CONNECTION;
    4.52 +			info->ports[port].status |= USB_PORT_STAT_HIGH_SPEED;
    4.53 +			break;
    4.54 +		default: /* error */
    4.55 +			return;
    4.56 +		}
    4.57 +		info->ports[port].status |= (USB_PORT_STAT_C_CONNECTION << 16);
    4.58 +	}
    4.59 +}
    4.60 +
    4.61 +/*
    4.62 + * set virtual device connection status
    4.63 + */
    4.64 +void rhport_connect(struct usbfront_info *info,
    4.65 +				int portnum, enum usb_device_speed speed)
    4.66 +{
    4.67 +	int port;
    4.68 +
    4.69 +	port = portnum - 1;
    4.70 +	if (info->devices[port].speed != speed) {
    4.71 +		switch (speed) {
    4.72 +		case USB_SPEED_UNKNOWN: /* disconnect */
    4.73 +			info->devices[port].status = USB_STATE_NOTATTACHED;
    4.74 +			break;
    4.75 +		case USB_SPEED_LOW:
    4.76 +		case USB_SPEED_FULL:
    4.77 +		case USB_SPEED_HIGH:
    4.78 +			info->devices[port].status = USB_STATE_ATTACHED;
    4.79 +			break;
    4.80 +		default: /* error */
    4.81 +			return;
    4.82 +		}
    4.83 +		info->devices[port].speed = speed;
    4.84 +		info->ports[port].c_connection = 1;
    4.85 +
    4.86 +		set_connect_state(info, portnum);
    4.87 +	}
    4.88 +}
    4.89 +
    4.90 +void rhport_disconnect(struct usbfront_info *info, int portnum)
    4.91 +{
    4.92 +	rhport_connect(info, portnum, USB_SPEED_UNKNOWN);
    4.93 +}
    4.94 +
    4.95 +void xenhcd_rhport_state_change(struct usbfront_info *info,
    4.96 +				int portnum, enum usb_device_speed speed)
    4.97 +{
    4.98 +	int changed = 0;
    4.99 +	unsigned long flags;
   4.100 +
   4.101 +	if (portnum < 1 || portnum > info->rh_numports)
   4.102 +		return; /* invalid port number */
   4.103 +
   4.104 +	spin_lock_irqsave(&info->lock, flags);
   4.105 +	rhport_connect(info, portnum, speed);
   4.106 +	if (info->ports[portnum-1].c_connection)
   4.107 +		changed = 1;
   4.108 +	spin_unlock_irqrestore(&info->lock, flags);
   4.109 +
   4.110 +	if (changed)
   4.111 +		usb_hcd_poll_rh_status(info_to_hcd(info));
   4.112 +}
   4.113 +
   4.114 +/*
   4.115 + * SetPortFeature(PORT_SUSPENDED)
   4.116 + */
   4.117 +void rhport_suspend(struct usbfront_info *info, int portnum)
   4.118 +{
   4.119 +	int port;
   4.120 +
   4.121 +	port = portnum - 1;
   4.122 +	info->ports[port].status |= USB_PORT_STAT_SUSPEND;
   4.123 +	info->devices[port].status = USB_STATE_SUSPENDED;
   4.124 +}
   4.125 +
   4.126 +/*
   4.127 + * ClearPortFeature(PORT_SUSPENDED)
   4.128 + */
   4.129 +void rhport_resume(struct usbfront_info *info, int portnum)
   4.130 +{
   4.131 +	int port;
   4.132 +
   4.133 +	port = portnum - 1;
   4.134 +	if (info->ports[port].status & USB_PORT_STAT_SUSPEND) {
   4.135 +		info->ports[port].resuming = 1;
   4.136 +		info->ports[port].timeout = jiffies + msecs_to_jiffies(20);
   4.137 +	}
   4.138 +}
   4.139 +
   4.140 +/*
   4.141 + * SetPortFeature(PORT_POWER)
   4.142 + */
   4.143 +void rhport_power_on(struct usbfront_info *info, int portnum)
   4.144 +{
   4.145 +	int port;
   4.146 +
   4.147 +	port = portnum - 1;
   4.148 +	if ((info->ports[port].status & USB_PORT_STAT_POWER) == 0) {
   4.149 +		info->ports[port].status |= USB_PORT_STAT_POWER;
   4.150 +		if (info->devices[port].status != USB_STATE_NOTATTACHED)
   4.151 +			info->devices[port].status = USB_STATE_POWERED;
   4.152 +		if (info->ports[port].c_connection)
   4.153 +			set_connect_state(info, portnum);
   4.154 +	}
   4.155 +}
   4.156 +
   4.157 +/*
   4.158 + * ClearPortFeature(PORT_POWER)
   4.159 + * SetConfiguration(non-zero)
   4.160 + * Power_Source_Off
   4.161 + * Over-current
   4.162 + */
   4.163 +void rhport_power_off(struct usbfront_info *info, int portnum)
   4.164 +{
   4.165 +	int port;
   4.166 +
   4.167 +	port = portnum - 1;
   4.168 +	if (info->ports[port].status & USB_PORT_STAT_POWER) {
   4.169 +		info->ports[port].status = 0;
   4.170 +		if (info->devices[port].status != USB_STATE_NOTATTACHED)
   4.171 +			info->devices[port].status = USB_STATE_ATTACHED;
   4.172 +	}
   4.173 +}
   4.174 +
   4.175 +/*
   4.176 + * ClearPortFeature(PORT_ENABLE)
   4.177 + */
   4.178 +void rhport_disable(struct usbfront_info *info, int portnum)
   4.179 +{
   4.180 +	int port;
   4.181 +
   4.182 +	port = portnum - 1;
   4.183 +	info->ports[port].status &= ~USB_PORT_STAT_ENABLE;
   4.184 +	info->ports[port].status &= ~USB_PORT_STAT_SUSPEND;
   4.185 +	info->ports[port].resuming = 0;
   4.186 +	if (info->devices[port].status != USB_STATE_NOTATTACHED)
   4.187 +		info->devices[port].status = USB_STATE_POWERED;
   4.188 +}
   4.189 +
   4.190 +/*
   4.191 + * SetPortFeature(PORT_RESET)
   4.192 + */
   4.193 +void rhport_reset(struct usbfront_info *info, int portnum)
   4.194 +{
   4.195 +	int port;
   4.196 +
   4.197 +	port = portnum -1;
   4.198 +	info->ports[port].status &= ~(USB_PORT_STAT_ENABLE
   4.199 +					| USB_PORT_STAT_LOW_SPEED
   4.200 +					| USB_PORT_STAT_HIGH_SPEED);
   4.201 +	info->ports[port].status |= USB_PORT_STAT_RESET;
   4.202 +
   4.203 +	if (info->devices[port].status != USB_STATE_NOTATTACHED)
   4.204 +		info->devices[port].status = USB_STATE_ATTACHED;
   4.205 +
   4.206 +	/* 10msec reset signaling */
   4.207 +	info->ports[port].timeout = jiffies + msecs_to_jiffies(10);
   4.208 +}
   4.209 +
   4.210 +#if 0
   4.211 +#ifdef CONFIG_PM
   4.212 +static int xenhcd_bus_suspend(struct usb_hcd *hcd)
   4.213 +{
   4.214 +	struct usbfront_info *info = hcd_to_info(hcd);
   4.215 +	int i, ports;
   4.216 +
   4.217 +	ports = info->rh_numports;
   4.218 +
   4.219 +	spin_lock_irq(&info->lock);
   4.220 +
   4.221 +	if (HC_IS_RUNNING(hcd->state)) {
   4.222 +		/*
   4.223 +		 * TODO:
   4.224 +		 * clean queue,
   4.225 +		 * stop all transfers,
   4.226 +		 * ...
   4.227 +		 */
   4.228 +		hcd->state = HC_STATE_QUIESCING;
   4.229 +	}
   4.230 +
   4.231 +	/* suspend any active ports*/
   4.232 +	for (i = 1; i <= ports; i++) {
   4.233 +		rhport_suspend(info, i);
   4.234 +	}
   4.235 +
   4.236 +	del_timer_sync(&info->watchdog);
   4.237 +
   4.238 +	spin_unlock_irq(&info->lock);
   4.239 +
   4.240 +	return 0;
   4.241 +}
   4.242 +
   4.243 +static int xenhcd_bus_resume(struct usb_hcd *hcd)
   4.244 +{
   4.245 +	struct usbfront_info *info = hcd_to_info(hcd);
   4.246 +	int i, ports;
   4.247 +
   4.248 +	ports = info->rh_numports;
   4.249 +
   4.250 +	spin_lock_irq(&info->lock);
   4.251 +	/* resume any suspended ports*/
   4.252 +	for (i = 1; i <= ports; i++) {
   4.253 +		rhport_resume(info, i);
   4.254 +	}
   4.255 +	hcd->state = HC_STATE_RUNNING;
   4.256 +	spin_unlock_irq(&info->lock);
   4.257 +	return 0;
   4.258 +}
   4.259 +#endif
   4.260 +#endif
   4.261 +
   4.262 +static void xenhcd_hub_descriptor(struct usbfront_info *info,
   4.263 +				  struct usb_hub_descriptor *desc)
   4.264 +{
   4.265 +	u16 temp;
   4.266 +	int ports = info->rh_numports;
   4.267 +
   4.268 +	desc->bDescriptorType = 0x29;
   4.269 +	desc->bPwrOn2PwrGood = 10; /* EHCI says 20ms max */
   4.270 +	desc->bHubContrCurrent = 0;
   4.271 +	desc->bNbrPorts = ports;
   4.272 +
   4.273 +	/* size of DeviceRemovable and PortPwrCtrlMask fields*/
   4.274 +	temp = 1 + (ports / 8);
   4.275 +	desc->bDescLength = 7 + 2 * temp;
   4.276 +
   4.277 +	/* bitmaps for DeviceRemovable and PortPwrCtrlMask */
   4.278 +	memset (&desc->bitmap[0], 0, temp);
   4.279 +	memset (&desc->bitmap[temp], 0xff, temp);
   4.280 +
   4.281 +	/* per-port over current reporting and no power switching */
   4.282 +	temp = 0x000a;
   4.283 +	desc->wHubCharacteristics = cpu_to_le16(temp);
   4.284 +}
   4.285 +
   4.286 +/* port status change mask for hub_status_data */
   4.287 +#define PORT_C_MASK \
   4.288 +	((USB_PORT_STAT_C_CONNECTION \
   4.289 +	| USB_PORT_STAT_C_ENABLE \
   4.290 +	| USB_PORT_STAT_C_SUSPEND \
   4.291 +	| USB_PORT_STAT_C_OVERCURRENT \
   4.292 +	| USB_PORT_STAT_C_RESET) << 16)
   4.293 +
   4.294 +/*
   4.295 + * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap.
   4.296 + * If port status changed, writes the bitmap to buf and return
   4.297 + * that length(number of bytes).
   4.298 + * If Nothing changed, return 0.
   4.299 + */
   4.300 +static int xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
   4.301 +{
   4.302 +	struct usbfront_info *info = hcd_to_info(hcd);
   4.303 +
   4.304 +	int ports;
   4.305 +	int i;
   4.306 +	int length;
   4.307 +
   4.308 +	unsigned long flags;
   4.309 +	int ret = 0;
   4.310 +
   4.311 +	int changed = 0;
   4.312 +
   4.313 +	if (!HC_IS_RUNNING(hcd->state))
   4.314 +		return 0;
   4.315 +
   4.316 +	/* initialize the status to no-changes */
   4.317 +	ports = info->rh_numports;
   4.318 +	length = 1 + (ports / 8);
   4.319 +	for (i = 0; i < length; i++) {
   4.320 +		buf[i] = 0;
   4.321 +		ret++;
   4.322 +	}
   4.323 +
   4.324 +	spin_lock_irqsave(&info->lock, flags);
   4.325 +
   4.326 +	for (i = 0; i < ports; i++) {
   4.327 +		/* check status for each port */
   4.328 +		if (info->ports[i].status & PORT_C_MASK) {
   4.329 +			if (i < 7)
   4.330 +				buf[0] |= 1 << (i + 1);
   4.331 +			else if (i < 15)
   4.332 +				buf[1] |= 1 << (i - 7);
   4.333 +			else if (i < 23)
   4.334 +				buf[2] |= 1 << (i - 15);
   4.335 +			else
   4.336 +				buf[3] |= 1 << (i - 23);
   4.337 +			changed = 1;
   4.338 +		}
   4.339 +	}
   4.340 +
   4.341 +	if (!changed)
   4.342 +		ret = 0;
   4.343 +
   4.344 +	spin_unlock_irqrestore(&info->lock, flags);
   4.345 +
   4.346 +	return ret;
   4.347 +}
   4.348 +
   4.349 +static int xenhcd_hub_control(struct usb_hcd *hcd,
   4.350 +			       u16 typeReq,
   4.351 +			       u16 wValue,
   4.352 +			       u16 wIndex,
   4.353 +			       char *buf,
   4.354 +			       u16 wLength)
   4.355 +{
   4.356 +	struct usbfront_info *info = hcd_to_info(hcd);
   4.357 +	int ports = info->rh_numports;
   4.358 +	unsigned long flags;
   4.359 +	int ret = 0;
   4.360 +	int i;
   4.361 +	int changed = 0;
   4.362 +
   4.363 +#ifdef USBFRONT_DEBUG
   4.364 +	WPRINTK("xenusb_hub_control(typeReq %x wValue %x wIndex %x)\n",
   4.365 +	       typeReq, wValue, wIndex);
   4.366 +#endif
   4.367 +
   4.368 +	spin_lock_irqsave(&info->lock, flags);
   4.369 +	switch (typeReq) {
   4.370 +	case ClearHubFeature:
   4.371 +		/* ignore this request */
   4.372 +		break;
   4.373 +	case ClearPortFeature:
   4.374 +		if (!wIndex || wIndex > ports)
   4.375 +			goto error;
   4.376 +
   4.377 +		switch(wValue) {
   4.378 +		case USB_PORT_FEAT_SUSPEND:
   4.379 +			rhport_resume(info, wIndex);
   4.380 +			break;
   4.381 +		case USB_PORT_FEAT_POWER:
   4.382 +			rhport_power_off(info, wIndex);
   4.383 +			break;
   4.384 +		case USB_PORT_FEAT_ENABLE:
   4.385 +			rhport_disable(info, wIndex);
   4.386 +			break;
   4.387 +		case USB_PORT_FEAT_C_CONNECTION:
   4.388 +			info->ports[wIndex-1].c_connection = 0;
   4.389 +			/* falling through */
   4.390 +		default:
   4.391 +			info->ports[wIndex-1].status &= ~(1 << wValue);
   4.392 +			break;
   4.393 +		}
   4.394 +		break;
   4.395 +	case GetHubDescriptor:
   4.396 +		xenhcd_hub_descriptor(info,
   4.397 +				      (struct usb_hub_descriptor*) buf);
   4.398 +		break;
   4.399 +	case GetHubStatus:
   4.400 +		/* always local power supply good and no over-current exists. */
   4.401 +		*(__le32 *)buf = cpu_to_le32(0);
   4.402 +		break;
   4.403 +	case GetPortStatus:
   4.404 +		if (!wIndex || wIndex > ports)
   4.405 +			goto error;
   4.406 +
   4.407 +		wIndex--;
   4.408 +
   4.409 +		/* resume completion */
   4.410 +		if (info->ports[wIndex].resuming &&
   4.411 +			time_after_eq(jiffies, info->ports[wIndex].timeout)) {
   4.412 +			info->ports[wIndex].status |= (USB_PORT_STAT_C_SUSPEND << 16);
   4.413 +			info->ports[wIndex].status &= ~USB_PORT_STAT_SUSPEND;
   4.414 +		}
   4.415 +
   4.416 +		/* reset completion */
   4.417 +		if ((info->ports[wIndex].status & USB_PORT_STAT_RESET) != 0 &&
   4.418 +			time_after_eq(jiffies, info->ports[wIndex].timeout)) {
   4.419 +			info->ports[wIndex].status |= (USB_PORT_STAT_C_RESET << 16);
   4.420 +			info->ports[wIndex].status &= ~USB_PORT_STAT_RESET;
   4.421 +
   4.422 +			if (info->devices[wIndex].status != USB_STATE_NOTATTACHED) {
   4.423 +				info->ports[wIndex].status |= USB_PORT_STAT_ENABLE;
   4.424 +				info->devices[wIndex].status = USB_STATE_DEFAULT;
   4.425 +			}
   4.426 +
   4.427 +			switch(info->devices[wIndex].speed) {
   4.428 +			case USB_SPEED_LOW:
   4.429 +				info->ports[wIndex].status |= USB_PORT_STAT_LOW_SPEED;
   4.430 +				break;
   4.431 +			case USB_SPEED_HIGH:
   4.432 +				info->ports[wIndex].status |= USB_PORT_STAT_HIGH_SPEED;
   4.433 +				break;
   4.434 +			default:
   4.435 +				break;
   4.436 +			}
   4.437 +		}
   4.438 +
   4.439 +		((u16 *) buf)[0] = cpu_to_le16 (info->ports[wIndex].status);
   4.440 +		((u16 *) buf)[1] = cpu_to_le16 (info->ports[wIndex].status >> 16);
   4.441 +		break;
   4.442 +	case SetHubFeature:
   4.443 +		/* not supported */
   4.444 +		goto error;
   4.445 +	case SetPortFeature:
   4.446 +		if (!wIndex || wIndex > ports)
   4.447 +			goto error;
   4.448 +
   4.449 +		switch(wValue) {
   4.450 +		case USB_PORT_FEAT_POWER:
   4.451 +			rhport_power_on(info, wIndex);
   4.452 +			break;
   4.453 +		case USB_PORT_FEAT_RESET:
   4.454 +			rhport_reset(info, wIndex);
   4.455 +			break;
   4.456 +		case USB_PORT_FEAT_SUSPEND:
   4.457 +			rhport_suspend(info, wIndex);
   4.458 +			break;
   4.459 +		default:
   4.460 +			if ((info->ports[wIndex-1].status & USB_PORT_STAT_POWER) != 0) {
   4.461 +				info->ports[wIndex-1].status |= (1 << wValue);
   4.462 +			}
   4.463 +		}
   4.464 +		break;
   4.465 +
   4.466 +	default:
   4.467 +error:
   4.468 +		ret = -EPIPE;
   4.469 +	}
   4.470 +	spin_unlock_irqrestore(&info->lock, flags);
   4.471 +
   4.472 +	/* check status for each port */
   4.473 +	for (i = 0; i < ports; i++) {
   4.474 +		if (info->ports[i].status & PORT_C_MASK) {
   4.475 +			changed = 1;
   4.476 +		}
   4.477 +	}
   4.478 +	if (changed)
   4.479 +		usb_hcd_poll_rh_status(hcd);
   4.480 +
   4.481 +	return ret;
   4.482 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/drivers/xen/usbfront/usbfront-q.c	Wed Mar 18 11:42:51 2009 +0000
     5.3 @@ -0,0 +1,418 @@
     5.4 +/*
     5.5 + * usbfront-q.c
     5.6 + *
     5.7 + * Xen USB Virtual Host Controller - RING operations.
     5.8 + *
     5.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    5.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    5.11 + *
    5.12 + * This program is free software; you can redistribute it and/or modify
    5.13 + * it under the terms of the GNU General Public License as published by
    5.14 + * the Free Software Foundation; either version 2 of the License, or
    5.15 + * (at your option) any later version.
    5.16 + *
    5.17 + * This program is distributed in the hope that it will be useful,
    5.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.20 + * GNU General Public License for more details.
    5.21 + *
    5.22 + * You should have received a copy of the GNU General Public License
    5.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    5.24 + *
    5.25 + * or,
    5.26 + *
    5.27 + * When distributed separately from the Linux kernel or incorporated into
    5.28 + * other software packages, subject to the following license:
    5.29 + *
    5.30 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    5.31 + * of this software and associated documentation files (the "Software"), to
    5.32 + * deal in the Software without restriction, including without limitation the
    5.33 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    5.34 + * sell copies of the Software, and to permit persons to whom the Software is
    5.35 + * furnished to do so, subject to the following conditions:
    5.36 + *
    5.37 + * The above copyright notice and this permission notice shall be included in
    5.38 + * all copies or substantial portions of the Software.
    5.39 + *
    5.40 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    5.41 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    5.42 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    5.43 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    5.44 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    5.45 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    5.46 + * DEALINGS IN THE SOFTWARE.
    5.47 + */
    5.48 +
    5.49 +struct kmem_cache *xenhcd_urbp_cachep;
    5.50 +
    5.51 +static struct urb_priv *alloc_urb_priv(struct urb *urb)
    5.52 +{
    5.53 +	struct urb_priv *urbp;
    5.54 +
    5.55 +	urbp = kmem_cache_zalloc(xenhcd_urbp_cachep, GFP_ATOMIC);
    5.56 +	if (!urbp) {
    5.57 +		return NULL;
    5.58 +	}
    5.59 +
    5.60 +	urbp->urb = urb;
    5.61 +	urb->hcpriv = urbp;
    5.62 +	urbp->req_id = ~0;
    5.63 +	INIT_LIST_HEAD(&urbp->list);
    5.64 +
    5.65 +	return urbp;
    5.66 +}
    5.67 +
    5.68 +static void free_urb_priv(struct urb_priv *urbp)
    5.69 +{
    5.70 +	urbp->urb->hcpriv = NULL;
    5.71 +	kmem_cache_free(xenhcd_urbp_cachep, urbp);
    5.72 +}
    5.73 +
    5.74 +static inline int get_id_from_freelist(
    5.75 +	struct usbfront_info *info)
    5.76 +{
    5.77 +	unsigned long free;
    5.78 +	free = info->shadow_free;
    5.79 +	BUG_ON(free > USB_RING_SIZE);
    5.80 +	info->shadow_free = info->shadow[free].req.id;
    5.81 +	info->shadow[free].req.id = (unsigned int)0x0fff; /* debug */
    5.82 +	return free;
    5.83 +}
    5.84 +
    5.85 +static inline void add_id_to_freelist(
    5.86 +	struct usbfront_info *info, unsigned long id)
    5.87 +{
    5.88 +	info->shadow[id].req.id  = info->shadow_free;
    5.89 +	info->shadow[id].urb = NULL;
    5.90 +	info->shadow_free = id;
    5.91 +}
    5.92 +
    5.93 +static inline int count_pages(void *addr, int length)
    5.94 +{
    5.95 +	unsigned long start = (unsigned long) addr >> PAGE_SHIFT;
    5.96 +	unsigned long end = (unsigned long) (addr + length + PAGE_SIZE -1) >> PAGE_SHIFT;
    5.97 +	return end - start;
    5.98 +}
    5.99 +
   5.100 +static inline void xenhcd_gnttab_map(struct usbfront_info *info,
   5.101 +		void *addr, int length, grant_ref_t *gref_head,
   5.102 +		struct usbif_request_segment *seg, int nr_pages, int flags)
   5.103 +{
   5.104 +	grant_ref_t ref;
   5.105 +	struct page *page;
   5.106 +	unsigned long buffer_pfn;
   5.107 +	unsigned int offset;
   5.108 +	unsigned int len;
   5.109 +	unsigned int bytes;
   5.110 +	int i;
   5.111 +
   5.112 +	page = virt_to_page(addr);
   5.113 +	buffer_pfn = page_to_phys(page) >> PAGE_SHIFT;
   5.114 +	offset = offset_in_page(addr);
   5.115 +	len = length;
   5.116 +
   5.117 +	for(i = 0;i < nr_pages;i++){
   5.118 +		bytes = PAGE_SIZE - offset;
   5.119 +		if(bytes > len)
   5.120 +			bytes = len;
   5.121 +
   5.122 +		ref = gnttab_claim_grant_reference(gref_head);
   5.123 +		BUG_ON(ref == -ENOSPC);
   5.124 +		gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id, buffer_pfn, flags);
   5.125 +		seg[i].gref = ref;
   5.126 +		seg[i].offset = (uint16_t)offset;
   5.127 +		seg[i].length = (uint16_t)bytes;
   5.128 +
   5.129 +		buffer_pfn++;
   5.130 +		len -= bytes;
   5.131 +		offset = 0;
   5.132 +	}
   5.133 +}
   5.134 +
   5.135 +static int map_urb_for_request(struct usbfront_info *info, struct urb *urb,
   5.136 +		usbif_request_t *req)
   5.137 +{
   5.138 +	grant_ref_t gref_head;
   5.139 +	int nr_buff_pages = 0;
   5.140 +	int nr_isodesc_pages = 0;
   5.141 +	int ret = 0;
   5.142 +
   5.143 +	if (urb->transfer_buffer_length) {
   5.144 +		nr_buff_pages = count_pages(urb->transfer_buffer, urb->transfer_buffer_length);
   5.145 +
   5.146 +		if (usb_pipeisoc(urb->pipe))
   5.147 +			nr_isodesc_pages = count_pages(&urb->iso_frame_desc[0],
   5.148 +					sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets);
   5.149 +
   5.150 +		if (nr_buff_pages + nr_isodesc_pages > USBIF_MAX_SEGMENTS_PER_REQUEST)
   5.151 +			return -E2BIG;
   5.152 +
   5.153 +		ret = gnttab_alloc_grant_references(USBIF_MAX_SEGMENTS_PER_REQUEST, &gref_head);
   5.154 +		if (ret) {
   5.155 +			printk(KERN_ERR "usbfront: gnttab_alloc_grant_references() error\n");
   5.156 +			return -ENOMEM;
   5.157 +		}
   5.158 +
   5.159 +		xenhcd_gnttab_map(info, urb->transfer_buffer,
   5.160 +				urb->transfer_buffer_length,
   5.161 +				&gref_head, &req->seg[0], nr_buff_pages,
   5.162 +				usb_pipein(urb->pipe) ? 0 : GTF_readonly);
   5.163 +
   5.164 +		if (!usb_pipeisoc(urb->pipe))
   5.165 +			gnttab_free_grant_references(gref_head);
   5.166 +	}
   5.167 +
   5.168 +	req->pipe = usbif_setportnum_pipe(urb->pipe, urb->dev->portnum);
   5.169 +	req->transfer_flags = urb->transfer_flags;
   5.170 +	req->buffer_length = urb->transfer_buffer_length;
   5.171 +	req->nr_buffer_segs = nr_buff_pages;
   5.172 +
   5.173 +	switch (usb_pipetype(urb->pipe)) {
   5.174 +	case PIPE_ISOCHRONOUS:
   5.175 +		req->u.isoc.interval = urb->interval;
   5.176 +		req->u.isoc.start_frame = urb->start_frame;
   5.177 +		req->u.isoc.number_of_packets = urb->number_of_packets;
   5.178 +		req->u.isoc.nr_frame_desc_segs = nr_isodesc_pages;
   5.179 +		/*
   5.180 +		 * urb->number_of_packets must be > 0
   5.181 +		 */
   5.182 +		if (unlikely(urb->number_of_packets <= 0))
   5.183 +			BUG();
   5.184 +		xenhcd_gnttab_map(info, &urb->iso_frame_desc[0],
   5.185 +				sizeof(struct usb_iso_packet_descriptor) * urb->number_of_packets,
   5.186 +				&gref_head, &req->seg[nr_buff_pages], nr_isodesc_pages, 0);
   5.187 +		gnttab_free_grant_references(gref_head);
   5.188 +		break;
   5.189 +	case PIPE_INTERRUPT:
   5.190 +		req->u.intr.interval = urb->interval;
   5.191 +		break;
   5.192 +	case PIPE_CONTROL:
   5.193 +		if (urb->setup_packet)
   5.194 +			memcpy(req->u.ctrl, urb->setup_packet, 8);
   5.195 +		break;
   5.196 +	case PIPE_BULK:
   5.197 +		break;
   5.198 +	default:
   5.199 +		ret = -EINVAL;
   5.200 +	}
   5.201 +
   5.202 +	return ret;
   5.203 +}
   5.204 +
   5.205 +static void xenhcd_gnttab_done(struct usb_shadow *shadow)
   5.206 +{
   5.207 +	int nr_segs = 0;
   5.208 +	int i;
   5.209 +
   5.210 +	nr_segs = shadow->req.nr_buffer_segs;
   5.211 +
   5.212 +	if (usb_pipeisoc(shadow->req.pipe))
   5.213 +		nr_segs +=  shadow->req.u.isoc.nr_frame_desc_segs;
   5.214 +
   5.215 +	for (i = 0; i < nr_segs; i++)
   5.216 +		gnttab_end_foreign_access(shadow->req.seg[i].gref, 0UL);
   5.217 +}
   5.218 +
   5.219 +static void xenhcd_giveback_urb(struct usbfront_info *info, struct urb *urb)
   5.220 +__releases(info->lock)
   5.221 +__acquires(info->lock)
   5.222 +{
   5.223 +	struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
   5.224 +
   5.225 +	list_del_init(&urbp->list);
   5.226 +	free_urb_priv(urbp);
   5.227 +	switch (urb->status) {
   5.228 +	case -ECONNRESET:
   5.229 +	case -ENOENT:
   5.230 +		COUNT(info->stats.unlink);
   5.231 +		break;
   5.232 +	default:
   5.233 +		COUNT(info->stats.complete);
   5.234 +	}
   5.235 +	spin_unlock(&info->lock);
   5.236 +	usb_hcd_giveback_urb(info_to_hcd(info), urb, NULL);
   5.237 +	spin_lock(&info->lock);
   5.238 +}
   5.239 +
   5.240 +static inline int xenhcd_do_request(struct usbfront_info *info, struct urb_priv *urbp)
   5.241 +{
   5.242 +	usbif_request_t *ring_req;
   5.243 +	struct urb *urb = urbp->urb;
   5.244 +	uint16_t id;
   5.245 +	int notify;
   5.246 +	int ret = 0;
   5.247 +
   5.248 +	ring_req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt);
   5.249 +	id = get_id_from_freelist(info);
   5.250 +	ring_req->id = id;
   5.251 +
   5.252 +	ret = map_urb_for_request(info, urb, ring_req);
   5.253 +	if (ret < 0) {
   5.254 +		add_id_to_freelist(info, id);
   5.255 +		return ret;
   5.256 +	}
   5.257 +
   5.258 +	info->ring.req_prod_pvt++;
   5.259 +	info->shadow[id].urb = urb;
   5.260 +	info->shadow[id].req = *ring_req;
   5.261 +	urbp->req_id = id;
   5.262 +
   5.263 +	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify);
   5.264 +	if (notify)
   5.265 +		notify_remote_via_irq(info->irq);
   5.266 +
   5.267 +	return ret;
   5.268 +}
   5.269 +
   5.270 +static void xenhcd_kick_pending_urbs(struct usbfront_info *info)
   5.271 +{
   5.272 +	struct urb_priv *urbp;
   5.273 +	int ret;
   5.274 +
   5.275 +	while (!list_empty(&info->pending_urbs)) {
   5.276 +		if (RING_FULL(&info->ring)) {
   5.277 +			COUNT(info->stats.ring_full);
   5.278 +			timer_action(info, TIMER_RING_WATCHDOG);
   5.279 +			goto done;
   5.280 +		}
   5.281 +
   5.282 +		urbp = list_entry(info->pending_urbs.next, struct urb_priv, list);
   5.283 +		ret = xenhcd_do_request(info, urbp);
   5.284 +		if (ret == 0)
   5.285 +			list_move_tail(&urbp->list, &info->inprogress_urbs);
   5.286 +		else
   5.287 +			xenhcd_giveback_urb(info, urbp->urb);
   5.288 +	}
   5.289 +	timer_action_done(info, TIMER_SCAN_PENDING_URBS);
   5.290 +
   5.291 +done:
   5.292 +	return;
   5.293 +}
   5.294 +
   5.295 +static void xenhcd_giveback_unlinked_urbs(struct usbfront_info *info)
   5.296 +{
   5.297 +	struct urb_priv *urbp, *tmp;
   5.298 +
   5.299 +	list_for_each_entry_safe(urbp, tmp, &info->unlinked_urbs, list) {
   5.300 +		xenhcd_giveback_urb(info, urbp->urb);
   5.301 +	}
   5.302 +}
   5.303 +
   5.304 +static int xenhcd_submit_urb(struct usbfront_info *info, struct urb_priv *urbp)
   5.305 +{
   5.306 +	int ret = 0;
   5.307 +
   5.308 +	if (RING_FULL(&info->ring)) {
   5.309 +		list_add_tail(&urbp->list, &info->pending_urbs);
   5.310 +		COUNT(info->stats.ring_full);
   5.311 +		timer_action(info, TIMER_RING_WATCHDOG);
   5.312 +		goto done;
   5.313 +	}
   5.314 +
   5.315 +	if (!list_empty(&info->pending_urbs)) {
   5.316 +		list_add_tail(&urbp->list, &info->pending_urbs);
   5.317 +		timer_action(info, TIMER_SCAN_PENDING_URBS);
   5.318 +		goto done;
   5.319 +	}
   5.320 +
   5.321 +	ret = xenhcd_do_request(info, urbp);
   5.322 +	if (ret == 0)
   5.323 +		list_add_tail(&urbp->list, &info->inprogress_urbs);
   5.324 +
   5.325 +done:
   5.326 +	return ret;
   5.327 +}
   5.328 +
   5.329 +static int xenhcd_unlink_urb(struct usbfront_info *info, struct urb_priv *urbp)
   5.330 +{
   5.331 +	if (urbp->unlinked)
   5.332 +		return -EBUSY;
   5.333 +	urbp->unlinked = 1;
   5.334 +
   5.335 +	/* if the urb is in pending_urbs */
   5.336 +	if (urbp->req_id == ~0) {
   5.337 +		list_move_tail(&urbp->list, &info->unlinked_urbs);
   5.338 +		timer_action(info, TIMER_SCAN_PENDING_URBS);
   5.339 +	}
   5.340 +
   5.341 +	/* TODO: send cancel request to backend */
   5.342 +
   5.343 +	return 0;
   5.344 +}
   5.345 +
   5.346 +static int xenhcd_end_submit_urb(struct usbfront_info *info)
   5.347 +{
   5.348 +	usbif_response_t *ring_res;
   5.349 +	struct urb *urb;
   5.350 +	struct urb_priv *urbp;
   5.351 +
   5.352 +	RING_IDX i, rp;
   5.353 +	uint16_t id;
   5.354 +	int more_to_do = 0;
   5.355 +	unsigned long flags;
   5.356 +
   5.357 +	spin_lock_irqsave(&info->lock, flags);
   5.358 +	rp = info->ring.sring->rsp_prod;
   5.359 +	rmb(); /* ensure we see queued responses up to "rp" */
   5.360 +
   5.361 +	for (i = info->ring.rsp_cons; i != rp; i++) {
   5.362 +		ring_res = RING_GET_RESPONSE(&info->ring, i);
   5.363 +		id = ring_res->id;
   5.364 +		xenhcd_gnttab_done(&info->shadow[id]);
   5.365 +		urb = info->shadow[id].urb;
   5.366 +		barrier();
   5.367 +		add_id_to_freelist(info, id);
   5.368 +
   5.369 +		urbp = (struct urb_priv *)urb->hcpriv;
   5.370 +		if (likely(!urbp->unlinked)) {
   5.371 +			urb->status = ring_res->status;
   5.372 +			urb->actual_length = ring_res->actual_length;
   5.373 +			urb->error_count = ring_res->error_count;
   5.374 +			urb->start_frame = ring_res->start_frame;
   5.375 +		}
   5.376 +		barrier();
   5.377 +		xenhcd_giveback_urb(info, urb);
   5.378 +	}
   5.379 +	info->ring.rsp_cons = i;
   5.380 +
   5.381 +	if (i != info->ring.req_prod_pvt)
   5.382 +		RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do);
   5.383 +	else
   5.384 +		info->ring.sring->rsp_event = i + 1;
   5.385 +
   5.386 +	spin_unlock_irqrestore(&info->lock, flags);
   5.387 +
   5.388 +	cond_resched();
   5.389 +
   5.390 +	return more_to_do;
   5.391 +}
   5.392 +
   5.393 +int xenhcd_schedule(void *arg)
   5.394 +{
   5.395 +	struct usbfront_info *info = (struct usbfront_info *) arg;
   5.396 +
   5.397 +	while (!kthread_should_stop()) {
   5.398 +		wait_event_interruptible(
   5.399 +				info->wq,
   5.400 +				info->waiting_resp || kthread_should_stop());
   5.401 +		info->waiting_resp = 0;
   5.402 +		smp_mb();
   5.403 +
   5.404 +		if (xenhcd_end_submit_urb(info))
   5.405 +			info->waiting_resp = 1;
   5.406 +	}
   5.407 +
   5.408 +	return 0;
   5.409 +}
   5.410 +
   5.411 +static void xenhcd_notify_work(struct usbfront_info *info)
   5.412 +{
   5.413 +	info->waiting_resp = 1;
   5.414 +	wake_up(&info->wq);
   5.415 +}
   5.416 +
   5.417 +irqreturn_t xenhcd_int(int irq, void *dev_id, struct pt_regs *ptregs)
   5.418 +{
   5.419 +	xenhcd_notify_work((struct usbfront_info *) dev_id);
   5.420 +	return IRQ_HANDLED;
   5.421 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/drivers/xen/usbfront/usbfront.h	Wed Mar 18 11:42:51 2009 +0000
     6.3 @@ -0,0 +1,216 @@
     6.4 +/*
     6.5 + * usbfront.h
     6.6 + *
     6.7 + * This file is part of Xen USB Virtual Host Controller driver.
     6.8 + *
     6.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    6.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    6.11 + *
    6.12 + * This program is free software; you can redistribute it and/or modify
    6.13 + * it under the terms of the GNU General Public License as published by
    6.14 + * the Free Software Foundation; either version 2 of the License, or
    6.15 + * (at your option) any later version.
    6.16 + *
    6.17 + * This program is distributed in the hope that it will be useful,
    6.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.20 + * GNU General Public License for more details.
    6.21 + *
    6.22 + * You should have received a copy of the GNU General Public License
    6.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    6.24 + *
    6.25 + * or,
    6.26 + *
    6.27 + * When distributed separately from the Linux kernel or incorporated into
    6.28 + * other software packages, subject to the following license:
    6.29 + *
    6.30 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    6.31 + * of this software and associated documentation files (the "Software"), to
    6.32 + * deal in the Software without restriction, including without limitation the
    6.33 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    6.34 + * sell copies of the Software, and to permit persons to whom the Software is
    6.35 + * furnished to do so, subject to the following conditions:
    6.36 + *
    6.37 + * The above copyright notice and this permission notice shall be included in
    6.38 + * all copies or substantial portions of the Software.
    6.39 + *
    6.40 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    6.41 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    6.42 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    6.43 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    6.44 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    6.45 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    6.46 + * DEALINGS IN THE SOFTWARE.
    6.47 + */
    6.48 +
    6.49 +#ifndef __XEN_USBFRONT_H__
    6.50 +#define __XEN_USBFRONT_H__
    6.51 +
    6.52 +#include <linux/module.h>
    6.53 +#include <linux/usb.h>
    6.54 +#include <linux/list.h>
    6.55 +#include <linux/kthread.h>
    6.56 +#include <linux/wait.h>
    6.57 +#include <asm/io.h>
    6.58 +#include <xen/xenbus.h>
    6.59 +#include <xen/evtchn.h>
    6.60 +#include <xen/gnttab.h>
    6.61 +#include <xen/interface/xen.h>
    6.62 +#include <xen/interface/io/usbif.h>
    6.63 +
    6.64 +/*
    6.65 + * usbfront needs USB HCD headers,
    6.66 + * drivers/usb/core/hcd.h and drivers/usb/core/hub.h,
    6.67 + * but, they are not in public include path.
    6.68 + */
    6.69 +#include "../../usb/core/hcd.h"
    6.70 +#include "../../usb/core/hub.h"
    6.71 +
    6.72 +#define DRIVER_DESC "Xen USB2.0 Virtual Host Controller driver (usbfront)"
    6.73 +
    6.74 +static inline struct usbfront_info *hcd_to_info(struct usb_hcd *hcd)
    6.75 +{
    6.76 +	return (struct usbfront_info *) (hcd->hcd_priv);
    6.77 +}
    6.78 +
    6.79 +static inline struct usb_hcd *info_to_hcd(struct usbfront_info *info)
    6.80 +{
    6.81 +	return container_of ((void *) info, struct usb_hcd, hcd_priv);
    6.82 +}
    6.83 +
    6.84 +/*
    6.85 + * Private per-URB data
    6.86 + */
    6.87 +struct urb_priv {
    6.88 +	struct list_head list;
    6.89 +	struct urb *urb;
    6.90 +	int req_id;	/* RING_REQUEST id */
    6.91 +	unsigned unlinked:1; /* dequeued urb just marked */
    6.92 +};
    6.93 +
    6.94 +/* virtual roothub port status */
    6.95 +struct rhport_status {
    6.96 +	u32 status;
    6.97 +	unsigned resuming:1; /* in resuming */
    6.98 +	unsigned c_connection:1; /* connection changed */
    6.99 +	unsigned long timeout;
   6.100 +};
   6.101 +
   6.102 +/* status of attached device */
   6.103 +struct vdevice_status {
   6.104 +	int devnum;
   6.105 +	enum usb_device_state status;
   6.106 +	enum usb_device_speed speed;
   6.107 +};
   6.108 +
   6.109 +/* RING request shadow */
   6.110 +struct usb_shadow {
   6.111 +	usbif_request_t req;
   6.112 +	struct urb *urb;
   6.113 +};
   6.114 +
   6.115 +/* statistics for tuning, monitoring, ... */
   6.116 +struct xenhcd_stats {
   6.117 +	unsigned long ring_full; /* RING_FULL conditions */
   6.118 +	unsigned long complete; /* normal givebacked urbs */
   6.119 +	unsigned long unlink; /* unlinked urbs */
   6.120 +};
   6.121 +
   6.122 +struct usbfront_info {
   6.123 +	/*
   6.124 +	 * Virtual Host Controller has 3 queues.
   6.125 +	 *
   6.126 +	 * pending_urbs:
   6.127 +	 * 	If xenhcd_urb_enqueue() called in RING_FULL state,
   6.128 +	 * 	the enqueued urbs are added to this queue, and waits
   6.129 +	 * 	to be sent to the backend.
   6.130 +	 *
   6.131 +	 * inprogress_urbs:
   6.132 +	 * 	After xenhcd_urb_enqueue() called and RING_REQUEST sent,
   6.133 +	 * 	the urbs are added to this queue and waits for RING_RESPONSE.
   6.134 +	 *
   6.135 +	 * unlinked_urbs:
   6.136 +	 *	When xenhcd_urb_dequeue() called, if the dequeued urb is
   6.137 +	 *	listed in pending_urbs, that urb is moved to this queue
   6.138 +	 *	and waits to be given back to the USB core.
   6.139 +	 */
   6.140 +	struct list_head pending_urbs;
   6.141 +	struct list_head inprogress_urbs;
   6.142 +	struct list_head unlinked_urbs;
   6.143 +	spinlock_t lock;
   6.144 +
   6.145 +	/*
   6.146 +	 * timer function that kick pending_urbs and unlink_urbs.
   6.147 +	 */
   6.148 +	unsigned long actions;
   6.149 +	struct timer_list watchdog;
   6.150 +
   6.151 +	/*
   6.152 +	 * Virtual roothub:
   6.153 +	 * Emulates the hub ports and the attached devices status.
   6.154 +	 * USB_MAXCHILDREN is defined (16) in include/linux/usb.h
   6.155 +	 */
   6.156 +	int rh_numports;
   6.157 +	struct rhport_status ports[USB_MAXCHILDREN];
   6.158 +	struct vdevice_status devices[USB_MAXCHILDREN];
   6.159 +
   6.160 +#ifdef XENHCD_STATS
   6.161 +	struct xenhcd_stats stats;
   6.162 +#define COUNT(x) do { (x)++; } while (0)
   6.163 +#else
   6.164 +#define COUNT(x) do {} while (0)
   6.165 +#endif
   6.166 +
   6.167 +	/* Xen related staff */
   6.168 +	struct xenbus_device *xbdev;
   6.169 +	int ring_ref;
   6.170 +	usbif_front_ring_t ring;
   6.171 +	unsigned int irq;
   6.172 +	struct usb_shadow shadow[USB_RING_SIZE];
   6.173 +	unsigned long shadow_free;
   6.174 +
   6.175 +	/* RING_RESPONSE thread */
   6.176 +	struct task_struct *kthread;
   6.177 +	wait_queue_head_t wq;
   6.178 +	unsigned int waiting_resp;
   6.179 +};
   6.180 +
   6.181 +#define XENHCD_RING_JIFFIES (HZ/200)
   6.182 +#define XENHCD_SCAN_JIFFIES 1
   6.183 +
   6.184 +enum xenhcd_timer_action {
   6.185 +	TIMER_RING_WATCHDOG,
   6.186 +	TIMER_SCAN_PENDING_URBS,
   6.187 +};
   6.188 +
   6.189 +static inline void
   6.190 +timer_action_done(struct usbfront_info *info, enum xenhcd_timer_action action)
   6.191 +{
   6.192 +	clear_bit(action, &info->actions);
   6.193 +}
   6.194 +
   6.195 +static inline void
   6.196 +timer_action(struct usbfront_info *info, enum xenhcd_timer_action action)
   6.197 +{
   6.198 +	if (timer_pending(&info->watchdog)
   6.199 +			&& test_bit(TIMER_SCAN_PENDING_URBS, &info->actions))
   6.200 +		return;
   6.201 +
   6.202 +	if (!test_and_set_bit(action, &info->actions)) {
   6.203 +		unsigned long t;
   6.204 +
   6.205 +		switch(action) {
   6.206 +		case TIMER_RING_WATCHDOG:
   6.207 +			t = XENHCD_RING_JIFFIES;
   6.208 +			break;
   6.209 +		default:
   6.210 +			t = XENHCD_SCAN_JIFFIES;
   6.211 +			break;
   6.212 +		}
   6.213 +		mod_timer(&info->watchdog, t + jiffies);
   6.214 +	}
   6.215 +}
   6.216 +
   6.217 +irqreturn_t xenhcd_int(int irq, void *dev_id, struct pt_regs *ptregs);
   6.218 +
   6.219 +#endif /* __XEN_USBFRONT_H__ */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/drivers/xen/usbfront/xenbus.c	Wed Mar 18 11:42:51 2009 +0000
     7.3 @@ -0,0 +1,365 @@
     7.4 +/*
     7.5 + * xenbus.c
     7.6 + *
     7.7 + * Xenbus interface for Xen USB Virtual Host Controller
     7.8 + *
     7.9 + * Copyright (C) 2009, FUJITSU LABORATORIES LTD.
    7.10 + * Author: Noboru Iwamatsu <n_iwamatsu@jp.fujitsu.com>
    7.11 + *
    7.12 + * This program is free software; you can redistribute it and/or modify
    7.13 + * it under the terms of the GNU General Public License as published by
    7.14 + * the Free Software Foundation; either version 2 of the License, or
    7.15 + * (at your option) any later version.
    7.16 + *
    7.17 + * This program is distributed in the hope that it will be useful,
    7.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.20 + * GNU General Public License for more details.
    7.21 + *
    7.22 + * You should have received a copy of the GNU General Public License
    7.23 + * along with this program; if not, see <http://www.gnu.org/licenses/>.
    7.24 + *
    7.25 + * or,
    7.26 + *
    7.27 + * When distributed separately from the Linux kernel or incorporated into
    7.28 + * other software packages, subject to the following license:
    7.29 + *
    7.30 + * Permission is hereby granted, free of charge, to any person obtaining a copy
    7.31 + * of this software and associated documentation files (the "Software"), to
    7.32 + * deal in the Software without restriction, including without limitation the
    7.33 + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    7.34 + * sell copies of the Software, and to permit persons to whom the Software is
    7.35 + * furnished to do so, subject to the following conditions:
    7.36 + *
    7.37 + * The above copyright notice and this permission notice shall be included in
    7.38 + * all copies or substantial portions of the Software.
    7.39 + *
    7.40 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    7.41 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    7.42 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    7.43 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    7.44 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    7.45 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    7.46 + * DEALINGS IN THE SOFTWARE.
    7.47 + */
    7.48 +
    7.49 +#include "usbfront.h"
    7.50 +
    7.51 +extern struct hc_driver usbfront_hc_driver;
    7.52 +extern struct kmem_cache *xenhcd_urbp_cachep;
    7.53 +extern void xenhcd_rhport_state_change(struct usbfront_info *info,
    7.54 +					int port, enum usb_device_speed speed);
    7.55 +extern int xenhcd_schedule(void *arg);
    7.56 +
    7.57 +#define GRANT_INVALID_REF 0
    7.58 +
    7.59 +static void usbif_free(struct usbfront_info *info)
    7.60 +{
    7.61 +	if (info->ring_ref != GRANT_INVALID_REF) {
    7.62 +		gnttab_end_foreign_access(info->ring_ref,
    7.63 +					  (unsigned long)info->ring.sring);
    7.64 +		info->ring_ref = GRANT_INVALID_REF;
    7.65 +		info->ring.sring = NULL;
    7.66 +	}
    7.67 +	if (info->irq)
    7.68 +		unbind_from_irqhandler(info->irq, info);
    7.69 +	info->irq = 0;
    7.70 +}
    7.71 +
    7.72 +static int setup_usbring(struct xenbus_device *dev,
    7.73 +			   struct usbfront_info *info)
    7.74 +{
    7.75 +	usbif_sring_t *sring;
    7.76 +	int err;
    7.77 +
    7.78 +	info->ring_ref= GRANT_INVALID_REF;
    7.79 +
    7.80 +	sring = (usbif_sring_t *)get_zeroed_page(GFP_NOIO|__GFP_HIGH);
    7.81 +	if (!sring) {
    7.82 +		xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
    7.83 +		return -ENOMEM;
    7.84 +	}
    7.85 +	SHARED_RING_INIT(sring);
    7.86 +	FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);
    7.87 +
    7.88 +	err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring));
    7.89 +	if (err < 0) {
    7.90 +		free_page((unsigned long)sring);
    7.91 +		info->ring.sring = NULL;
    7.92 +		goto fail;
    7.93 +	}
    7.94 +	info->ring_ref = err;
    7.95 +
    7.96 +	err = bind_listening_port_to_irqhandler(
    7.97 +		dev->otherend_id, xenhcd_int, SA_SAMPLE_RANDOM, "usbif", info);
    7.98 +	if (err <= 0) {
    7.99 +		xenbus_dev_fatal(dev, err,
   7.100 +				 "bind_listening_port_to_irqhandler");
   7.101 +		goto fail;
   7.102 +	}
   7.103 +	info->irq = err;
   7.104 +
   7.105 +	return 0;
   7.106 +fail:
   7.107 +	usbif_free(info);
   7.108 +	return err;
   7.109 +}
   7.110 +
   7.111 +static int talk_to_backend(struct xenbus_device *dev,
   7.112 +			   struct usbfront_info *info)
   7.113 +{
   7.114 +	const char *message;
   7.115 +	struct xenbus_transaction xbt;
   7.116 +	int err;
   7.117 +
   7.118 +	err = setup_usbring(dev, info);
   7.119 +	if (err)
   7.120 +		goto out;
   7.121 +
   7.122 +again:
   7.123 +	err = xenbus_transaction_start(&xbt);
   7.124 +	if (err) {
   7.125 +		xenbus_dev_fatal(dev, err, "starting transaction");
   7.126 +		goto destroy_ring;
   7.127 +	}
   7.128 +
   7.129 +	err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u",
   7.130 +			    info->ring_ref);
   7.131 +	if (err) {
   7.132 +		message = "writing ring-ref";
   7.133 +		goto abort_transaction;
   7.134 +	}
   7.135 +
   7.136 +	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
   7.137 +			    irq_to_evtchn_port(info->irq));
   7.138 +	if (err) {
   7.139 +		message = "writing event-channel";
   7.140 +		goto abort_transaction;
   7.141 +	}
   7.142 +
   7.143 +	err = xenbus_transaction_end(xbt, 0);
   7.144 +	if (err) {
   7.145 +		if (err == -EAGAIN)
   7.146 +			goto again;
   7.147 +		xenbus_dev_fatal(dev, err, "completing transaction");
   7.148 +		goto destroy_ring;
   7.149 +	}
   7.150 +
   7.151 +	xenbus_switch_state(dev, XenbusStateInitialised);
   7.152 +
   7.153 +	return 0;
   7.154 +
   7.155 +abort_transaction:
   7.156 +	xenbus_transaction_end(xbt, 1);
   7.157 +	xenbus_dev_fatal(dev, err, "%s", message);
   7.158 +
   7.159 +destroy_ring:
   7.160 +	usbif_free(info);
   7.161 +
   7.162 +out:
   7.163 +	return err;
   7.164 +}
   7.165 +
   7.166 +static struct usb_hcd *create_hcd(struct xenbus_device *dev)
   7.167 +{
   7.168 +	int i;
   7.169 +	int err = 0;
   7.170 +	int num_ports;
   7.171 +	struct usb_hcd *hcd = NULL;
   7.172 +	struct usbfront_info *info = NULL;
   7.173 +
   7.174 +	err = xenbus_scanf(XBT_NIL, dev->otherend,
   7.175 +					"num-ports", "%d", &num_ports);
   7.176 +	if (err != 1) {
   7.177 +		xenbus_dev_fatal(dev, err, "reading num-ports");
   7.178 +		return ERR_PTR(-EINVAL);
   7.179 +	}
   7.180 +	if (num_ports < 1 || num_ports > USB_MAXCHILDREN) {
   7.181 +		xenbus_dev_fatal(dev, err, "invalid num-ports");
   7.182 +		return ERR_PTR(-EINVAL);
   7.183 +	}
   7.184 +
   7.185 +	hcd = usb_create_hcd(&usbfront_hc_driver, &dev->dev, dev->dev.bus_id);
   7.186 +	if (!hcd) {
   7.187 +		xenbus_dev_fatal(dev, err, "fail to allocate USB host controller");
   7.188 +		return ERR_PTR(-ENOMEM);
   7.189 +	}
   7.190 +	info = hcd_to_info(hcd);
   7.191 +	info->xbdev = dev;
   7.192 +	info->rh_numports = num_ports;
   7.193 +
   7.194 +	for (i = 0; i < USB_RING_SIZE; i++) {
   7.195 +		info->shadow[i].req.id = i+1;
   7.196 +		info->shadow[i].urb = NULL;
   7.197 +	}
   7.198 +	info->shadow[USB_RING_SIZE-1].req.id = 0x0fff;
   7.199 +
   7.200 +	return hcd;
   7.201 +}
   7.202 +
   7.203 +static int usbfront_probe(struct xenbus_device *dev,
   7.204 +			  const struct xenbus_device_id *id)
   7.205 +{
   7.206 +	int err;
   7.207 +	struct usb_hcd *hcd;
   7.208 +	struct usbfront_info *info;
   7.209 +	char name[TASK_COMM_LEN];
   7.210 +
   7.211 +	if (usb_disabled())
   7.212 +		return -ENODEV;
   7.213 +
   7.214 +	hcd = create_hcd(dev);
   7.215 +	if (IS_ERR(hcd)) {
   7.216 +		err = PTR_ERR(hcd);
   7.217 +		xenbus_dev_fatal(dev, err, "fail to create usb host controller");
   7.218 +		goto fail;
   7.219 +	}
   7.220 +
   7.221 +	info = hcd_to_info(hcd);
   7.222 +	dev->dev.driver_data = info;
   7.223 +
   7.224 +	err = usb_add_hcd(hcd, 0, 0);
   7.225 +	if (err != 0) {
   7.226 +		xenbus_dev_fatal(dev, err, "fail to adding USB host controller");
   7.227 +		goto fail;
   7.228 +	}
   7.229 +
   7.230 +	init_waitqueue_head(&info->wq);
   7.231 +	snprintf(name, TASK_COMM_LEN, "xenhcd.%d", hcd->self.busnum);
   7.232 +	info->kthread = kthread_run(xenhcd_schedule, info, name);
   7.233 +        if (IS_ERR(info->kthread)) {
   7.234 +                err = PTR_ERR(info->kthread);
   7.235 +                info->kthread = NULL;
   7.236 +                goto fail;
   7.237 +        }
   7.238 +
   7.239 +	err = talk_to_backend(dev, info);
   7.240 +	if (err)
   7.241 +		goto fail;
   7.242 +
   7.243 +	return 0;
   7.244 +
   7.245 +fail:
   7.246 +	usb_put_hcd(hcd);
   7.247 +	dev->dev.driver_data = NULL;
   7.248 +	return err;
   7.249 +}
   7.250 +
   7.251 +/*
   7.252 + * 0=disconnected, 1=low_speed, 2=full_speed, 3=high_speed
   7.253 + */
   7.254 +static void usbfront_do_hotplug(struct usbfront_info *info)
   7.255 +{
   7.256 +	char port_str[8];
   7.257 +	int i;
   7.258 +	int err;
   7.259 +	int state;
   7.260 +
   7.261 +	for (i = 1; i <= info->rh_numports; i++) {
   7.262 +		sprintf(port_str, "port-%d", i);
   7.263 +		err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
   7.264 +					port_str, "%d", &state);
   7.265 +		if (err == 1)
   7.266 +			xenhcd_rhport_state_change(info, i, state);
   7.267 +	}
   7.268 +}
   7.269 +
   7.270 +static void backend_changed(struct xenbus_device *dev,
   7.271 +				     enum xenbus_state backend_state)
   7.272 +{
   7.273 +	struct usbfront_info *info = dev->dev.driver_data;
   7.274 +
   7.275 +	switch (backend_state) {
   7.276 +	case XenbusStateInitialising:
   7.277 +	case XenbusStateInitWait:
   7.278 +	case XenbusStateInitialised:
   7.279 +	case XenbusStateUnknown:
   7.280 +	case XenbusStateClosed:
   7.281 +		break;
   7.282 +
   7.283 +	case XenbusStateConnected:
   7.284 +		if (dev->state == XenbusStateConnected)
   7.285 +			break;
   7.286 +		if (dev->state == XenbusStateInitialised)
   7.287 +			usbfront_do_hotplug(info);
   7.288 +		xenbus_switch_state(dev, XenbusStateConnected);
   7.289 +		break;
   7.290 +
   7.291 +	case XenbusStateClosing:
   7.292 +		xenbus_frontend_closed(dev);
   7.293 +		break;
   7.294 +
   7.295 +	case XenbusStateReconfiguring:
   7.296 +		if (dev->state == XenbusStateConnected)
   7.297 +			xenbus_switch_state(dev, XenbusStateReconfiguring);
   7.298 +		break;
   7.299 +
   7.300 +	case XenbusStateReconfigured:
   7.301 +		usbfront_do_hotplug(info);
   7.302 +		xenbus_switch_state(dev, XenbusStateConnected);
   7.303 +		break;
   7.304 +
   7.305 +	default:
   7.306 +		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
   7.307 +				 backend_state);
   7.308 +		break;
   7.309 +	}
   7.310 +}
   7.311 +
   7.312 +static int usbfront_remove(struct xenbus_device *dev)
   7.313 +{
   7.314 +	struct usbfront_info *info = dev->dev.driver_data;
   7.315 +	struct usb_hcd *hcd = info_to_hcd(info);
   7.316 +
   7.317 +	usb_remove_hcd(hcd);
   7.318 +	if (info->kthread) {
   7.319 +		kthread_stop(info->kthread);
   7.320 +		info->kthread = NULL;
   7.321 +	}
   7.322 +	usbif_free(info);
   7.323 +	usb_put_hcd(hcd);
   7.324 +
   7.325 +	return 0;
   7.326 +}
   7.327 +
   7.328 +static const struct xenbus_device_id usbfront_ids[] = {
   7.329 +	{ "vusb" },
   7.330 +	{ "" },
   7.331 +};
   7.332 +
   7.333 +static struct xenbus_driver usbfront_driver = {
   7.334 +	.name = "vusb",
   7.335 +	.owner = THIS_MODULE,
   7.336 +	.ids = usbfront_ids,
   7.337 +	.probe = usbfront_probe,
   7.338 +	.otherend_changed = backend_changed,
   7.339 +	.remove = usbfront_remove,
   7.340 +};
   7.341 +
   7.342 +static int __init usbfront_init(void)
   7.343 +{
   7.344 +	if (!is_running_on_xen())
   7.345 +		return -ENODEV;
   7.346 +
   7.347 +	xenhcd_urbp_cachep = kmem_cache_create("xenhcd_urb_priv",
   7.348 +			sizeof(struct urb_priv), 0, 0, NULL, NULL);
   7.349 +	if (!xenhcd_urbp_cachep) {
   7.350 +		printk(KERN_ERR "usbfront failed to create kmem cache\n");
   7.351 +		return -ENOMEM;
   7.352 +	}
   7.353 +
   7.354 +	return xenbus_register_frontend(&usbfront_driver);
   7.355 +}
   7.356 +
   7.357 +static void __exit usbfront_exit(void)
   7.358 +{
   7.359 +	kmem_cache_destroy(xenhcd_urbp_cachep);
   7.360 +	xenbus_unregister_driver(&usbfront_driver);
   7.361 +}
   7.362 +
   7.363 +module_init(usbfront_init);
   7.364 +module_exit(usbfront_exit);
   7.365 +
   7.366 +MODULE_AUTHOR("");
   7.367 +MODULE_DESCRIPTION(DRIVER_DESC);
   7.368 +MODULE_LICENSE("Dual BSD/GPL");