patch-2.2.18 linux/drivers/usb/usb.c

Next file: linux/drivers/usb/usb.h
Previous file: linux/drivers/usb/usb-uhci.h
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/usb/usb.c linux/drivers/usb/usb.c
@@ -1,7 +1,12 @@
 /*
- * driver/usb/usb.c
+ * drivers/usb/usb.c
  *
  * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -10,328 +15,1431 @@
  * Think of this as a "USB library" rather than anything else.
  * It should be considered a slave, with no callbacks. Callbacks
  * are evil.
- */
-
-/*
- * Table 9-2
  *
- * Offset	Field		Size	Value		Desc
- * 0		bmRequestType	1	Bitmap		D7:	Direction
- *							0 = Host-to-device
- *							1 = Device-to-host
- *							D6..5:	Type
- *							0 = Standard
- *							1 = Class
- *							2 = Vendor
- *							3 = Reserved
- *							D4..0:	Recipient
- *							0 = Device
- *							1 = Interface
- *							2 = Endpoint
- *							3 = Other
- *							4..31 = Reserved
- * 1		bRequest	1	Value		Specific request (9-3)
- * 2		wValue		2	Value		Varies
- * 4		wIndex		2	Index/Offset	Varies
- * 6		wLength		2	Count		Bytes for data
+ * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $
  */
 
+#include <linux/config.h>
+#include <linux/module.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>  /* for in_interrupt() */
 
-#include "usb.h"
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* waitpid() call glue uses this */
+static int errno;
+#endif
+
+
+#ifdef CONFIG_USB_DEBUG
+	#define DEBUG
+#else
+	#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+static const int usb_bandwidth_option =
+#ifdef CONFIG_USB_BANDWIDTH
+				1;
+#else
+				0;
+#endif
+
+/*
+ * Prototypes for the device driver probing/loading functions
+ */
+static void usb_find_drivers(struct usb_device *);
+static int  usb_find_interface_driver(struct usb_device *, unsigned int);
+static void usb_check_support(struct usb_device *);
 
 /*
  * We have a per-interface "registered driver" list.
  */
-static LIST_HEAD(usb_driver_list);
+LIST_HEAD(usb_driver_list);
+LIST_HEAD(usb_bus_list);
+
+static struct usb_busmap busmap;
+
+static struct usb_driver *usb_minors[16];
 
 int usb_register(struct usb_driver *new_driver)
 {
+	if (new_driver->fops != NULL) {
+		if (usb_minors[new_driver->minor/16]) {
+			 err("error registering %s driver", new_driver->name);
+			return -EINVAL;
+		}
+		usb_minors[new_driver->minor/16] = new_driver;
+	}
+
+	info("registered new driver %s", new_driver->name);
+
+	init_MUTEX(&new_driver->serialize);
+
 	/* Add it to the list of known drivers */
 	list_add(&new_driver->driver_list, &usb_driver_list);
 
-	/*
-	 * We should go through all existing devices, and see if any of
-	 * them would be acceptable to the new driver.. Let's do that
-	 * in version 2.0.
-	 */
+	usb_scan_devices();
+
 	return 0;
 }
 
+/*
+ * We go through all existing devices, and see if any of them would
+ * be acceptable to the new driver.. This is done using a depth-first
+ * search for devices without a registered driver already, then 
+ * running 'probe' with each of the drivers registered on every one 
+ * of these.
+ */
+void usb_scan_devices(void)
+{
+	struct list_head *tmp;
+
+	tmp = usb_bus_list.next;
+	while (tmp != &usb_bus_list) {
+		struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list);
+
+		tmp = tmp->next;
+		usb_check_support(bus->root_hub);
+	}
+}
+
+/*
+ * This function is part of a depth-first search down the device tree,
+ * removing any instances of a device driver.
+ */
+static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
+{
+	int i;
+
+	if (!dev) {
+		err("null device being purged!!!");
+		return;
+	}
+
+	for (i=0; i<USB_MAXCHILDREN; i++)
+		if (dev->children[i])
+			usb_drivers_purge(driver, dev->children[i]);
+
+	if (!dev->actconfig)
+		return;
+			
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *interface = &dev->actconfig->interface[i];
+		
+		if (interface->driver == driver) {
+			down(&driver->serialize);
+			driver->disconnect(dev, interface->private_data);
+			up(&driver->serialize);
+			usb_driver_release_interface(driver, interface);
+			/*
+			 * This will go through the list looking for another
+			 * driver that can handle the device
+			 */
+			usb_find_interface_driver(dev, i);
+		}
+	}
+}
+
+/*
+ * Unlink a driver from the driver list when it is unloaded
+ */
 void usb_deregister(struct usb_driver *driver)
 {
+	struct list_head *tmp;
+
+	info("deregistering driver %s", driver->name);
+	if (driver->fops != NULL)
+		usb_minors[driver->minor/16] = NULL;
+
+	/*
+	 * first we remove the driver, to be sure it doesn't get used by
+	 * another thread while we are stepping through removing entries
+	 */
 	list_del(&driver->driver_list);
+
+	tmp = usb_bus_list.next;
+	while (tmp != &usb_bus_list) {
+		struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list);
+
+		tmp = tmp->next;
+		usb_drivers_purge(driver, bus->root_hub);
+	}
+}
+
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+	int i;
+
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+		if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum)
+			return &dev->actconfig->interface[i];
+
+	return NULL;
+}
+
+/*
+ * usb_calc_bus_time:
+ *
+ * returns (approximate) USB bus time in nanoseconds for a USB transaction.
+ */
+static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount)
+{
+	unsigned long	tmp;
+
+	if (low_speed)		/* no isoc. here */
+	{
+		if (input_dir)
+		{
+			tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+		else
+		{
+			tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+	}
+
+	/* for full-speed: */
+
+	if (!isoc)		/* Input or Output */
+	{
+		tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+		return (9107L + BW_HOST_DELAY + tmp);
+	} /* end not Isoc */
+
+	/* for isoc: */
+
+	tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+	return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+}
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc.  Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ * However, this first cut at USB bandwidth allocation does not
+ * contain any frame allocation tracking.
+ */
+int usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+	int		new_alloc;
+	int		old_alloc = dev->bus->bandwidth_allocated;
+	unsigned int	pipe = urb->pipe;
+	long		bustime;
+
+	bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe),
+			usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+	if (usb_pipeisoc(pipe))
+		bustime = NS_TO_US(bustime) / urb->number_of_packets;
+	else
+		bustime = NS_TO_US(bustime);
+
+	new_alloc = old_alloc + (int)bustime;
+		/* what new total allocated bus time would be */
+
+	if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC)
+		dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us",
+			usb_bandwidth_option ? "" : "would have ",
+			old_alloc, new_alloc, bustime);
+
+	if (!usb_bandwidth_option)	/* don't enforce it */
+		return (bustime);
+	return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR;
+}
+
+void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+	dev->bus->bandwidth_allocated += bustime;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs++;
+	else
+		dev->bus->bandwidth_int_reqs++;
+	urb->bandwidth = bustime;
+	
+	dbg("bw_alloc increased by %d to %d for %d requesters",
+		bustime,
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release a pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc)
+{
+	dev->bus->bandwidth_allocated -= urb->bandwidth;
+	if (isoc)
+		dev->bus->bandwidth_isoc_reqs--;
+	else
+		dev->bus->bandwidth_int_reqs--;
+
+	dbg("bw_alloc reduced by %d to %d for %d requesters",
+		urb->bandwidth,
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+	urb->bandwidth = 0;
+}
+
+/*
+ * New functions for (de)registering a controller
+ */
+struct usb_bus *usb_alloc_bus(struct usb_operations *op)
+{
+	struct usb_bus *bus;
+
+	bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		return NULL;
+
+	memset(&bus->devmap, 0, sizeof(struct usb_devmap));
+
+	bus->op = op;
+	bus->root_hub = NULL;
+	bus->hcpriv = NULL;
+	bus->busnum = -1;
+	bus->bandwidth_allocated = 0;
+	bus->bandwidth_int_reqs  = 0;
+	bus->bandwidth_isoc_reqs = 0;
+
+	INIT_LIST_HEAD(&bus->bus_list);
+	INIT_LIST_HEAD(&bus->inodes);
+
+	return bus;
+}
+
+void usb_free_bus(struct usb_bus *bus)
+{
+	if (!bus)
+		return;
+
+	kfree(bus);
+}
+
+void usb_register_bus(struct usb_bus *bus)
+{
+	int busnum;
+
+	busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1);
+	if (busnum < USB_MAXBUS) {
+		set_bit(busnum, busmap.busmap);
+		bus->busnum = busnum;
+	} else
+		warn("too many buses");
+
+	/* Add it to the list of buses */
+	list_add(&bus->bus_list, &usb_bus_list);
+
+	usbdevfs_add_bus(bus);
+
+	info("new USB bus registered, assigned bus number %d", bus->busnum);
+}
+
+void usb_deregister_bus(struct usb_bus *bus)
+{
+	info("USB bus %d deregistered", bus->busnum);
+
+	/*
+	 * NOTE: make sure that all the devices are removed by the
+	 * controller code, as well as having it call this when cleaning
+	 * itself up
+	 */
+	list_del(&bus->bus_list);
+
+        usbdevfs_remove_bus(bus);
+
+	clear_bit(bus->busnum, busmap.busmap);
+}
+
+/*
+ * This function is for doing a depth-first search for devices which
+ * have support, for dynamic loading of driver modules.
+ */
+static void usb_check_support(struct usb_device *dev)
+{
+	int i;
+
+	if (!dev) {
+		err("null device being checked!!!");
+		return;
+	}
+
+	for (i=0; i<USB_MAXCHILDREN; i++)
+		if (dev->children[i])
+			usb_check_support(dev->children[i]);
+
+	if (!dev->actconfig)
+		return;
+
+	/* now we check this device */
+	if (dev->devnum > 0)
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+			usb_find_interface_driver(dev, i);
+}
+
+
+/*
+ * This is intended to be used by usb device drivers that need to
+ * claim more than one interface on a device at once when probing
+ * (audio and acm are good examples).  No device driver should have
+ * to mess with the internal usb_interface or usb_device structure
+ * members.
+ */
+void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+{
+	if (!iface || !driver)
+		return;
+
+	dbg("%s driver claimed interface %p", driver->name, iface);
+
+	iface->driver = driver;
+	iface->private_data = priv;
+} /* usb_driver_claim_interface() */
+
+/*
+ * This should be used by drivers to check other interfaces to see if
+ * they are available or not.
+ */
+int usb_interface_claimed(struct usb_interface *iface)
+{
+	if (!iface)
+		return 0;
+
+	return (iface->driver != NULL);
+} /* usb_interface_claimed() */
+
+/*
+ * This should be used by drivers to release their claimed interfaces
+ */
+void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
+{
+	/* this should never happen, don't release something that's not ours */
+	if (!iface || iface->driver != driver)
+		return;
+
+	iface->driver = NULL;
+	iface->private_data = NULL;
 }
 
 /*
  * This entrypoint gets called for each new device.
  *
  * We now walk the list of registered USB drivers,
- * looking for one that will accept this device as
- * his..
+ * looking for one that will accept this interface.
+ *
+ * The probe return value is changed to be a private pointer.  This way
+ * the drivers don't have to dig around in our structures to set the
+ * private pointer if they only need one interface. 
+ *
+ * Returns: 0 if a driver accepted the interface, -1 otherwise
  */
-void usb_device_descriptor(struct usb_device *dev)
+static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
 {
 	struct list_head *tmp = usb_driver_list.next;
+	struct usb_interface *interface;
+	
+	if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
+		err("bad find_interface_driver params");
+		return -1;
+	}
+
+	interface = dev->actconfig->interface + ifnum;
+
+	if (usb_interface_claimed(interface))
+		return -1;
 
 	while (tmp != &usb_driver_list) {
-		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+		void *private;
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+		  				       driver_list);
+			
 		tmp = tmp->next;
-		if (driver->probe(dev))
+		down(&driver->serialize);
+		private = driver->probe(dev, ifnum);
+		up(&driver->serialize);
+		if (!private)
 			continue;
-		dev->driver = driver;
+		usb_driver_claim_interface(driver, interface, private);
+
+		return 0;
+	}
+	
+	return -1;
+}
+
+
+#if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ */
+
+static int exec_helper (void *arg)
+{
+	void **params = (void **) arg;
+	char *path = (char *) params [0];
+	char **argv = (char **) params [1];
+	char **envp = (char **) params [2];
+	return exec_usermodehelper (path, argv, envp);
+}
+
+int call_usermodehelper (char *path, char **argv, char **envp)
+{
+	void *params [3] = { path, argv, envp };
+	int pid, pid2, retval;
+	mm_segment_t fs;
+
+	if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) {
+		err ("failed fork of %s, errno = %d", argv [0], -pid);
+		return -1;
+	}
+
+	/* set signal mask? */
+	fs = get_fs ();
+	set_fs (KERNEL_DS);				/* retval is in kernel space. */
+	pid2 = waitpid (pid, &retval, __WCLONE);	/* "errno" gets assigned */
+	set_fs (fs);
+	/* restore signal mask? */
+
+	if (pid2 != pid) {
+		err ("waitpid(%d) failed, returned %d\n", pid, pid2);
+			return -1;
+	}
+	return retval;
+}
+
+static int to_bcd (char *buf, __u16 *bcdValue)
+{
+	int	retval = 0;
+	char	*value = (char *) bcdValue;
+	int	temp;
+
+	/* digits are 0-9 then ":;<=>?" for devices using
+	 * non-bcd (non-standard!) values here ... */
+
+	/* No leading (or later, trailing) zeroes since scripts do
+	 * literal matches, and that's how they're doing them. */
+	if ((temp = value [1] & 0xf0) != 0) {
+		temp >>= 4;
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+
+	temp = value [1] & 0x0f;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	*buf++ = '.';
+	retval++;
+
+	temp = value [0] & 0xf0;
+	temp >>= 4;
+	temp += '0';
+	*buf++ = (char) temp;
+	retval++;
+
+	if ((temp = value [0] & 0x0f) != 0) {
+		temp += '0';
+		*buf++ = (char) temp;
+		retval++;
+	}
+	*buf++ = 0;
+
+	return retval;
+}
+
+/*
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, or both.
+ *
+ * Some synchronization is important: removes can't start processing
+ * before the add-device processing completes, and vice versa.  That keeps
+ * a stack of USB-related identifiers stable while they're in use.  If we
+ * know that agents won't complete after they return (such as by forking
+ * a process that completes later), it's enough to just waitpid() for the
+ * agent -- as is currently done.
+ *
+ * The reason: we know we're called either from khubd (the typical case)
+ * or from root hub initialization (init, kapmd, modprobe, etc).  In both
+ * cases, we know no other thread can recycle our address, since we must
+ * already have been serialized enough to prevent that.
+ */
+static void call_policy (char *verb, struct usb_device *dev)
+{
+	char *argv [3], **envp, *buf, *scratch;
+	int i = 0, value;
+
+	if (!hotplug_path [0])
+		return;
+	if (in_interrupt ()) {
+		dbg ("In_interrupt");
+		return;
+	}
+	if (!current->fs->root) {
+		/* statically linked USB is initted rather early */
+		dbg ("call_policy %s, num %d -- no FS yet", verb, dev->devnum);
+		return;
+	}
+	if (dev->devnum < 0) {
+		dbg ("device already deleted ??");
+		return;
+	}
+	if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) {
+		dbg ("enomem");
+		return;
+	}
+	if (!(buf = kmalloc (256, GFP_KERNEL))) {
+		kfree (envp);
+		dbg ("enomem2");
 		return;
 	}
 
-	/*
-	 * Ok, no driver accepted the device, so show the info
-	 * for debugging..
+	/* only one standardized param to hotplug command: type */
+	argv [0] = hotplug_path;
+	argv [1] = "usb";
+	argv [2] = 0;
+
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef	DEBUG
+	/* hint that policy agent should enter no-stdout debug mode */
+	envp [i++] = "DEBUG=kernel";
+#endif
+	/* extensible set of named bus-specific parameters,
+	 * supporting multiple driver selection algorithms.
 	 */
-	printk("Unknown new USB device:\n");
-	usb_show_device(dev);
+	scratch = buf;
+
+	/* action:  add, remove */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "ACTION=%s", verb) + 1;
+
+#ifdef	CONFIG_USB_DEVICEFS
+	/* If this is available, userspace programs can directly read
+	 * all the device descriptors we don't tell them about.  Or
+	 * even act as usermode drivers.
+	 *
+	 * XXX how little intelligence can we hardwire?
+	 * (a) mount point: /devfs, /dev, /proc/bus/usb etc.
+	 * (b) naming convention: bus1/device3, 001/003 etc.
+	 */
+	envp [i++] = "DEVFS=/proc/bus/usb";
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d",
+		dev->bus->busnum, dev->devnum) + 1;
+#endif
+
+	/* per-device configuration hacks are often necessary */
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "PRODUCT=%x/%x/",
+		dev->descriptor.idVendor,
+		dev->descriptor.idProduct);
+	scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1;
+
+	/* otherwise, use a simple (so far) generic driver binding model */
+	envp [i++] = scratch;
+	if (dev->descriptor.bDeviceClass == 0) {
+		int alt = dev->actconfig->interface [0].act_altsetting;
+
+		/* simple/common case: one config, one interface, one driver
+		 * unsimple cases:  everything else
+		 */
+		scratch += sprintf (scratch, "INTERFACE=%d/%d/%d",
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass,
+			dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol)
+			+ 1;
+		/* INTERFACE-0, INTERFACE-1, ... ? */
+	} else {
+		/* simple/common case: generic device, handled generically */
+		scratch += sprintf (scratch, "TYPE=%d/%d/%d",
+			dev->descriptor.bDeviceClass,
+			dev->descriptor.bDeviceSubClass,
+			dev->descriptor.bDeviceProtocol) + 1;
+	}
+	envp [i++] = 0;
+	/* assert: (scratch - buf) < sizeof buf */
+
+	/* NOTE: user mode daemons can call the agents too */
+
+	dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum);
+	value = call_usermodehelper (argv [0], argv, envp);
+	kfree (buf);
+	kfree (envp);
+	dbg ("kusbd policy returned 0x%x", value);
+}
+
+#else
+
+static inline void
+call_policy (char *verb, struct usb_device *dev)
+{ } 
+
+#endif	/* KMOD && HOTPLUG */
+
+
+/*
+ * This entrypoint gets called for each new device.
+ *
+ * All interfaces are scanned for matching drivers.
+ */
+static void usb_find_drivers(struct usb_device *dev)
+{
+	unsigned ifnum;
+	unsigned rejected = 0;
+	unsigned claimed = 0;
+
+	for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
+		/* if this interface hasn't already been claimed */
+		if (!usb_interface_claimed(dev->actconfig->interface + ifnum)) {
+			if (usb_find_interface_driver(dev, ifnum))
+				rejected++;
+			else
+				claimed++;
+		}
+	}
+ 
+	if (rejected)
+		dbg("unhandled interfaces on device");
+
+	if (!claimed) {
+		warn("USB device %d (prod/vend 0x%x/0x%x) is not claimed by any active driver.",
+			dev->devnum,
+			dev->descriptor.idVendor,
+			dev->descriptor.idProduct);
+#ifdef DEBUG
+		usb_show_device(dev);
+#endif
+	}
 }
 
 /*
- * Parse the fairly incomprehensible output of
- * the USB configuration data, and build up the
- * USB device database.
+ * Only HC's should call usb_alloc_dev and usb_free_dev directly
+ * Anybody may use usb_inc_dev_use or usb_dec_dev_use
  */
-static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
+struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
 {
-	int parsed = 0;
-	int n_len;
-	unsigned short n_desc;
+	struct usb_device *dev;
+
+	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	memset(dev, 0, sizeof(*dev));
 
-	for (;;) {
-		int i;
+	dev->bus = bus;
+	dev->parent = parent;
+	atomic_set(&dev->refcnt, 1);
+	INIT_LIST_HEAD(&dev->inodes);
+	INIT_LIST_HEAD(&dev->filelist);
 
-		if (len < descindex)
+	dev->bus->op->allocate(dev);
+
+	return dev;
+}
+
+void usb_free_dev(struct usb_device *dev)
+{
+	if (atomic_dec_and_test(&dev->refcnt)) {
+		dev->bus->op->deallocate(dev);
+		usb_destroy_configuration(dev);
+		kfree(dev);
+	}
+}
+
+void usb_inc_dev_use(struct usb_device *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+/* ------------------------------------------------------------------------------------- 
+ * New USB Core Functions
+ * -------------------------------------------------------------------------------------*/
+
+urb_t *usb_alloc_urb(int iso_packets)
+{
+	urb_t *urb;
+
+	urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t),
+	      in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	if (!urb) {
+		err("alloc_urb: kmalloc failed");
+		return NULL;
+	}
+
+	memset(urb, 0, sizeof(*urb));
+
+	spin_lock_init(&urb->lock);
+
+	return urb;
+}
+
+/*-------------------------------------------------------------------*/
+void usb_free_urb(urb_t* urb)
+{
+	if (urb)
+		kfree(urb);
+}
+/*-------------------------------------------------------------------*/
+int usb_submit_urb(urb_t *urb)
+{
+	if (urb && urb->dev)
+		return urb->dev->bus->op->submit_urb(urb);
+	else
+		return -ENODEV;
+}
+
+/*-------------------------------------------------------------------*/
+int usb_unlink_urb(urb_t *urb)
+{
+	if (urb && urb->dev)
+		return urb->dev->bus->op->unlink_urb(urb);
+	else
+		return -ENODEV;
+}
+/*-------------------------------------------------------------------*
+ *                     COMPLETION HANDLERS                           *
+ *-------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------*
+ * completion handler for compatibility wrappers (sync control/bulk) *
+ *-------------------------------------------------------------------*/
+static void usb_api_blocking_completion(urb_t *urb)
+{
+	api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+	if (waitqueue_active(awd->wakeup))
+		wake_up(awd->wakeup);
+#if 0
+	else
+		dbg("(blocking_completion): waitqueue empty!"); 
+		// even occurs if urb was unlinked by timeout...
+#endif
+}
+
+/*-------------------------------------------------------------------*
+ *                         COMPATIBILITY STUFF                       *
+ *-------------------------------------------------------------------*/
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length)
+{ 
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+  
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	current->state = TASK_INTERRUPTIBLE;
+	add_wait_queue(&wqh, &wait);
+	urb->context = &awd;
+	status = usb_submit_urb(urb);
+	if (status) {
+		// something went wrong
+		usb_free_urb(urb);
+		remove_wait_queue(&wqh, &wait);
+		return status;
+	}
+
+	if (urb->status == -EINPROGRESS) {
+		while (timeout && urb->status == -EINPROGRESS)
+			status = timeout = schedule_timeout(timeout);
+	} else
+		status = 1;
+
+	remove_wait_queue(&wqh, &wait);
+
+	if (!status) {
+		// timeout
+		printk("usb_control/bulk_msg: timeout\n");
+		usb_unlink_urb(urb);  // remove urb safely
+		status = -ETIMEDOUT;
+	} else
+		status = urb->status;
+
+	if (actual_length)
+		*actual_length = urb->actual_length;
+
+	usb_free_urb(urb);
+  	return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, 
+			    devrequest *cmd,  void *data, int len, int timeout)
+{
+	urb_t *urb;
+	int retv;
+	int length;
+
+	urb = usb_alloc_urb(0);
+	if (!urb)
+		return -ENOMEM;
+  
+	FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len,    /* build urb */  
+		   (usb_complete_t)usb_api_blocking_completion,0);
+
+	retv = usb_start_wait_urb(urb, timeout, &length);
+	if (retv < 0)
+		return retv;
+	else
+		return length;
+	
+}
+
+/*-------------------------------------------------------------------*/
+int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+			 __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+	devrequest *dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	int ret;
+	
+	if (!dr)
+		return -ENOMEM;
+
+	dr->requesttype = requesttype;
+	dr->request = request;
+	dr->value = cpu_to_le16p(&value);
+	dr->index = cpu_to_le16p(&index);
+	dr->length = cpu_to_le16p(&size);
+
+	//dbg("usb_control_msg");	
+
+	ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+	kfree(dr);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------*/
+/* compatibility wrapper, builds bulk urb, and waits for completion */
+/* synchronous behavior */
+
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, 
+			void *data, int len, int *actual_length, int timeout)
+{
+	urb_t *urb;
+
+	if (len < 0)
+		return -EINVAL;
+
+	urb=usb_alloc_urb(0);
+	if (!urb)
+		return -ENOMEM;
+
+	FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len,   /* build urb */
+			(usb_complete_t)usb_api_blocking_completion,0);
+
+	return usb_start_wait_urb(urb,timeout,actual_length);
+}
+
+/*
+ * usb_get_current_frame_number()
+ *
+ * returns the current frame number for the parent USB bus/controller
+ * of the given USB device.
+ */
+int usb_get_current_frame_number(struct usb_device *usb_dev)
+{
+	return usb_dev->bus->op->get_frame_number (usb_dev);
+}
+/*-------------------------------------------------------------------*/
+
+static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size)
+{
+	struct usb_descriptor_header *header;
+	unsigned char *begin;
+	int parsed = 0, len, numskipped;
+
+	header = (struct usb_descriptor_header *)buffer;
+
+	/* Everything should be fine being passed into here, but we sanity */
+	/*  check JIC */
+	if (header->bLength > size) {
+		err("ran out of descriptors parsing");
+		return -1;
+	}
+		
+	if (header->bDescriptorType != USB_DT_ENDPOINT) {
+		warn("unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X",
+			endpoint->bDescriptorType, USB_DT_ENDPOINT);
+		return parsed;
+	}
+
+	if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE)
+		memcpy(endpoint, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
+	else
+		memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE);
+	
+	le16_to_cpus(&endpoint->wMaxPacketSize);
+
+	buffer += header->bLength;
+	size -= header->bLength;
+	parsed += header->bLength;
+
+	/* Skip over the rest of the Class Specific or Vendor Specific */
+	/*  descriptors */
+	begin = buffer;
+	numskipped = 0;
+	while (size >= sizeof(struct usb_descriptor_header)) {
+		header = (struct usb_descriptor_header *)buffer;
+
+		if (header->bLength < 2) {
+			err("invalid descriptor length of %d", header->bLength);
 			return -1;
-		n_desc = *(unsigned short *)ptr;
-		n_len = n_desc & 0xff;
+		}
+
+		/* If we find another descriptor which is at or below us */
+		/*  in the descriptor heirarchy then we're done  */
+		if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+		    (header->bDescriptorType == USB_DT_INTERFACE) ||
+		    (header->bDescriptorType == USB_DT_CONFIG) ||
+		    (header->bDescriptorType == USB_DT_DEVICE))
+			break;
+
+		dbg("skipping descriptor 0x%X",
+			header->bDescriptorType);
+		numskipped++;
+
+		buffer += header->bLength;
+		size -= header->bLength;
+		parsed += header->bLength;
+	}
+	if (numskipped)
+		dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+	/* Copy any unknown descriptors into a storage area for drivers */
+	/*  to later parse */
+	len = (int)(buffer - begin);
+	if (!len) {
+		endpoint->extra = NULL;
+		endpoint->extralen = 0;
+		return parsed;
+	}
+
+	endpoint->extra = kmalloc(len, GFP_KERNEL);
+
+	if (!endpoint->extra) {
+		err("couldn't allocate memory for endpoint extra descriptors");
+		endpoint->extralen = 0;
+		return parsed;
+	}
 
-		if (n_desc == ((desctype << 8) + descindex))
-			break;
+	memcpy(endpoint->extra, begin, len);
+	endpoint->extralen = len;
 
-		if (((n_desc >> 8)&0xFF) == desctype &&
-			n_len > descindex)
-		{
-			printk("bug: oversized descriptor.\n");
-			break;
-		}
-			
-		if (n_len < 2 || n_len > len)
-		{
-			printk("Short descriptor.\n");
-			return -1;
-		}
-		printk(
-		"Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
-			desctype, descindex,
-			(n_desc >> 8) & 0xFF, n_desc & 0xFF);
-		for (i = 0 ; i < n_len; i++)
-			printk("   %d %02x\n", i, ptr[i]);
-		len -= n_len;
-		ptr += n_len;
-		parsed += n_len;
-	}
-	
-	printk("Found %02X:%02X\n",
-		desctype, descindex);
 	return parsed;
 }
 
-/*
- * Parse the even more incomprehensible mess made of the USB spec
- * by USB audio having private magic to go with it.
- */
- 
-static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desctype)
+static int usb_parse_interface(struct usb_device *dev, struct usb_interface *interface, unsigned char *buffer, int size)
 {
-	int n_len = ptr[0];
+	int i, len, numskipped, retval, parsed = 0;
+	struct usb_descriptor_header *header;
+	struct usb_interface_descriptor *ifp;
+	unsigned char *begin;
 
-	if (n_len < 2 || n_len > len)
-	{
-		printk("Short descriptor.\n");
+	interface->act_altsetting = 0;
+	interface->num_altsetting = 0;
+	interface->max_altsetting = USB_ALTSETTINGALLOC;
+
+	interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+	
+	if (!interface->altsetting) {
+		err("couldn't kmalloc interface->altsetting");
 		return -1;
 	}
 
-	if (ptr[1] == desctype)
-		return 0;
-		
-	return -1;
-}
+	while (size > 0) {
+		if (interface->num_altsetting >= interface->max_altsetting) {
+			void *ptr;
+			int oldmas;
+
+			oldmas = interface->max_altsetting;
+			interface->max_altsetting += USB_ALTSETTINGALLOC;
+			if (interface->max_altsetting > USB_MAXALTSETTING) {
+				warn("too many alternate settings (max %d)",
+					USB_MAXALTSETTING);
+				return -1;
+			}
+
+			ptr = interface->altsetting;
+			interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+			if (!interface->altsetting) {
+				err("couldn't kmalloc interface->altsetting");
+				interface->altsetting = ptr;
+				return -1;
+			}
+			memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas);
 
+			kfree(ptr);
+		}
 
-static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
-{
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);
-	int i;
+		ifp = interface->altsetting + interface->num_altsetting;
+		interface->num_altsetting++;
 
-	if (parsed < 0)
-		return parsed;
-	memcpy(endpoint, ptr + parsed, ptr[parsed]);
+		memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);
 
-	parsed += ptr[parsed];
-	len -= ptr[parsed];
+		/* Skip over the interface */
+		buffer += ifp->bLength;
+		parsed += ifp->bLength;
+		size -= ifp->bLength;
+
+		begin = buffer;
+		numskipped = 0;
+
+		/* Skip over any interface, class or vendor descriptors */
+		while (size >= sizeof(struct usb_descriptor_header)) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if (header->bLength < 2) {
+				err("invalid descriptor length of %d", header->bLength);
+				return -1;
+			}
+
+			/* If we find another descriptor which is at or below */
+			/*  us in the descriptor heirarchy then return */
+			if ((header->bDescriptorType == USB_DT_INTERFACE) ||
+			    (header->bDescriptorType == USB_DT_ENDPOINT) ||
+			    (header->bDescriptorType == USB_DT_CONFIG) ||
+			    (header->bDescriptorType == USB_DT_DEVICE))
+				break;
 
-	while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0)
-	{
-		usb_audio_endpoint(endpoint, ptr+parsed+i);
-		len -= ptr[parsed+i];
-		parsed += ptr[parsed+i];
-	}
-	
-	return parsed;// + ptr[parsed];
-}
+			numskipped++;
 
-static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
-{
-	int i;
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
-	int retval;
+			buffer += header->bLength;
+			parsed += header->bLength;
+			size -= header->bLength;
+		}
 
-	if (parsed < 0)
-		return parsed;
+		if (numskipped)
+			dbg("skipped %d class/vendor specific interface descriptors", numskipped);
 
-	memcpy(interface, ptr + parsed, *ptr);
-	len -= ptr[parsed];
-	parsed += ptr[parsed];
+		/* Copy any unknown descriptors into a storage area for */
+		/*  drivers to later parse */
+		len = (int)(buffer - begin);
+		if (!len) {
+			ifp->extra = NULL;
+			ifp->extralen = 0;
+		} else {
+			ifp->extra = kmalloc(len, GFP_KERNEL);
+
+			if (!ifp->extra) {
+				err("couldn't allocate memory for interface extra descriptors");
+				ifp->extralen = 0;
+				return -1;
+			}
+			memcpy(ifp->extra, begin, len);
+			ifp->extralen = len;
+		}
 
-	while((i=usb_check_descriptor(ptr+parsed, len, 0x24))>=0)
-	{
-		usb_audio_interface(interface, ptr+parsed+i);
-		len -= ptr[parsed+i];
-		parsed += ptr[parsed+i];
-	}
-	
-	if (interface->bNumEndpoints > USB_MAXENDPOINTS)
-	{
-		printk(KERN_WARNING "usb: too many endpoints.\n");
-		return -1;
-	}
+		/* Did we hit an unexpected descriptor? */
+		header = (struct usb_descriptor_header *)buffer;
+		if ((size >= sizeof(struct usb_descriptor_header)) &&
+		    ((header->bDescriptorType == USB_DT_CONFIG) ||
+		     (header->bDescriptorType == USB_DT_DEVICE)))
+			return parsed;
 
-	interface->endpoint = (struct usb_endpoint_descriptor *)
-		kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
-	if(interface->endpoint==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
-		return -1;	
-	}
-	memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor));
+		if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
+			warn("too many endpoints");
+			return -1;
+		}
+
+		ifp->endpoint = (struct usb_endpoint_descriptor *)
+			kmalloc(ifp->bNumEndpoints *
+			sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
+		if (!ifp->endpoint) {
+			err("out of memory");
+			return -1;	
+		}
+
+		memset(ifp->endpoint, 0, ifp->bNumEndpoints *
+			sizeof(struct usb_endpoint_descriptor));
 	
-	for (i = 0; i < interface->bNumEndpoints; i++) {
-//		if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
-//			parsed += 9;	/* skip over the HID descriptor for now */
-//			len -= 9;
-//		}
-		retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len);
-		if (retval < 0) return retval;
-		parsed += retval;
-		len -= retval;
+		for (i = 0; i < ifp->bNumEndpoints; i++) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if (header->bLength > size) {
+				err("ran out of descriptors parsing");
+				return -1;
+			}
+		
+			retval = usb_parse_endpoint(dev, ifp->endpoint + i, buffer, size);
+			if (retval < 0)
+				return retval;
+
+			buffer += retval;
+			parsed += retval;
+			size -= retval;
+		}
+
+		/* We check to see if it's an alternate to this one */
+		ifp = (struct usb_interface_descriptor *)buffer;
+		if (size < USB_DT_INTERFACE_SIZE ||
+		    ifp->bDescriptorType != USB_DT_INTERFACE ||
+		    !ifp->bAlternateSetting)
+			return parsed;
 	}
+
 	return parsed;
 }
 
-static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len)
+int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer)
 {
 	int i;
-	int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
-
-	if (parsed < 0)
-		return parsed;
+	int retval;
+	int size;
+	struct usb_descriptor_header *header;
 
-	memcpy(config, ptr + parsed, *ptr);
-	len -= *ptr;
-	parsed += *ptr;
+	memcpy(config, buffer, USB_DT_CONFIG_SIZE);
+	le16_to_cpus(&config->wTotalLength);
+	size = config->wTotalLength;
 
-	if (config->bNumInterfaces > USB_MAXINTERFACES)
-	{
-		printk(KERN_WARNING "usb: too many interfaces.\n");
+	if (config->bNumInterfaces > USB_MAXINTERFACES) {
+		warn("too many interfaces");
 		return -1;
-
 	}
 
-	config->interface = (struct usb_interface_descriptor *)
-		kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
-	if(config->interface==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
+	config->interface = (struct usb_interface *)
+		kmalloc(config->bNumInterfaces *
+		sizeof(struct usb_interface), GFP_KERNEL);
+	dbg("kmalloc IF %p, numif %i",config->interface,config->bNumInterfaces);
+	if (!config->interface) {
+		err("out of memory");
 		return -1;	
 	}
-	memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
+
+	memset(config->interface, 0,
+	       config->bNumInterfaces * sizeof(struct usb_interface));
+
+	buffer += config->bLength;
+	size -= config->bLength;
 	
 	for (i = 0; i < config->bNumInterfaces; i++) {
-		int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len);
-		if (retval < 0)
-			return parsed; // HACK
-//			return retval;
-		parsed += retval;
-		len -= retval;
-	}
-	return parsed;
-}
+		int numskipped, len;
+		char *begin;
 
-int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
-{
-	int i;
-	unsigned char *ptr = __buf;
+		/* Skip over the rest of the Class Specific or Vendor */
+		/*  Specific descriptors */
+		begin = buffer;
+		numskipped = 0;
+		while (size >= sizeof(struct usb_descriptor_header)) {
+			header = (struct usb_descriptor_header *)buffer;
+
+			if ((header->bLength > size) || (header->bLength < 2)) {
+				err("invalid descriptor length of %d", header->bLength);
+				return -1;
+			}
+
+			/* If we find another descriptor which is at or below */
+			/*  us in the descriptor heirarchy then we're done  */
+			if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+			    (header->bDescriptorType == USB_DT_INTERFACE) ||
+			    (header->bDescriptorType == USB_DT_CONFIG) ||
+			    (header->bDescriptorType == USB_DT_DEVICE))
+				break;
 
-	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
-	{
-		printk(KERN_WARNING "usb: too many configurations.\n");
-		return -1;
-	}
+			dbg("skipping descriptor 0x%X", header->bDescriptorType);
+			numskipped++;
 
-	dev->config = (struct usb_config_descriptor *)
-		kmalloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor), GFP_KERNEL);
-	if(dev->config==NULL)
-	{
-		printk(KERN_WARNING "usb: out of memory.\n");
-		return -1;	
-	}
-	memset(dev->config, 0, dev->descriptor.bNumConfigurations*sizeof(struct usb_config_descriptor));
-	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
-		int retval = usb_parse_config(dev, dev->config + i, ptr, bytes);
+			buffer += header->bLength;
+			size -= header->bLength;
+		}
+		if (numskipped)
+			dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+		/* Copy any unknown descriptors into a storage area for */
+		/*  drivers to later parse */
+		len = (int)(buffer - begin);
+		if (!len) {
+			config->extra = NULL;
+			config->extralen = 0;
+		} else {
+			config->extra = kmalloc(len, GFP_KERNEL);
+			if (!config->extra) {
+				err("couldn't allocate memory for config extra descriptors");
+				config->extralen = 0;
+				return -1;
+			}
+
+			memcpy(config->extra, begin, len);
+			config->extralen = len;
+		}
+
+		retval = usb_parse_interface(dev, config->interface + i, buffer, size);
 		if (retval < 0)
 			return retval;
-		ptr += retval;
-		bytes += retval;
+
+		buffer += retval;
+		size -= retval;
 	}
-	return 0;
+
+	return size;
 }
 
 void usb_destroy_configuration(struct usb_device *dev)
 {
-	int c, i;
-	struct usb_config_descriptor *cf;
-	struct usb_interface_descriptor *ifp;
+	int c, i, j, k;
 	
-	if(dev->config==NULL)
+	if (!dev->config)
 		return;
-	for(c=0;c<dev->descriptor.bNumConfigurations;c++)
-	{
-		cf=&dev->config[c];
-		if(cf->interface==NULL)
+
+	if (dev->rawdescriptors) {
+		for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+			kfree(dev->rawdescriptors[i]);
+
+		kfree(dev->rawdescriptors);
+	}
+
+	for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+		struct usb_config_descriptor *cf = &dev->config[c];
+
+		if (!cf->interface)
 			break;
-		for(i=0;i<cf->bNumInterfaces;i++)
-		{
-			ifp=&cf->interface[i];
-			if(ifp->endpoint==NULL)
+
+		for (i = 0; i < cf->bNumInterfaces; i++) {
+			struct usb_interface *ifp =
+				&cf->interface[i];
+				
+			if (!ifp->altsetting)
 				break;
-			kfree(ifp->endpoint);
+
+			for (j = 0; j < ifp->num_altsetting; j++) {
+				struct usb_interface_descriptor *as =
+					&ifp->altsetting[j];
+					
+				if(as->extra) {
+					kfree(as->extra);
+				}
+
+				if (!as->endpoint)
+					break;
+					
+				for(k = 0; k < as->bNumEndpoints; k++) {
+					if(as->endpoint[k].extra) {
+						kfree(as->endpoint[k].extra);
+					}
+				}	
+				kfree(as->endpoint);
+			}
+
+			kfree(ifp->altsetting);
 		}
 		kfree(cf->interface);
 	}
 	kfree(dev->config);
 }
-			
-void usb_init_root_hub(struct usb_device *dev)
+
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+	int retval;
+
+	for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+		*utf++ = *ascii++ & 0x7f;
+		*utf++ = 0;
+	}
+	return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+	char buf [30];
+
+	// assert (len > (2 * (sizeof (buf) + 1)));
+	// assert (strlen (type) <= 8);
+
+	// language ids
+	if (id == 0) {
+		*data++ = 4; *data++ = 3;	/* 4 bytes data */
+		*data++ = 0; *data++ = 0;	/* some language id */
+		return 4;
+
+	// serial number
+	} else if (id == 1) {
+		sprintf (buf, "%x", serial);
+
+	// product description
+	} else if (id == 2) {
+		sprintf (buf, "USB %s Root Hub", type);
+
+	// id 3 == vendor description
+
+	// unsupported IDs --> "stall"
+	} else
+	    return 0;
+
+	data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+	data [1] = 3;
+	return data [0];
+}
+
+/*
+ * __usb_get_extra_descriptor() finds a descriptor of specific type in the
+ * extra field of the interface and endpoint descriptor structs.
+ */
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
 {
-	dev->devnum = -1;
-	dev->slow = 0;
+	struct usb_descriptor_header *header;
+
+	while (size >= sizeof(struct usb_descriptor_header)) {
+		header = (struct usb_descriptor_header *)buffer;
+
+		if (header->bLength < 2) {
+			err("invalid descriptor length of %d", header->bLength);
+			return -1;
+		}
+
+		if (header->bDescriptorType == type) {
+			*ptr = header;
+			return 0;
+		}
+
+		buffer += header->bLength;
+		size -= header->bLength;
+	}
+	return -1;
 }
 
 /*
@@ -340,29 +1448,46 @@
 void usb_disconnect(struct usb_device **pdev)
 {
 	struct usb_device * dev = *pdev;
+	int i;
 
-	if (dev) {
-		int i;
+	if (!dev)
+		return;
 
-		*pdev = NULL;
+	*pdev = NULL;
 
-		printk("USB disconnect on device %d\n", dev->devnum);
+	info("USB disconnect on device %d", dev->devnum);
 
-		if(dev->driver) dev->driver->disconnect(dev);
+	call_policy ("remove", dev);
 
-		/* Free up all the children.. */
-		for (i = 0; i < USB_MAXCHILDREN; i++) {
-			struct usb_device **child = dev->children + i;
-			usb_disconnect(child);
+	if (dev->actconfig) {
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+			struct usb_interface *interface = &dev->actconfig->interface[i];
+			struct usb_driver *driver = interface->driver;
+			if (driver) {
+				down(&driver->serialize);
+				driver->disconnect(dev, interface->private_data);
+				up(&driver->serialize);
+				usb_driver_release_interface(driver, interface);
+			}
 		}
+	}
 
-		/* Free up the device itself, including its device number */
-		if (dev->devnum > 0)
-			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
-		dev->bus->op->deallocate(dev);
+	/* Free up all the children.. */
+	for (i = 0; i < USB_MAXCHILDREN; i++) {
+		struct usb_device **child = dev->children + i;
+		if (*child)
+			usb_disconnect(child);
+	}
+
+	/* Free the device number and remove the /proc/bus/usb entry */
+	if (dev->devnum > 0) {
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		usbdevfs_remove_device(dev);
 	}
-}
 
+	/* Free up the device itself */
+	usb_free_dev(dev);
+}
 
 /*
  * Connect a new USB device. This basically just initializes
@@ -373,9 +1498,11 @@
 void usb_connect(struct usb_device *dev)
 {
 	int devnum;
-
-	dev->descriptor.bMaxPacketSize0 = 8;  /* XXX fixed 8 bytes for now */
-
+	// FIXME needs locking for SMP!!
+	/* why? this is called only from the hub thread, 
+	 * which hopefully doesn't run on multiple CPU's simulatenously 8-)
+	 */
+	dev->descriptor.bMaxPacketSize0 = 8;  /* Start off at 8 bytes  */
 	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
 	if (devnum < 128) {
 		set_bit(devnum, dev->bus->devmap.devicemap);
@@ -387,287 +1514,578 @@
  * These are the actual routines to send
  * and receive control messages.
  */
-int usb_set_address(struct usb_device *dev)
-{
-	devrequest dr;
 
-	dr.requesttype = 0;
-	dr.request = USB_REQ_SET_ADDRESS;
-	dr.value = dev->devnum;
-	dr.index = 0;
-	dr.length = 0;
+#define GET_TIMEOUT 3
+#define SET_TIMEOUT 3
 
-	return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
+int usb_set_address(struct usb_device *dev)
+{
+	return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+		0, dev->devnum, 0, NULL, 0, HZ * GET_TIMEOUT);
 }
 
 int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
 {
-	devrequest dr;
-
-	dr.requesttype = 0x80;
-	dr.request = USB_REQ_GET_DESCRIPTOR;
-	dr.value = (type << 8) + index;
-	dr.index = 0;
-	dr.length = size;
+	int i = 5;
+	int result;
+	
+	memset(buf,0,size);	// Make sure we parse really received data
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+	while (i--) {
+		if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 ||
+		     result == -EPIPE)
+			break;
+	}
+	return result;
 }
 
-int usb_get_device_descriptor(struct usb_device *dev)
+int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
+		unsigned char type, unsigned char id, void *buf, int size)
 {
-	return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+		(type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
 }
 
-int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HUB | 0x80;
-	dr.request = USB_REQ_GET_DESCRIPTOR;
-	dr.value = (USB_DT_HUB << 8);
-	dr.index = 0;
-	dr.length = size;
-
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+		(USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
 }
 
-int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_device_descriptor(struct usb_device *dev)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_PORT;
-	dr.request = USB_REQ_CLEAR_FEATURE;
-	dr.value = feature;
-	dr.index = port;
-	dr.length = 0;
+	int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,
+				     sizeof(dev->descriptor));
+	if (ret >= 0) {
+		le16_to_cpus(&dev->descriptor.bcdUSB);
+		le16_to_cpus(&dev->descriptor.idVendor);
+		le16_to_cpus(&dev->descriptor.idProduct);
+		le16_to_cpus(&dev->descriptor.bcdDevice);
+	}
+	return ret;
+}
 
-	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+int usb_get_status(struct usb_device *dev, int type, int target, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT);
 }
 
-int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+int usb_get_protocol(struct usb_device *dev, int ifnum)
 {
-	devrequest dr;
+	unsigned char type;
+	int ret;
 
-	dr.requesttype = USB_RT_PORT;
-	dr.request = USB_REQ_SET_FEATURE;
-	dr.value = feature;
-	dr.index = port;
-	dr.length = 0;
+	if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+	    USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+	    0, ifnum, &type, 1, HZ * GET_TIMEOUT)) < 0)
+		return ret;
 
-	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+	return type;
 }
 
-int usb_get_hub_status(struct usb_device *dev, void *data)
+int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
 {
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HUB | 0x80;
-	dr.request = USB_REQ_GET_STATUS;
-	dr.value = 0;
-	dr.index = 0;
-	dr.length = 4;
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		protocol, ifnum, NULL, 0, HZ * SET_TIMEOUT);
+}
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT);
 }
 
-int usb_get_port_status(struct usb_device *dev, int port, void *data)
+void usb_set_maxpacket(struct usb_device *dev)
 {
-	devrequest dr;
+	int i, b;
 
-	dr.requesttype = USB_RT_PORT | 0x80;
-	dr.request = USB_REQ_GET_STATUS;
-	dr.value = 0;
-	dr.index = port;
-	dr.length = 4;
+	for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *ifp = dev->actconfig->interface + i;
+		struct usb_interface_descriptor *as = ifp->altsetting + ifp->act_altsetting;
+		struct usb_endpoint_descriptor *ep = as->endpoint;
+		int e;
 
-	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+		for (e=0; e<as->bNumEndpoints; e++) {
+			b = ep[e].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+			if ((ep[e].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+				USB_ENDPOINT_XFER_CONTROL) {	/* Control => bidirectional */
+				dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+				dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+				}
+			else if (usb_endpoint_out(ep[e].bEndpointAddress)) {
+				if (ep[e].wMaxPacketSize > dev->epmaxpacketout[b])
+					dev->epmaxpacketout[b] = ep[e].wMaxPacketSize;
+			}
+			else {
+				if (ep[e].wMaxPacketSize > dev->epmaxpacketin [b])
+					dev->epmaxpacketin [b] = ep[e].wMaxPacketSize;
+			}
+		}
+	}
 }
 
-int usb_get_protocol(struct usb_device *dev)
+/*
+ * endp: endpoint number in bits 0-3;
+ *	direction flag in bit 7 (1 = IN, 0 = OUT)
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
 {
-	unsigned char buf[8];
-	devrequest dr;
+	int result;
+	__u16 status;
+	int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
 
-	dr.requesttype = USB_RT_HIDD | 0x80;
-	dr.request = USB_REQ_GET_PROTOCOL;
-	dr.value = 0;
-	dr.index = 1;
-	dr.length = 1;
+/*
+	if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp)))
+		return 0;
+*/
 
-	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
-		return -1;
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT);
 
-	return buf[0];
-}
+	/* don't clear if failed */
+	if (result < 0)
+		return result;
 
-int usb_set_protocol(struct usb_device *dev, int protocol)
-{
-	devrequest dr;
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
+		&status, sizeof(status), HZ * SET_TIMEOUT);
+	if (result < 0)
+		return result;
 
-	dr.requesttype = USB_RT_HIDD;
-	dr.request = USB_REQ_SET_PROTOCOL;
-	dr.value = protocol;
-	dr.index = 1;
-	dr.length = 0;
+	if (le16_to_cpu(status) & 1)
+		return -EPIPE;		/* still halted */
 
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+	/* toggle is reset on clear */
+
+	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
 
 	return 0;
 }
 
-/* keyboards want a nonzero duration according to HID spec, but
-   mice should use infinity (0) -keryan */
-int usb_set_idle(struct usb_device *dev,  int duration, int report_id)
-{
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HIDD;
-	dr.request = USB_REQ_SET_IDLE;
-	dr.value = (duration << 8) | report_id;
-	dr.index = 1;
-	dr.length = 0;
-
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+	struct usb_interface *iface;
+	int ret;
 
+	iface = usb_ifnum_to_if(dev, interface);
+	if (!iface) {
+		warn("selecting invalid interface %d", interface);
+		return -EINVAL;
+	}
+
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+	    USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
+	    interface, NULL, 0, HZ * 5)) < 0)
+		return ret;
+
+	iface->act_altsetting = alternate;
+	dev->toggle[0] = 0;	/* 9.1.1.5 says to do this */
+	dev->toggle[1] = 0;
+	usb_set_maxpacket(dev);
 	return 0;
 }
 
 int usb_set_configuration(struct usb_device *dev, int configuration)
 {
-	devrequest dr;
-
-	dr.requesttype = 0;
-	dr.request = USB_REQ_SET_CONFIGURATION;
-	dr.value = configuration;
-	dr.index = 0;
-	dr.length = 0;
+	int i, ret;
+	struct usb_config_descriptor *cp = NULL;
+	
+	for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+		if (dev->config[i].bConfigurationValue == configuration) {
+			cp = &dev->config[i];
+			break;
+		}
+	}
+	if (!cp) {
+		warn("selecting invalid configuration %d", configuration);
+		return -EINVAL;
+	}
 
-	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
-		return -1;
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+	    USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0)
+		return ret;
+
+	dev->actconfig = cp;
+	dev->toggle[0] = 0;
+	dev->toggle[1] = 0;
+	usb_set_maxpacket(dev);
 
 	return 0;
 }
 
-int usb_get_report(struct usb_device *dev)
+int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
 {
-	unsigned char buf[8];
-	devrequest dr;
-
-	dr.requesttype = USB_RT_HIDD | 0x80;
-	dr.request = USB_REQ_GET_REPORT;
-	dr.value = 0x100;
-	dr.index = 1;
-	dr.length = 3;
-
-	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
-		return -1;
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
+}
 
-	return buf[0];
+int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(type << 8) + id, ifnum, buf, size, HZ);
 }
 
 int usb_get_configuration(struct usb_device *dev)
 {
-	unsigned int cfgno,size;
-	unsigned char buffer[400];
-	unsigned char * bufptr;
-	
-	bufptr=buffer;
-        for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
-  		/* Get the first 8 bytes - guaranteed */
-	  	if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
-	    		return -1;
+	int result;
+	unsigned int cfgno, length;
+	unsigned char buffer[8];
+	unsigned char *bigbuffer;
+	struct usb_config_descriptor *desc =
+		(struct usb_config_descriptor *)buffer;
+
+	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
+		warn("too many configurations");
+		return -EINVAL;
+	}
+
+	if (dev->descriptor.bNumConfigurations < 1) {
+		warn("not enough configurations");
+		return -EINVAL;
+	}
+
+	dev->config = (struct usb_config_descriptor *)
+		kmalloc(dev->descriptor.bNumConfigurations *
+		sizeof(struct usb_config_descriptor), GFP_KERNEL);
+	if (!dev->config) {
+		err("out of memory");
+		return -ENOMEM;	
+	}
+	memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+		sizeof(struct usb_config_descriptor));
+
+	dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *
+		dev->descriptor.bNumConfigurations, GFP_KERNEL);
+	if (!dev->rawdescriptors) {
+		err("out of memory");
+		return -ENOMEM;
+	}
+
+	for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
+		/* We grab the first 8 bytes so we know how long the whole */
+		/*  configuration is */
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
+		if (result < 8) {
+			if (result < 0)
+				err("unable to get descriptor");
+			else {
+				err("config descriptor too short (expected %i, got %i)", 8, result);
+				result = -EINVAL;
+			}
+			goto err;
+		}
 
   	  	/* Get the full buffer */
-	  	size = *(unsigned short *)(bufptr+2);
-	  	if (bufptr+size > buffer+sizeof(buffer))
-	  	{
-			printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
-			size = buffer+sizeof(buffer)-bufptr;
+		length = le16_to_cpu(desc->wTotalLength);
+
+		bigbuffer = kmalloc(length, GFP_KERNEL);
+		if (!bigbuffer) {
+			err("unable to allocate memory for configuration descriptors");
+			result = -ENOMEM;
+			goto err;
+		}
+
+		/* Now that we know the length, get the whole thing */
+		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
+		if (result < 0) {
+			err("couldn't get all of config descriptors");
+			kfree(bigbuffer);
+			goto err;
+		}	
+	
+		if (result < length) {
+			err("config descriptor too short (expected %i, got %i)", length, result);
+			result = -EINVAL;
+			kfree(bigbuffer);
+			goto err;
+		}
+
+		dev->rawdescriptors[cfgno] = bigbuffer;
+
+		result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
+		if (result > 0)
+			dbg("descriptor data left");
+		else if (result < 0) {
+			result = -EINVAL;
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	dev->descriptor.bNumConfigurations = cfgno;
+	return result;
+}
+
+/*
+ * usb_string:
+ *	returns string length (> 0) or error (< 0)
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+	unsigned char *tbuf;
+	int err;
+	unsigned int u, idx;
+
+	if (size <= 0 || !buf || !index)
+		return -EINVAL;
+	buf[0] = 0;
+	tbuf = kmalloc(256, GFP_KERNEL);
+	if (!tbuf)
+		return -ENOMEM;
+
+	/* get langid for strings if it's not yet known */
+	if (!dev->have_langid) {
+		err = usb_get_string(dev, 0, 0, tbuf, 4);
+		if (err < 0) {
+			err("error getting string descriptor 0 (error=%d)", err);
+			goto errout;
+		} else if (tbuf[0] < 4) {
+			err("string descriptor 0 too short");
+			err = -EINVAL;
+			goto errout;
+		} else {
+			dev->have_langid = -1;
+			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+				/* always use the first langid listed */
+			dbg("USB device number %d default language ID 0x%x",
+				dev->devnum, dev->string_langid);
 		}
-		if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size))
-			return -1;
-			
-		/* Prepare for next configuration */
-		bufptr+=size;
 	}
-	return usb_parse_configuration(dev, buffer, size);
+
+	/*
+	 * Just ask for a maximum length string and then take the length
+	 * that was returned.
+	 */
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, 255);
+	if (err < 0)
+		goto errout;
+
+	size--;		/* leave room for trailing NULL char in output buffer */
+	for (idx = 0, u = 2; u < err; u += 2) {
+		if (idx >= size)
+			break;
+		if (tbuf[u+1])			/* high byte */
+			buf[idx++] = '?';  /* non-ASCII character */
+		else
+			buf[idx++] = tbuf[u];
+	}
+	buf[idx] = 0;
+	err = idx;
+
+ errout:
+	kfree(tbuf);
+	return err;
 }
 
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
  * get the ball rolling..
+ *
+ * Returns 0 for success, != 0 for error.
  */
-void usb_new_device(struct usb_device *dev)
+int usb_new_device(struct usb_device *dev)
 {
-	int addr, i;
+	int err;
 
-	printk("USB new device connect, assigned device number %d\n",
-		dev->devnum);
+	info("USB new device connect, assigned device number %d", dev->devnum);
+ 
+	/* USB v1.1 5.5.3 */
+	/* We read the first 8 bytes from the device descriptor to get to */
+	/*  the bMaxPacketSize0 field. Then we set the maximum packet size */
+	/*  for the control pipe, and retrieve the rest */
+	dev->epmaxpacketin [0] = 8;
+	dev->epmaxpacketout[0] = 8;
+
+	err = usb_set_address(dev);
+	if (err < 0) {
+		err("USB device not accepting new address (error=%d)", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
 
-	dev->maxpacketsize = 0;		/* Default to 8 byte max packet size */
+	wait_ms(10);	/* Let the SET_ADDRESS settle */
 
-	addr = dev->devnum;
-	dev->devnum = 0;
+	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+	if (err < 8) {
+		if (err < 0)
+			err("USB device not responding, giving up (error=%d)", err);
+		else
+			err("USB device descriptor short read (expected %i, got %i)",8,err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+	dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
+	dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
+
+	err = usb_get_device_descriptor(dev);
+	if (err < sizeof(dev->descriptor)) {
+		if (err < 0)
+			err("unable to get device descriptor (error=%d)", err);
+		else
+			err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err);
+	
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+
+	err = usb_get_configuration(dev);
+	if (err < 0) {
+		err("unable to get configuration (error=%d)", err);
+		usb_destroy_configuration(dev);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+
+	/* we set the default configuration here */
+	err = usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+	if (err) {
+		err("failed to set default configuration (error=%d)", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+		dev->devnum = -1;
+		return 1;
+	}
+
+	dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
+		dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+#ifdef DEBUG
+	if (dev->descriptor.iManufacturer)
+		usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
+	if (dev->descriptor.iProduct)
+		usb_show_string(dev, "Product", dev->descriptor.iProduct);
+	if (dev->descriptor.iSerialNumber)
+		usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#endif
 
-	/* Slow devices */
-	for (i = 0; i < 5; i++) {
-		if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
-			break;
+	/* now that the basic setup is over, add a /proc/bus/usb entry */
+	usbdevfs_add_device(dev);
 
-		printk("get_descriptor failed, waiting\n");
-		wait_ms(200);
-	}
-	if (i == 5) {
-		printk("giving up\n");
-		return;
-	}
+	/* find drivers willing to handle this device */
+	usb_find_drivers(dev);
 
-#if 0
-	printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
-#endif
-	switch (dev->descriptor.bMaxPacketSize0) {
-		case 8: dev->maxpacketsize = 0; break;
-		case 16: dev->maxpacketsize = 1; break;
-		case 32: dev->maxpacketsize = 2; break;
-		case 64: dev->maxpacketsize = 3; break;
-	}
-#if 0
-	printk("dev->mps: %d\n", dev->maxpacketsize);
-#endif
+	/* userspace may load modules and/or configure further */
+	call_policy ("add", dev);
 
-	dev->devnum = addr;
+	return 0;
+}
 
-	if (usb_set_address(dev)) {
-		printk("Unable to set address\n");
-		/* FIXME: We should disable the port */
-		return;
-	}
+static int usb_open(struct inode * inode, struct file * file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct usb_driver *c = usb_minors[minor/16];
+	int err = -ENODEV;
 
-	wait_ms(10);	/* Let the SET_ADDRESS settle */
+	if (!c || !c->fops)
+		return err;
 
-	if (usb_get_device_descriptor(dev)) {
-		printk("Unable to get device descriptor\n");
-		return;
-	}
+	file->f_op = c->fops;
 
-	if (usb_get_configuration(dev)) {
-		printk("Unable to get configuration\n");
-		return;
+	return file->f_op->open(inode,file);
+}
+
+static struct file_operations usb_fops = {
+	open:		usb_open,
+};
+
+int usb_major_init(void)
+{
+	if (register_chrdev(USB_MAJOR,"usb",&usb_fops)) {
+		err("unable to get major %d for usb devices", USB_MAJOR);
+		return -EBUSY;
 	}
+	return 0;
+}
+
+void usb_major_cleanup(void)
+{
+	unregister_chrdev(USB_MAJOR, "usb");
+}
 
-#if 0
-	printk("Vendor: %X\n", dev->descriptor.idVendor);
-	printk("Product: %X\n", dev->descriptor.idProduct);
-#endif
 
-	usb_device_descriptor(dev);
+#ifdef CONFIG_PROC_FS
+struct list_head *usb_driver_get_list(void)
+{
+	return &usb_driver_list;
 }
 
-int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+struct list_head *usb_bus_get_list(void)
 {
-	return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
+	return &usb_bus_list;
 }
+#endif
+
+/*
+ * USB may be built into the kernel or be built as modules.
+ * If the USB core [and maybe a host controller driver] is built
+ * into the kernel, and other device drivers are built as modules,
+ * then these symbols need to be exported for the modules to use.
+ */
+EXPORT_SYMBOL(usb_ifnum_to_if);
+
+EXPORT_SYMBOL(usb_register);
+EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL(usb_scan_devices);
+EXPORT_SYMBOL(usb_alloc_bus);
+EXPORT_SYMBOL(usb_free_bus);
+EXPORT_SYMBOL(usb_register_bus);
+EXPORT_SYMBOL(usb_deregister_bus);
+EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL(usb_free_dev);
+EXPORT_SYMBOL(usb_inc_dev_use);
+
+EXPORT_SYMBOL(usb_driver_claim_interface);
+EXPORT_SYMBOL(usb_interface_claimed);
+EXPORT_SYMBOL(usb_driver_release_interface);
+
+EXPORT_SYMBOL(usb_root_hub_string);
+EXPORT_SYMBOL(usb_new_device);
+EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_connect);
+EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(usb_check_bandwidth);
+EXPORT_SYMBOL(usb_claim_bandwidth);
+EXPORT_SYMBOL(usb_release_bandwidth);
+
+EXPORT_SYMBOL(usb_set_address);
+EXPORT_SYMBOL(usb_get_descriptor);
+EXPORT_SYMBOL(usb_get_class_descriptor);
+EXPORT_SYMBOL(__usb_get_extra_descriptor);
+EXPORT_SYMBOL(usb_get_device_descriptor);
+EXPORT_SYMBOL(usb_get_string);
+EXPORT_SYMBOL(usb_string);
+EXPORT_SYMBOL(usb_get_protocol);
+EXPORT_SYMBOL(usb_set_protocol);
+EXPORT_SYMBOL(usb_get_report);
+EXPORT_SYMBOL(usb_set_report);
+EXPORT_SYMBOL(usb_set_idle);
+EXPORT_SYMBOL(usb_clear_halt);
+EXPORT_SYMBOL(usb_set_interface);
+EXPORT_SYMBOL(usb_get_configuration);
+EXPORT_SYMBOL(usb_set_configuration);
+
+EXPORT_SYMBOL(usb_get_current_frame_number);
+
+EXPORT_SYMBOL(usb_alloc_urb);
+EXPORT_SYMBOL(usb_free_urb);
+EXPORT_SYMBOL(usb_submit_urb);
+EXPORT_SYMBOL(usb_unlink_urb);
 
+EXPORT_SYMBOL(usb_control_msg);
+EXPORT_SYMBOL(usb_bulk_msg);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)