dummy_hcd.c revision 5742b0c95026c817d9c266174ca39a909e8d38ca
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * dummy_hcd.c -- Dummy/Loopback USB host and device emulator driver.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Maintainer: Alan Stern <stern@rowland.harvard.edu>
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003 David Brownell
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003-2005 Alan Stern
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version.
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This exposes a device side "USB gadget" API, driven by requests to a
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Linux-USB host controller driver.  USB traffic is simulated; there's
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * no need for USB hardware.  Use this with two other drivers:
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  - Gadget driver, responding to requests (slave);
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  - Host-side device driver, as already familiar in Linux.
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Having this all in one kernel can help some stages of development,
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * bypassing some hardware (and driver) issues.  UML could help too.
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DEBUG
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/config.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/smp_lock.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/timer.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/version.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb_gadget.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/system.h>
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/unaligned.h>
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "../core/hcd.h"
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"USB Host+Gadget Emulator"
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION	"17 Dec 2004"
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char	driver_name [] = "dummy_hcd";
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char	driver_desc [] = "USB Host+Gadget Emulator";
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char	gadget_name [] = "dummy_udc";
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION (DRIVER_DESC);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR ("David Brownell");
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE ("GPL");
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* gadget side driver data structres */
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dummy_ep {
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head		queue;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long			last_io;	/* jiffies timestamp */
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_gadget		*gadget;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const struct usb_endpoint_descriptor *desc;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_ep			ep;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned			halted : 1;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned			already_seen : 1;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned			setup_stage : 1;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dummy_request {
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head		queue;		/* ep's requests */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_request		req;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep)
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of (_ep, struct dummy_ep, ep);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy_request *usb_request_to_dummy_request
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(struct usb_request *_req)
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of (_req, struct dummy_request, req);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Every device has ep0 for control requests, plus up to 30 more endpoints,
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in one of two types:
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   - Configurable:  direction (in/out), type (bulk, iso, etc), and endpoint
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *     number can be changed.  Names like "ep-a" are used for this type.
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   - Fixed Function:  in other cases.  some characteristics may be mutable;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *     that'd be hardware-specific.  Names like "ep12out-bulk" are used.
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Gadget drivers are responsible for not setting up conflicting endpoint
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * configurations, illegal or unsupported packet lengths, and so on.
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char ep0name [] = "ep0";
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char *const ep_name [] = {
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep0name,				/* everyone has ep0 */
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* act like a net2280: high speed, six configurable endpoints */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ep-a", "ep-b", "ep-c", "ep-d", "ep-e", "ep-f",
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* or like pxa250: fifteen fixed function endpoints */
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ep1in-bulk", "ep2out-bulk", "ep3in-iso", "ep4out-iso", "ep5in-int",
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ep6in-bulk", "ep7out-bulk", "ep8in-iso", "ep9out-iso", "ep10in-int",
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ep11in-bulk", "ep12out-bulk", "ep13in-iso", "ep14out-iso",
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"ep15in-int",
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* or like sa1100: two fixed function endpoints */
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ep1out-bulk", "ep2in-bulk",
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DUMMY_ENDPOINTS	(sizeof(ep_name)/sizeof(char *))
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define FIFO_SIZE		64
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct urbp {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urb		*urb;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head	urbp_list;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dummy {
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t			lock;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * SLAVE/GADGET side support
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep			ep [DUMMY_ENDPOINTS];
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int				address;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_gadget		gadget;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_gadget_driver	*driver;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request		fifo_req;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8				fifo_buf [FIFO_SIZE];
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16				devstatus;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * MASTER/HOST side support
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct timer_list		timer;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32				port_status;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned			resuming:1;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long			re_timeout;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_device		*udev;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct list_head		urbp_list;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy *hcd_to_dummy (struct usb_hcd *hcd)
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (struct dummy *) (hcd->hcd_priv);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct usb_hcd *dummy_to_hcd (struct dummy *dum)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of((void *) dum, struct usb_hcd, hcd_priv);
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct device *dummy_dev (struct dummy *dum)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dummy_to_hcd(dum)->self.controller;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy *ep_to_dummy (struct dummy_ep *ep)
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of (ep->gadget, struct dummy, gadget);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy *gadget_to_dummy (struct usb_gadget *gadget)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of (gadget, struct dummy, gadget);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline struct dummy *gadget_dev_to_dummy (struct device *dev)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return container_of (dev, struct dummy, gadget.dev);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dummy			*the_controller;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This "hardware" may look a bit odd in diagnostics since it's got both
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * host and device sides; and it binds different drivers to each side.
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct platform_device		the_pdev;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct device_driver dummy_driver = {
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name		= (char *) driver_name,
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.bus		= &platform_bus_type,
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* SLAVE/GADGET SIDE DRIVER
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This only tracks gadget state.  All the work is done when the host
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * side tries some (emulated) i/o operation.  Real device controller
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * drivers would do real i/o using dma, fifos, irqs, timers, etc.
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define is_enabled(dum) \
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(dum->port_status & USB_PORT_STAT_ENABLE)
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned		max;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep || !desc || ep->desc || _ep->name == ep0name
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			|| desc->bDescriptorType != USB_DT_ENDPOINT)
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver || !is_enabled (dum))
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ESHUTDOWN;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	max = le16_to_cpu(desc->wMaxPacketSize) & 0x3ff;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* drivers must not request bad settings, since lower levels
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * (hardware or its drivers) may not check.  some endpoints
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * can't do iso, many have maxpacket limitations, etc.
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * since this "hardware" driver is here to help debugging, we
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * have some extra sanity checks.  (there could be more though,
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * especially for "ep9out" style fixed function ones.)
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = -EINVAL;
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (desc->bmAttributes & 0x03) {
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_ENDPOINT_XFER_BULK:
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (strstr (ep->ep.name, "-iso")
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				|| strstr (ep->ep.name, "-int")) {
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (dum->gadget.speed) {
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_HIGH:
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max == 512)
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* conserve return statements */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (max) {
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case 8: case 16: case 32: case 64:
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* we'll fake any legal size */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_LOW:
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto done;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_ENDPOINT_XFER_INT:
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (strstr (ep->ep.name, "-iso")) /* bulk is ok */
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* real hardware might not handle all packet sizes */
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (dum->gadget.speed) {
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_HIGH:
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max <= 1024)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* save a return statement */
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_FULL:
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max <= 64)
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* save a return statement */
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max <= 8)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_ENDPOINT_XFER_ISOC:
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (strstr (ep->ep.name, "-bulk")
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				|| strstr (ep->ep.name, "-int"))
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* real hardware might not handle all packet sizes */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (dum->gadget.speed) {
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_HIGH:
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max <= 1024)
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* save a return statement */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_SPEED_FULL:
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (max <= 1023)
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* save a return statement */
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* few chips support control except on ep0 */
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto done;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	_ep->maxpacket = max;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep->desc = desc;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_dbg (dummy_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n",
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_ep->name,
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		desc->bEndpointAddress & 0x0f,
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		({ char *val;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 switch (desc->bmAttributes & 0x03) {
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_ENDPOINT_XFER_BULK: val = "bulk"; break;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_ENDPOINT_XFER_ISOC: val = "iso"; break;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_ENDPOINT_XFER_INT: val = "intr"; break;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 default: val = "ctrl"; break;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 }; val; }),
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		max);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* at this point real hardware should be NAKing transfers
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to that endpoint, until a buffer is queued to it.
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = 0;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* called with spinlock held */
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void nuke (struct dummy *dum, struct dummy_ep *ep)
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (!list_empty (&ep->queue)) {
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dummy_request	*req;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req = list_entry (ep->queue.next, struct dummy_request, queue);
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_del_init (&req->queue);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req.status = -ESHUTDOWN;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock (&dum->lock);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req.complete (&ep->ep, &req->req);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock (&dum->lock);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_disable (struct usb_ep *_ep)
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep || !ep->desc || _ep->name == ep0name)
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep->desc = NULL;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = 0;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nuke (dum, ep);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_dbg (dummy_dev(dum), "disabled %s\n", _ep->name);
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_request *
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_alloc_request (struct usb_ep *_ep, int mem_flags)
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request	*req;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep)
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	req = kmalloc (sizeof *req, mem_flags);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!req)
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (req, 0, sizeof *req);
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&req->queue);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return &req->req;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_free_request (struct usb_ep *_ep, struct usb_request *_req)
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request	*req;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	req = usb_request_to_dummy_request (_req);
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	WARN_ON (!list_empty (&req->queue));
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree (req);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void *
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_alloc_buffer (
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_ep *_ep,
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned bytes,
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dma_addr_t *dma,
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int mem_flags
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char			*retval;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver)
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = kmalloc (bytes, mem_flags);
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*dma = (dma_addr_t) retval;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_free_buffer (
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_ep *_ep,
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void *buf,
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dma_addr_t dma,
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned bytes
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bytes)
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree (buf);
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfifo_complete (struct usb_ep *ep, struct usb_request *req)
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request	*req;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	req = usb_request_to_dummy_request (_req);
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_req || !list_empty (&req->queue) || !_req->complete)
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep || (!ep->desc && _ep->name != ep0name))
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver || !is_enabled (dum))
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ESHUTDOWN;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_dbg (dummy_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n",
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep, _req, _ep->name, _req->length, _req->buf);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	_req->status = -EINPROGRESS;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	_req->actual = 0;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* implement an emulated single-request FIFO */
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_empty (&dum->fifo_req.queue) &&
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_empty (&ep->queue) &&
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			_req->length <= FIFO_SIZE) {
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req = &dum->fifo_req;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req = *_req;
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req.buf = dum->fifo_buf;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy (dum->fifo_buf, _req->buf, _req->length);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req.context = dum;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		req->req.complete = fifo_complete;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock (&dum->lock);
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_req->actual = _req->length;
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_req->status = 0;
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_req->complete (_ep, _req);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock (&dum->lock);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&req->queue, &ep->queue);
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* real hardware would likely enable transfers here, in case
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * it'd been left NAKing.
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req)
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval = -EINVAL;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request	*req = NULL;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep || !_req)
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver)
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ESHUTDOWN;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry (req, &ep->queue, queue) {
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (&req->req == _req) {
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_del_init (&req->queue);
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			_req->status = -ECONNRESET;
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = 0;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval == 0) {
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg (dummy_dev(dum),
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"dequeued req %p from %s, len %d buf %p\n",
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req, _ep->name, _req->length, _req->buf);
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		_req->complete (_ep, _req);
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_set_halt (struct usb_ep *_ep, int value)
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep		*ep;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!_ep)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep = usb_ep_to_dummy_ep (_ep);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = ep_to_dummy (ep);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver)
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ESHUTDOWN;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!value)
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->halted = 0;
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			!list_empty (&ep->queue))
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EAGAIN;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->halted = 1;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME clear emulated data toggle too */
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct usb_ep_ops dummy_ep_ops = {
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.enable		= dummy_enable,
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disable	= dummy_disable,
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.alloc_request	= dummy_alloc_request,
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.free_request	= dummy_free_request,
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.alloc_buffer	= dummy_alloc_buffer,
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.free_buffer	= dummy_free_buffer,
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* map, unmap, ... eventually hook the "generic" dma calls */
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.queue		= dummy_queue,
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.dequeue	= dummy_dequeue,
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_halt	= dummy_set_halt,
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* there are both host and device side versions of this call ... */
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_g_get_frame (struct usb_gadget *_gadget)
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct timeval	tv;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do_gettimeofday (&tv);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return tv.tv_usec / 1000;
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_wakeup (struct usb_gadget *_gadget)
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = gadget_to_dummy (_gadget);
6045742b0c95026c817d9c266174ca39a909e8d38caAlan Stern	if (!(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))
6055742b0c95026c817d9c266174ca39a909e8d38caAlan Stern			|| !(dum->devstatus &
6065742b0c95026c817d9c266174ca39a909e8d38caAlan Stern				( (1 << USB_DEVICE_B_HNP_ENABLE)
6075742b0c95026c817d9c266174ca39a909e8d38caAlan Stern				| (1 << USB_DEVICE_REMOTE_WAKEUP))))
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* hub notices our request, issues downstream resume, etc */
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->resuming = 1;
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = gadget_to_dummy (_gadget);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (value)
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct usb_gadget_ops dummy_ops = {
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_frame	= dummy_g_get_frame,
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.wakeup		= dummy_wakeup,
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_selfpowered = dummy_set_selfpowered,
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* "function" sysfs attribute */
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t
63810523b3b82456e416cbaffcc24ea2246980aa746Yani Ioannoushow_function (struct device *dev, struct device_attribute *attr, char *buf)
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum = gadget_dev_to_dummy (dev);
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->driver || !dum->driver->function)
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsDEVICE_ATTR (function, S_IRUGO, show_function, NULL);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Driver registration/unregistration.
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is basically hardware-specific; there's usually only one real USB
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * device (not host) controller since that's how USB devices are intended
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to work.  So most implementations of these api calls will rely on the
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fact that only one driver will ever bind to the hardware.  But curious
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * hardware can be built with discrete components, so the gadget API doesn't
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * require that assumption.
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * For this emulator, it might be convenient to create a usb slave device
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for each driver that registers:  just add to a big root hub.
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_udc_release (struct device *dev)
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_pdev_release (struct device *dev)
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_register_udc (struct dummy *dum)
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		rc;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy (dum->gadget.dev.bus_id, "udc");
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.dev.parent = dummy_dev(dum);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.dev.release = dummy_udc_release;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = device_register (&dum->gadget.dev);
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc == 0)
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_create_file (&dum->gadget.dev, &dev_attr_function);
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdummy_unregister_udc (struct dummy *dum)
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_remove_file (&dum->gadget.dev, &dev_attr_function);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_unregister (&dum->gadget.dev);
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsusb_gadget_register_driver (struct usb_gadget_driver *driver)
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum = the_controller;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		retval, i;
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum)
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dum->driver)
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!driver->bind || !driver->unbind || !driver->setup
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			|| driver->speed == USB_SPEED_UNKNOWN)
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * SLAVE side init ... the layer above hardware, which
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * can't enumerate without help from the driver we're binding.
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.name = gadget_name;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.ops = &dummy_ops;
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.is_dualspeed = 1;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7185742b0c95026c817d9c266174ca39a909e8d38caAlan Stern	/* maybe claim OTG support, though we won't complete HNP */
7195742b0c95026c817d9c266174ca39a909e8d38caAlan Stern	dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
7205742b0c95026c817d9c266174ca39a909e8d38caAlan Stern
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->devstatus = 0;
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->resuming = 0;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&dum->gadget.ep_list);
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < DUMMY_ENDPOINTS; i++) {
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dummy_ep	*ep = &dum->ep [i];
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ep_name [i])
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->ep.name = ep_name [i];
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->ep.ops = &dummy_ep_ops;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_add_tail (&ep->ep.ep_list, &dum->gadget.ep_list);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->halted = ep->already_seen = ep->setup_stage = 0;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->ep.maxpacket = ~0;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->last_io = jiffies;
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->gadget = &dum->gadget;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->desc = NULL;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		INIT_LIST_HEAD (&ep->queue);
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.ep0 = &dum->ep [0].ep;
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->ep [0].ep.maxpacket = 64;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_del_init (&dum->ep [0].ep.ep_list);
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD(&dum->fifo_req.queue);
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->driver = driver;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->gadget.dev.driver = &driver->driver;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_dbg (dummy_dev(dum), "binding gadget driver '%s'\n",
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			driver->driver.name);
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = driver->bind (&dum->gadget)) != 0) {
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->driver = NULL;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->gadget.dev.driver = NULL;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// FIXME: Check these calls for errors and re-order
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver->driver.bus = dum->gadget.dev.parent->bus;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_register (&driver->driver);
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_bind_driver (&dum->gadget.dev);
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* khubd will enumerate this in a while */
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->port_status |= USB_PORT_STAT_CONNECTION
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		| (1 << USB_PORT_FEAT_C_CONNECTION);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL (usb_gadget_register_driver);
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* caller must hold lock */
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_ep	*ep;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* prevent any more requests */
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->address = 0;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The timer is left running so that outstanding URBs can fail */
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* nuke any pending requests first, so driver i/o is quiesced */
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nuke (dum, ep);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* driver now does any non-usb quiescing necessary */
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (driver) {
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock (&dum->lock);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		driver->disconnect (&dum->gadget);
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock (&dum->lock);
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsusb_gadget_unregister_driver (struct usb_gadget_driver *driver)
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum = the_controller;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum)
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!driver || driver != dum->driver)
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_dbg (dummy_dev(dum), "unregister gadget driver '%s'\n",
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			driver->driver.name);
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	stop_activity (dum, driver);
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE |
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver->unbind (&dum->gadget);
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->driver = NULL;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_release_driver (&dum->gadget.dev);
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister (&driver->driver);
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL (usb_gadget_unregister_driver);
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef is_enabled
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENOSYS;
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL (net2280_set_fifo_mode);
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* MASTER/HOST SIDE DRIVER
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this uses the hcd framework to hook up to host side drivers.
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * its root hub will only have one device, otherwise it acts like
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a normal host controller.
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * when urbs are queued, they're just stuck on a list that we
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * scan in a timer callback.  that callback connects writes from
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the host with reads from the device, and so on, based on the
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * usb 2.0 rules.
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_urb_enqueue (
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd			*hcd,
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_host_endpoint	*ep,
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urb			*urb,
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int				mem_flags
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum;
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urbp	*urbp;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!urb->transfer_buffer && urb->transfer_buffer_length)
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	urbp = kmalloc (sizeof *urbp, mem_flags);
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!urbp)
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	urbp->urb = urb;
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = hcd_to_dummy (hcd);
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->udev) {
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->udev = urb->dev;
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_get_dev (dum->udev);
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (unlikely (dum->udev != urb->dev))
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err (dummy_dev(dum), "usb_device address has changed!\n");
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_add_tail (&urbp->urbp_list, &dum->urbp_list);
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	urb->hcpriv = urbp;
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb->error_count = 1;		/* mark as a new urb */
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* kick the scheduler, it'll do the rest */
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!timer_pending (&dum->timer))
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mod_timer (&dum->timer, jiffies + 1);
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* giveback happens automatically in timer callback */
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void maybe_set_status (struct urb *urb, int status)
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock (&urb->lock);
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->status == -EINPROGRESS)
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb->status = status;
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock (&urb->lock);
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* transfer up to a frame's worth; caller must own lock */
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstransfer (struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit)
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy_request	*req;
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstop:
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if there's no request queued, the device is NAKing; return */
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry (req, &ep->queue, queue) {
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned	host_len, dev_len, len;
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int		is_short, to_host;
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int		rescan = 0;
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* 1..N packets of ep->ep.maxpacket each ... the last one
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * may be short (including zero length).
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * writer can send a zlp explicitly (length 0) or implicitly
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * (length mod maxpacket zero, and 'zero' flag); they always
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * terminate reads.
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		host_len = urb->transfer_buffer_length - urb->actual_length;
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_len = req->req.length - req->req.actual;
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len = min (host_len, dev_len);
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* FIXME update emulated data toggle too */
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		to_host = usb_pipein (urb->pipe);
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (unlikely (len == 0))
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			is_short = 1;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else {
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			char		*ubuf, *rbuf;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* not enough bandwidth left? */
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (limit < ep->ep.maxpacket && limit < len)
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = min (len, (unsigned) limit);
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len == 0)
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* use an extra pass for the final short packet */
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (len > ep->ep.maxpacket) {
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				rescan = 1;
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				len -= (len % ep->ep.maxpacket);
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			is_short = (len % ep->ep.maxpacket) != 0;
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* else transfer packet(s) */
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ubuf = urb->transfer_buffer + urb->actual_length;
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rbuf = req->req.buf + req->req.actual;
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (to_host)
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				memcpy (ubuf, rbuf, len);
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			else
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				memcpy (rbuf, ubuf, len);
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->last_io = jiffies;
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			limit -= len;
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			urb->actual_length += len;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			req->req.actual += len;
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* short packets terminate, maybe with overflow/underflow.
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * it's only really an error to write too much.
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * partially filling a buffer optionally blocks queue advances
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * (so completion handlers can clean up the queue) but we don't
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * need to emulate such data-in-flight.  so we only show part
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * of the URB_SHORT_NOT_OK effect: completion status.
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (is_short) {
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (host_len == dev_len) {
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req->req.status = 0;
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, 0);
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else if (to_host) {
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req->req.status = 0;
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (dev_len > host_len)
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					maybe_set_status (urb, -EOVERFLOW);
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				else
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					maybe_set_status (urb,
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						(urb->transfer_flags
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							& URB_SHORT_NOT_OK)
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						? -EREMOTEIO : 0);
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else if (!to_host) {
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, 0);
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (host_len > dev_len)
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					req->req.status = -EOVERFLOW;
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				else
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					req->req.status = 0;
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* many requests terminate without a short packet */
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (req->req.length == req->req.actual
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					&& !req->req.zero)
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req->req.status = 0;
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (urb->transfer_buffer_length == urb->actual_length
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					&& !(urb->transfer_flags
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						& URB_ZERO_PACKET)) {
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, 0);
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* device side completion --> continuable */
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (req->req.status != -EINPROGRESS) {
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_del_init (&req->queue);
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_unlock (&dum->lock);
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			req->req.complete (&ep->ep, &req->req);
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			spin_lock (&dum->lock);
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* requests might have been unlinked... */
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rescan = 1;
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* host side completion --> terminate */
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->status != -EINPROGRESS)
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* rescan to continue with any other queued i/o */
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rescan)
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto top;
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return limit;
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	limit = ep->ep.maxpacket;
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dum->gadget.speed == USB_SPEED_HIGH) {
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int	tmp;
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* high bandwidth mode */
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp = le16_to_cpu (tmp);
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp = (tmp >> 11) & 0x03;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmp *= 8 /* applies to entire frame */;
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		limit += limit * tmp;
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return limit;
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define is_active(dum)	((dum->port_status & \
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			USB_PORT_STAT_SUSPEND)) \
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		== (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		i;
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!is_active (dum))
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((address & ~USB_DIR_IN) == 0)
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return &dum->ep [0];
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 1; i < DUMMY_ENDPOINTS; i++) {
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dummy_ep	*ep = &dum->ep [i];
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ep->desc)
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep->desc->bEndpointAddress == address)
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return ep;
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef is_active
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Dev_Request	(USB_TYPE_STANDARD | USB_RECIP_DEVICE)
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Dev_InRequest	(Dev_Request | USB_DIR_IN)
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Intf_Request	(USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Intf_InRequest	(Intf_Request | USB_DIR_IN)
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Ep_Request	(USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define Ep_InRequest	(Ep_Request | USB_DIR_IN)
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* drive both sides of the transfers; looks like irq handlers to
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * both drivers except the callbacks aren't in_irq().
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dummy_timer (unsigned long _dum)
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum = (struct dummy *) _dum;
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urbp		*urbp, *tmp;
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			limit, total;
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			i;
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* simplistic model for one frame's bandwidth */
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (dum->gadget.speed) {
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_SPEED_LOW:
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		total = 8/*bytes*/ * 12/*packets*/;
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_SPEED_FULL:
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		total = 64/*bytes*/ * 19/*packets*/;
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case USB_SPEED_HIGH:
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/;
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err (dummy_dev(dum), "bogus device speed\n");
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME if HZ != 1000 this will probably misbehave ... */
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* look at each urb queued by the host side driver */
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dum->udev) {
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_err (dummy_dev(dum),
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"timer fired with no URBs pending?\n");
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore (&dum->lock, flags);
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < DUMMY_ENDPOINTS; i++) {
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ep_name [i])
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->ep [i].already_seen = 0;
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsrestart:
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry_safe (urbp, tmp, &dum->urbp_list, urbp_list) {
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct urb		*urb;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dummy_request	*req;
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		u8			address;
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct dummy_ep		*ep = NULL;
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int			type;
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb = urbp->urb;
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->status != -EINPROGRESS) {
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* likely it was just unlinked */
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto return_urb;
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		type = usb_pipetype (urb->pipe);
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* used up this frame's non-periodic bandwidth?
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * FIXME there's infinite bandwidth for control and
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * periodic transfers ... unrealistic.
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (total <= 0 && type == PIPE_BULK)
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* find the gadget's ep for this request (if configured) */
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		address = usb_pipeendpoint (urb->pipe);
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (usb_pipein (urb->pipe))
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			address |= USB_DIR_IN;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep = find_endpoint(dum, address);
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ep) {
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* set_configuration() disagreement */
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg (dummy_dev(dum),
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"no ep configured for urb %p\n",
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				urb);
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maybe_set_status (urb, -EPROTO);
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto return_urb;
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep->already_seen)
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep->already_seen = 1;
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep == &dum->ep [0] && urb->error_count) {
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->setup_stage = 1;	/* a new urb */
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			urb->error_count = 0;
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep->halted && !ep->setup_stage) {
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* NOTE: must not be iso! */
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev_dbg (dummy_dev(dum), "ep %s halted, urb %p\n",
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ep->ep.name, urb);
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maybe_set_status (urb, -EPIPE);
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto return_urb;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* FIXME make sure both ends agree on maxpacket */
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* handle control requests */
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep == &dum->ep [0] && ep->setup_stage) {
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct usb_ctrlrequest		setup;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int				value = 1;
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct dummy_ep			*ep2;
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			setup = *(struct usb_ctrlrequest*) urb->setup_packet;
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			le16_to_cpus (&setup.wIndex);
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			le16_to_cpus (&setup.wValue);
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			le16_to_cpus (&setup.wLength);
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (setup.wLength != urb->transfer_buffer_length) {
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, -EOVERFLOW);
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto return_urb;
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* paranoia, in case of stale queued data */
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_for_each_entry (req, &ep->queue, queue) {
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				list_del_init (&req->queue);
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req->req.status = -EOVERFLOW;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev_dbg (dummy_dev(dum), "stale req = %p\n",
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						req);
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_unlock (&dum->lock);
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				req->req.complete (&ep->ep, &req->req);
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_lock (&dum->lock);
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ep->already_seen = 0;
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto restart;
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* gadget driver never sees set_address or operations
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * on standard feature flags.  some hardware doesn't
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * even expose them.
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->last_io = jiffies;
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->setup_stage = 0;
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->halted = 0;
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (setup.bRequest) {
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_REQ_SET_ADDRESS:
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (setup.bRequestType != Dev_Request)
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->address = setup.wValue;
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, 0);
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dev_dbg (dummy_dev(dum), "set_address = %d\n",
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						setup.wValue);
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				value = 0;
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_REQ_SET_FEATURE:
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (setup.bRequestType == Dev_Request) {
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					value = 0;
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					switch (setup.wValue) {
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					case USB_DEVICE_REMOTE_WAKEUP:
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
12235742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					case USB_DEVICE_B_HNP_ENABLE:
12245742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						dum->gadget.b_hnp_enable = 1;
12255742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						break;
12265742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					case USB_DEVICE_A_HNP_SUPPORT:
12275742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						dum->gadget.a_hnp_support = 1;
12285742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						break;
12295742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					case USB_DEVICE_A_ALT_HNP_SUPPORT:
12305742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						dum->gadget.a_alt_hnp_support
12315742b0c95026c817d9c266174ca39a909e8d38caAlan Stern							= 1;
12325742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						break;
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					default:
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value = -EOPNOTSUPP;
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (value == 0) {
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						dum->devstatus |=
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							(1 << setup.wValue);
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						maybe_set_status (urb, 0);
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else if (setup.bRequestType == Ep_Request) {
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					// endpoint halt
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ep2 = find_endpoint (dum,
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							setup.wIndex);
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (!ep2) {
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value = -EOPNOTSUPP;
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ep2->halted = 1;
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					value = 0;
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					maybe_set_status (urb, 0);
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_REQ_CLEAR_FEATURE:
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (setup.bRequestType == Dev_Request) {
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					switch (setup.wValue) {
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					case USB_DEVICE_REMOTE_WAKEUP:
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						dum->devstatus &= ~(1 <<
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							USB_DEVICE_REMOTE_WAKEUP);
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value = 0;
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						maybe_set_status (urb, 0);
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					default:
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value = -EOPNOTSUPP;
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else if (setup.bRequestType == Ep_Request) {
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					// endpoint halt
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ep2 = find_endpoint (dum,
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							setup.wIndex);
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (!ep2) {
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value = -EOPNOTSUPP;
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ep2->halted = 0;
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					value = 0;
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					maybe_set_status (urb, 0);
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case USB_REQ_GET_STATUS:
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (setup.bRequestType == Dev_InRequest
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						|| setup.bRequestType
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							== Intf_InRequest
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						|| setup.bRequestType
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							== Ep_InRequest
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						) {
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					char *buf;
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					// device: remote wakeup, selfpowered
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					// interface: nothing
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					// endpoint: halt
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					buf = (char *)urb->transfer_buffer;
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (urb->transfer_buffer_length > 0) {
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						if (setup.bRequestType ==
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								Ep_InRequest) {
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ep2 = find_endpoint (dum, setup.wIndex);
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ep2) {
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		value = -EOPNOTSUPP;
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf [0] = ep2->halted;
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						} else if (setup.bRequestType ==
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								Dev_InRequest) {
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buf [0] = (u8)
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								dum->devstatus;
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						} else
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							buf [0] = 0;
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (urb->transfer_buffer_length > 1)
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						buf [1] = 0;
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					urb->actual_length = min (2,
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						urb->transfer_buffer_length);
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					value = 0;
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					maybe_set_status (urb, 0);
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* gadget driver handles all other requests.  block
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * until setup() returns; no reentrancy issues etc.
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (value > 0) {
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_unlock (&dum->lock);
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				value = dum->driver->setup (&dum->gadget,
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						&setup);
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_lock (&dum->lock);
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (value >= 0) {
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* no delays (max 64KB data stage) */
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					limit = 64*1024;
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto treat_control_like_bulk;
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* error, see below */
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (value < 0) {
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (value != -EOPNOTSUPP)
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dev_dbg (dummy_dev(dum),
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						"setup --> %d\n",
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						value);
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				maybe_set_status (urb, -EPIPE);
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				urb->actual_length = 0;
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto return_urb;
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* non-control requests */
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		limit = total;
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (usb_pipetype (urb->pipe)) {
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case PIPE_ISOCHRONOUS:
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* FIXME is it urb->interval since the last xfer?
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * use urb->iso_frame_desc[i].
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * complete whether or not ep has requests queued.
13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * report random errors, to debug drivers.
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			limit = max (limit, periodic_bytes (dum, ep));
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			maybe_set_status (urb, -ENOSYS);
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case PIPE_INTERRUPT:
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* FIXME is it urb->interval since the last xfer?
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * this almost certainly polls too fast.
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			limit = max (limit, periodic_bytes (dum, ep));
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* FALLTHROUGH */
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// case PIPE_BULK:  case PIPE_CONTROL:
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		treat_control_like_bulk:
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->last_io = jiffies;
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			total = transfer (dum, urb, ep, limit);
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* incomplete transfer? */
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->status == -EINPROGRESS)
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsreturn_urb:
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb->hcpriv = NULL;
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_del (&urbp->urbp_list);
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree (urbp);
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ep)
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ep->already_seen = ep->setup_stage = 0;
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock (&dum->lock);
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_hcd_giveback_urb (dummy_to_hcd(dum), urb, NULL);
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock (&dum->lock);
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto restart;
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* want a 1 msec delay here */
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!list_empty (&dum->urbp_list))
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_put_dev (dum->udev);
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dum->udev = NULL;
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PORT_C_MASK \
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 ((1 << USB_PORT_FEAT_C_CONNECTION) \
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	| (1 << USB_PORT_FEAT_C_ENABLE) \
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	| (1 << USB_PORT_FEAT_C_SUSPEND) \
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	| (1 << USB_PORT_FEAT_C_OVER_CURRENT) \
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	| (1 << USB_PORT_FEAT_C_RESET))
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_hub_status (struct usb_hcd *hcd, char *buf)
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = hcd_to_dummy (hcd);
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(dum->port_status & PORT_C_MASK))
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = 0;
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*buf = (1 << 1);
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status);
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = 1;
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldshub_descriptor (struct usb_hub_descriptor *desc)
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset (desc, 0, sizeof *desc);
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bDescriptorType = 0x29;
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bDescLength = 9;
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001);
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bNbrPorts = 1;
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bitmap [0] = 0xff;
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	desc->bitmap [1] = 0xff;
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_hub_control (
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd	*hcd,
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		typeReq,
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wValue,
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wIndex,
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char		*buf,
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16		wLength
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds) {
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy	*dum;
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		retval = 0;
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = hcd_to_dummy (hcd);
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (typeReq) {
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ClearHubFeature:
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case ClearPortFeature:
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_SUSPEND:
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)) {
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* 20msec resume signaling */
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->resuming = 1;
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->re_timeout = jiffies +
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							msecs_to_jiffies(20);
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_POWER:
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status = 0;
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->resuming = 0;
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			stop_activity(dum, dum->driver);
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status &= ~(1 << wValue);
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetHubDescriptor:
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		hub_descriptor ((struct usb_hub_descriptor *) buf);
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetHubStatus:
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*(u32 *) buf = __constant_cpu_to_le32 (0);
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case GetPortStatus:
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (wIndex != 1)
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = -EPIPE;
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* whoever resets or resumes must GetPortStatus to
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * complete it!!
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
14991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->resuming = 0;
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->re_timeout = 0;
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dum->driver && dum->driver->resume) {
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_unlock (&dum->lock);
15041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->driver->resume (&dum->gadget);
15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_lock (&dum->lock);
15061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
15071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
15081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0
15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				&& time_after (jiffies, dum->re_timeout)) {
15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status |= (1 << USB_PORT_FEAT_C_RESET);
15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status &= ~(1 << USB_PORT_FEAT_RESET);
15121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->re_timeout = 0;
15131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dum->driver) {
15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->port_status |= USB_PORT_STAT_ENABLE;
15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* give it the best speed we agree on */
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->gadget.speed = dum->driver->speed;
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->gadget.ep0->maxpacket = 64;
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				switch (dum->gadget.speed) {
15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case USB_SPEED_HIGH:
15201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dum->port_status |=
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						USB_PORT_STAT_HIGH_SPEED;
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case USB_SPEED_LOW:
15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dum->gadget.ep0->maxpacket = 8;
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dum->port_status |=
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						USB_PORT_STAT_LOW_SPEED;
15271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
15281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				default:
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dum->gadget.speed = USB_SPEED_FULL;
15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
15311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
15331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SetHubFeature:
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -EPIPE;
15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
15401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SetPortFeature:
15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (wValue) {
15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_SUSPEND:
15431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((dum->port_status & (1 << USB_PORT_FEAT_SUSPEND))
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					== 0) {
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->port_status |=
15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						(1 << USB_PORT_FEAT_SUSPEND);
15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (dum->driver && dum->driver->suspend) {
15481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					spin_unlock (&dum->lock);
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dum->driver->suspend (&dum->gadget);
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					spin_lock (&dum->lock);
15515742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					/* HNP would happen here; for now we
15525742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					 * assume b_bus_req is always true.
15535742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					 */
15545742b0c95026c817d9c266174ca39a909e8d38caAlan Stern					if (((1 << USB_DEVICE_B_HNP_ENABLE)
15555742b0c95026c817d9c266174ca39a909e8d38caAlan Stern							& dum->devstatus) != 0)
15565742b0c95026c817d9c266174ca39a909e8d38caAlan Stern						dev_dbg (dummy_dev(dum),
15575742b0c95026c817d9c266174ca39a909e8d38caAlan Stern							"no HNP yet!\n");
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case USB_PORT_FEAT_RESET:
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* if it's already running, disconnect first */
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dum->port_status & USB_PORT_STAT_ENABLE) {
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dum->port_status &= ~(USB_PORT_STAT_ENABLE
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						| USB_PORT_STAT_LOW_SPEED
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						| USB_PORT_STAT_HIGH_SPEED);
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (dum->driver) {
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dev_dbg (dummy_dev(dum),
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							"disconnect\n");
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					stop_activity (dum, dum->driver);
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* FIXME test that code path! */
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* 50msec reset signaling */
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->re_timeout = jiffies + msecs_to_jiffies(50);
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* FALLTHROUGH */
15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dum->port_status |= (1 << wValue);
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev_dbg (dummy_dev(dum),
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"hub control req%04x v%04x i%04x l%d\n",
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			typeReq, wValue, wIndex, wLength);
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* "protocol stall" on error */
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = -EPIPE;
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline ssize_t
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsshow_urb (char *buf, size_t size, struct urb *urb)
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ep = usb_pipeendpoint (urb->pipe);
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return snprintf (buf, size,
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		"urb/%p %s ep%d%s%s len %d/%d\n",
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb,
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		({ char *s;
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 switch (urb->dev->speed) {
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_SPEED_LOW:	s = "ls"; break;
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_SPEED_FULL:	s = "fs"; break;
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case USB_SPEED_HIGH:	s = "hs"; break;
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 default:		s = "?"; break;
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 }; s; }),
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "",
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		({ char *s; \
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 switch (usb_pipetype (urb->pipe)) { \
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case PIPE_CONTROL:	s = ""; break; \
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case PIPE_BULK:	s = "-bulk"; break; \
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 case PIPE_INTERRUPT:	s = "-int"; break; \
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 default: 		s = "-iso"; break; \
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}; s;}),
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		urb->actual_length, urb->transfer_buffer_length);
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t
162510523b3b82456e416cbaffcc24ea2246980aa746Yani Ioannoushow_urbs (struct device *dev, struct device_attribute *attr, char *buf)
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd		*hcd = dev_get_drvdata (dev);
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum = hcd_to_dummy (hcd);
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urbp		*urbp;
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t			size = 0;
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		flags;
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave (&dum->lock, flags);
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	list_for_each_entry (urbp, &dum->urbp_list, urbp_list) {
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size_t		temp;
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		temp = show_urb (buf, PAGE_SIZE - size, urbp->urb);
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf += temp;
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size += temp;
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore (&dum->lock, flags);
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return size;
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL);
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_start (struct usb_hcd *hcd)
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = hcd_to_dummy (hcd);
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * MASTER side init ... we emulate a root hub that'll only ever
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * talk to one device (the slave side).  Also appears in sysfs,
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * just like more familiar pci-based HCDs.
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init (&dum->lock);
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_timer (&dum->timer);
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->timer.function = dummy_timer;
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum->timer.data = (unsigned long) dum;
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD (&dum->urbp_list);
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1666247f3105636caa9d1d8a4c3dfb755de42633bc80Alan Stern	if ((retval = dummy_register_udc (dum)) != 0)
1667247f3105636caa9d1d8a4c3dfb755de42633bc80Alan Stern		return retval;
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1669bc96c0ad1ed0c938fefc0423aa99f086c5a2a1eaAlan Stern	/* only show a low-power port: just 8mA */
1670bc96c0ad1ed0c938fefc0423aa99f086c5a2a1eaAlan Stern	hcd->power_budget = 8;
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hcd->state = HC_STATE_RUNNING;
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16735742b0c95026c817d9c266174ca39a909e8d38caAlan Stern#ifdef CONFIG_USB_OTG
16745742b0c95026c817d9c266174ca39a909e8d38caAlan Stern	hcd->self.otg_port = 1;
16755742b0c95026c817d9c266174ca39a909e8d38caAlan Stern#endif
16765742b0c95026c817d9c266174ca39a909e8d38caAlan Stern
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_create_file (dummy_dev(dum), &dev_attr_urbs);
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dummy_stop (struct usb_hcd *hcd)
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dummy		*dum;
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dum = hcd_to_dummy (hcd);
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	device_remove_file (dummy_dev(dum), &dev_attr_urbs);
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_gadget_unregister_driver (dum->driver);
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dummy_unregister_udc (dum);
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info (dummy_dev(dum), "stopped\n");
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_h_get_frame (struct usb_hcd *hcd)
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dummy_g_get_frame (NULL);
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct hc_driver dummy_hcd = {
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.description =		(char *) driver_name,
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.product_desc =		"Dummy host controller",
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hcd_priv_size =	sizeof(struct dummy),
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flags =		HCD_USB2,
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.start =		dummy_start,
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.stop =			dummy_stop,
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.urb_enqueue = 		dummy_urb_enqueue,
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.urb_dequeue = 		dummy_urb_dequeue,
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_frame_number = 	dummy_h_get_frame,
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hub_status_data = 	dummy_hub_status,
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hub_control = 		dummy_hub_control,
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_probe (struct device *dev)
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd		*hcd;
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_info (dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hcd = usb_create_hcd (&dummy_hcd, dev, dev->bus_id);
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!hcd)
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the_controller = hcd_to_dummy (hcd);
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_add_hcd(hcd, 0, 0);
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval != 0) {
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		usb_put_hcd (hcd);
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		the_controller = NULL;
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dummy_remove (struct device *dev)
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_hcd		*hcd;
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hcd = dev_get_drvdata (dev);
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_remove_hcd (hcd);
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_put_hcd (hcd);
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the_controller = NULL;
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
17531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dummy_pdev_detect (void)
17551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval;
17571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = driver_register (&dummy_driver);
17591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval < 0)
17601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the_pdev.name = "hc";
17631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the_pdev.dev.driver = &dummy_driver;
17641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the_pdev.dev.release = dummy_pdev_release;
17651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = platform_device_register (&the_pdev);
17671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval < 0)
17681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		driver_unregister (&dummy_driver);
17691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
17701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dummy_pdev_remove (void)
17731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	platform_device_unregister (&the_pdev);
17751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister (&dummy_driver);
17761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*-------------------------------------------------------------------------*/
17791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init (void)
17811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	retval;
17831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (usb_disabled ())
17851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
17861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = dummy_pdev_detect ()) != 0)
17871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
17881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((retval = dummy_probe (&the_pdev.dev)) != 0)
17891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dummy_pdev_remove ();
17901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
17911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init (init);
17931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup (void)
17951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dummy_remove (&the_pdev.dev);
17971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dummy_pdev_remove ();
17981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit (cleanup);
1800