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

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

diff -u --recursive --new-file v2.3.33/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
@@ -5,6 +5,7 @@
  * (C) Copyright Johannes Erdfelt 1999
  * (C) Copyright Andreas Gal 1999
  * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -13,6 +14,8 @@
  * Think of this as a "USB library" rather than anything else.
  * It should be considered a slave, with no callbacks. Callbacks
  * are evil.
+ *
+ * $Id: usb.c,v 1.37 1999/12/17 10:48:08 fliegl Exp $
  */
 
 #ifndef EXPORT_SYMTAB
@@ -26,9 +29,21 @@
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/malloc.h>
+#include <linux/interrupt.h>  /* for in_interrupt() */
 
 #include "usb.h"
 
+#define MODSTR "usbcore: "
+
+#ifdef USB_DEBUG
+	#define dbg printk
+#else
+	#define dbg nix
+static void nix(const char *format, ...)
+{
+}
+#endif
+
 /*
  * Prototypes for the device driver probing/loading functions
  */
@@ -36,8 +51,6 @@
 static int  usb_find_interface_driver(struct usb_device *, unsigned int);
 static void usb_check_support(struct usb_device *);
 
-static int usb_debug = 1;
-
 /*
  * We have a per-interface "registered driver" list.
  */
@@ -54,15 +67,14 @@
 
 	if (new_driver->fops != NULL) {
 		if (usb_minors[new_driver->minor/16]) {
-			printk(KERN_ERR "Error registering %s driver\n",
+			 printk(KERN_ERR "Error registering %s driver\n",
 				new_driver->name);
-			return USB_ST_NOTSUPPORTED;
+			return -EINVAL;
 		}
 		usb_minors[new_driver->minor/16] = new_driver;
 	}
 
 	printk("usbcore: Registered new driver %s\n", new_driver->name);
-
 	/* Add it to the list of known drivers */
 	list_add(&new_driver->driver_list, &usb_driver_list);
 
@@ -77,7 +89,7 @@
 	while (tmp != &usb_bus_list) {
 		struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list);
 
-	        tmp = tmp->next;
+		tmp = tmp->next;
 		usb_check_support(bus->root_hub);
 	}
 	return 0;
@@ -100,21 +112,21 @@
 		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 (!dev->actconfig)
+		return;
+			
+	for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
+		struct usb_interface *interface = &dev->actconfig->interface[i];
 		
-	        if (interface->driver == driver) {
-                        driver->disconnect(dev, interface->private_data);
+		if (interface->driver == driver) {
+			driver->disconnect(dev, interface->private_data);
 			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);
-                }
+			/*
+			 * This will go through the list looking for another
+			 * driver that can handle the device
+			 */
+			usb_find_interface_driver(dev, i);
+		}
 	}
 }
 
@@ -157,12 +169,12 @@
 	if (low_speed)		/* no isoc. here */
 	{
 		if (input_dir)
-                {
-		        tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+		{
+			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);
 		}
@@ -308,13 +320,13 @@
 		if (dev->children[i])
 			usb_check_support(dev->children[i]);
 
-        if (!dev->actconfig)
-                return;
+	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);
+	if (dev->devnum > 0)
+		for (i = 0; i < dev->actconfig->bNumInterfaces; i++)
+			usb_find_interface_driver(dev, i);
 }
 
 
@@ -376,7 +388,7 @@
 static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
 {
 	struct list_head *tmp = usb_driver_list.next;
-        struct usb_interface *interface;
+	struct usb_interface *interface;
 	
 	if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
 		printk(KERN_ERR "usb-core: bad find_interface_driver params\n");
@@ -385,22 +397,22 @@
 
 	interface = dev->actconfig->interface + ifnum;
 
-        if (usb_interface_claimed(interface))
-                return -1;
+	if (usb_interface_claimed(interface))
+		return -1;
 
-        while (tmp != &usb_driver_list) {
+	while (tmp != &usb_driver_list) {
 		void *private;
-                struct usb_driver *driver = list_entry(tmp, struct usb_driver,
-		  			               driver_list);
-                        
-                tmp = tmp->next;
-                if (!(private = driver->probe(dev, ifnum)))
-                        continue;
+		struct usb_driver *driver = list_entry(tmp, struct usb_driver,
+		  				       driver_list);
+			
+		tmp = tmp->next;
+		if (!(private = driver->probe(dev, ifnum)))
+			continue;
 		usb_driver_claim_interface(driver, interface, private);
 
-                return 0;
-        }
-        
+		return 0;
+	}
+	
 	return -1;
 }
 
@@ -412,7 +424,7 @@
 static void usb_find_drivers(struct usb_device *dev)
 {
 	unsigned ifnum;
-        unsigned rejected = 0;
+	unsigned rejected = 0;
 
 	for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) {
 		/* if this interface hasn't already been claimed */
@@ -422,9 +434,8 @@
 		}
 	}
  
-	if (rejected) {
+	if (rejected)
 		printk(KERN_DEBUG "usbcore: unhandled interfaces on device.\n");
-	}
 }
 
 /*
@@ -463,7 +474,358 @@
 {
 	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)
+	{
+		printk(KERN_ERR MODSTR"alloc_urb: kmalloc failed\n");
+		return 0;
+	}
+	memset(urb,0,sizeof(urb_t));
+	return urb;
+}
+
+/*-------------------------------------------------------------------*/
+void usb_free_urb(urb_t* urb)
+{
+	kfree(urb);
+}
+/*-------------------------------------------------------------------*/
+int usb_submit_urb(urb_t *urb)
+{
+	if(urb && urb->dev)
+		return urb->dev->bus->op->submit_urb(urb);
+	else
+		return -1;
+}
+
+/*-------------------------------------------------------------------*/
+int usb_unlink_urb(urb_t *urb)
+{
+	if(urb && urb->dev)
+		return urb->dev->bus->op->unlink_urb(urb);
+	else
+		return -1;
+}
+/*-------------------------------------------------------------------*
+ *                     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(KERN_DEBUG MODSTR "(blocking_completion): waitqueue empty!\n"); 
+		// even occurs if urb was unlinked by timeout...
+#endif
+}
+
+/*-------------------------------------------------------------------*
+ * completion handler for compatibility wrappers (async bulk)        *
+ *-------------------------------------------------------------------*/
+static void usb_api_async_completion(urb_t *urb)
+{
+	api_wrapper_data *awd=(api_wrapper_data*)urb->context;
+
+	if (awd->handler)
+		awd->handler(urb->status,urb->transfer_buffer,urb->actual_length,awd->stuff);
+ }
+
+/*-------------------------------------------------------------------*
+ *                         COMPATIBILITY STUFF                       *
+ *-------------------------------------------------------------------*/
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(urb_t *urb, int timeout, unsigned long* rval)
+{ 
+	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_UNINTERRUPTIBLE;
+	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)
+		status=schedule_timeout(timeout); // ZZzzzz....
+	else
+		status = 1;
+
+	remove_wait_queue(&wqh, &wait);
+
+	if (!status) {
+		// timeout
+		printk(KERN_DEBUG MODSTR"usb_control/bulk_msg: timeout\n");
+		usb_unlink_urb(urb);  // remove urb safely
+		status=-ETIMEDOUT;
+	}
+	else
+		status=urb->status;
+
+	if (rval)
+		*rval=urb->actual_length;
+
+	usb_free_urb(urb);
+  	return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) are 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;
+	unsigned long 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;
+	  
+	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(KERN_DEBUG MODSTR"usb_control_msg\n");	
+	return usb_internal_control_msg(dev, pipe, &dr, data, size, timeout);
+}
+
+/*-------------------------------------------------------------------*/
+/* 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, unsigned long *rval, 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,rval);
+}
+/*-------------------------------------------------------------------*/
+
+void *usb_request_bulk(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id)
+{
+	urb_t *urb;
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data *awd;
+
+	if (!(urb=usb_alloc_urb(0)))
+		return NULL;
+	if (!(awd = kmalloc(sizeof(api_wrapper_data), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) {
+		kfree(urb);
+		return NULL;
+	}
+
+	/* build urb */
+	FILL_BULK_URB(urb, dev, pipe, data, len, (usb_complete_t)usb_api_async_completion, awd);
+
+	awd->handler=handler;
+	awd->stuff=dev_id;
+	if (usb_submit_urb(urb) < 0) {
+		kfree(awd);
+		kfree(urb);
+		return NULL;
+	}
+	return urb;
+}
+
+// compatibility wrapper. Remove urb only if it is called before the
+// transaction's completion interrupt. If called from within the
+// completion handler (urb->completed==1), it does nothing, since the
+// qh is already removed 
+
+int usb_terminate_bulk(struct usb_device *dev, void *first)
+{
+	urb_t *urb=(urb_t*)first;
+	dbg(KERN_DEBUG MODSTR"usb_terminate_bulk: urb:%p\n",urb);
+	if (!urb) // none found? there is nothing to remove!
+		return -ENODEV;
+  
+	usb_unlink_urb(urb);
+	kfree(urb->context);
+	kfree(urb);
+	return USB_ST_NOERROR;
+}
+
+/*
+ * usb_release_bandwidth():
+ *
+ * called to release an interrupt pipe's bandwidth (in microseconds)
+ */
+void usb_release_bandwidth(struct usb_device *dev, int bw_alloc)
+{
+	dev->bus->bandwidth_allocated -= bw_alloc;
+	dev->bus->bandwidth_int_reqs--;
+	PRINTD ("bw_alloc reduced to %d for %d requesters",
+		dev->bus->bandwidth_allocated,
+		dev->bus->bandwidth_int_reqs +
+		dev->bus->bandwidth_isoc_reqs);
+}
+
+static void irq_callback(urb_t *urb)
+{
+	struct irq_wrapper_data *wd = (struct irq_wrapper_data *)urb->context;
+
+	if (!wd->handler)
+		return;
+#if 0 // verbose...
+	if (!wd->handler(urb->status, urb->transfer_buffer, urb->actual_length, wd->context))
+		printk(KERN_ERR "usb: legacy irq callback returned 0!!!\n");
+#else
+	wd->handler(urb->status, urb->transfer_buffer, urb->actual_length, wd->context);
+#endif
+}
+
+int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle)
+{
+	long    bustime;
+	int     ret;
+	struct irq_wrapper_data *wd;
+	urb_t *urb;
+	unsigned int maxsze = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	*handle = NULL;
+	
+	//printk("irq: dev:%p pipe:%08X handler:%p period:%d dev_id:%p max:%d\n", dev, pipe, handler, period, dev_id, maxsze);
+	
+	/* Check host controller's bandwidth for this int. request. */
+	bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0,
+			usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+	bustime = NS_TO_US(bustime);	/* work in microseconds */
+	if (check_bandwidth_alloc (dev->bus->bandwidth_allocated, bustime))
+		return -EUSERS;  // no bandwidth left
+
+	if (!maxsze || !usb_pipeint(pipe))
+		return -EINVAL;
+
+	if (!(urb = usb_alloc_urb(0)))
+		return -ENOMEM;
+
+	if (!(wd = kmalloc(sizeof(struct irq_wrapper_data), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) {
+		kfree(urb);
+		return -ENOMEM;
+	}
+	if (!(urb->transfer_buffer = kmalloc(maxsze, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL))) {
+		kfree(urb);
+		kfree(wd);
+		return -ENOMEM;
+	}
+	wd->handler=handler;
+	wd->context=dev_id;
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer_length = urb->actual_length = maxsze;
+	urb->interval = period;
+	urb->context = wd;
+	urb->complete = irq_callback;
+	if ((ret = usb_submit_urb(urb)) < 0) {
+		kfree(wd);
+		kfree(urb->transfer_buffer);
+		kfree(urb);
+		return ret;
+	}
+	*handle = urb;
+
+	/* Claim the USB bandwidth if no error. */
+	if (!ret) {
+		dev->bus->bandwidth_allocated += bustime;
+		dev->bus->bandwidth_int_reqs++;
+		PRINTD ("bw_alloc bumped to %d for %d requesters",
+			dev->bus->bandwidth_allocated,
+			dev->bus->bandwidth_int_reqs +
+			dev->bus->bandwidth_isoc_reqs);
+	}
+
+	return ret;
+}
+
+int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe)
+{
+	long    bustime;
+	int	err;
+	urb_t *urb = (urb_t*)handle;
+
+	if (!urb)
+		return -EBADF;
+	err=usb_unlink_urb(urb);
+	kfree(urb->context);
+	kfree(urb->transfer_buffer);
+	kfree(urb);
 
+	/* Return the USB bandwidth if no error. */
+	if (!err) {
+		bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0,
+				usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
+		bustime = NS_TO_US(bustime);	/* work in microseconds */
+		usb_release_bandwidth(dev, bustime);
+	}
+	return err;
+}
+
+/*
+ * 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;
@@ -485,8 +847,12 @@
 		return parsed;
 	}
 
-	memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE);
-        le16_to_cpus(&endpoint->wMaxPacketSize);
+	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;
@@ -505,20 +871,21 @@
 		}
 
 		/* If we find another descriptor which is at or below us */
-		/*  in the descriptor heirarchy then we're done */
+		/*  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;
 
+		printk(KERN_INFO "usb: skipping descriptor 0x%X\n",
+			header->bDescriptorType);
 		numskipped++;
 
 		buffer += header->bLength;
 		size -= header->bLength;
 		parsed += header->bLength;
 	}
-
 	if (numskipped)
 		printk(KERN_INFO "usb: skipped %d class/vendor specific endpoint descriptors\n", numskipped);
 
@@ -532,6 +899,7 @@
 	}
 
 	endpoint->extra = kmalloc(len, GFP_KERNEL);
+
 	if (!endpoint->extra) {
 		printk(KERN_ERR "Couldn't allocate memory for endpoint extra descriptors\n");
 		endpoint->extralen = 0;
@@ -556,6 +924,7 @@
 	interface->max_altsetting = USB_ALTSETTINGALLOC;
 
 	interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL);
+	
 	if (!interface->altsetting) {
 		printk(KERN_ERR "couldn't kmalloc interface->altsetting\n");
 		return -1;
@@ -634,6 +1003,7 @@
 			ifp->extralen = 0;
 		} else {
 			ifp->extra = kmalloc(len, GFP_KERNEL);
+
 			if (!ifp->extra) {
 				printk(KERN_ERR "couldn't allocate memory for interface extra descriptors\n");
 				ifp->extralen = 0;
@@ -694,7 +1064,7 @@
 	return parsed;
 }
 
-static int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer)
+int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer)
 {
 	int i;
 	int retval;
@@ -702,7 +1072,7 @@
 	struct usb_descriptor_header *header;
 
 	memcpy(config, buffer, USB_DT_INTERFACE_SIZE);
-
+	usb_show_config_descriptor(config);
 	le16_to_cpus(&config->wTotalLength);
 	size = config->wTotalLength;
 
@@ -714,6 +1084,7 @@
 	config->interface = (struct usb_interface *)
 		kmalloc(config->bNumInterfaces *
 		sizeof(struct usb_interface), GFP_KERNEL);
+	printk("kmalloc IF %p, numif %i\n",config->interface,config->bNumInterfaces);
 	if (!config->interface) {
 		printk(KERN_WARNING "usb: out of memory\n");
 		return -1;	
@@ -754,7 +1125,7 @@
 
 void usb_destroy_configuration(struct usb_device *dev)
 {
-	int c, i, j;
+	int c, i, j, k;
 	
 	if (!dev->config)
 		return;
@@ -763,24 +1134,34 @@
 		struct usb_config_descriptor *cf = &dev->config[c];
 
 		if (!cf->interface)
-		        break;
+			break;
 
 		for (i = 0; i < cf->bNumInterfaces; i++) {
 			struct usb_interface *ifp =
 				&cf->interface[i];
-
+				
 			if (!ifp->altsetting)
-			        break;
+				break;
 
 			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);
@@ -815,16 +1196,16 @@
 
 	printk("usbcore: USB disconnect on device %d\n", dev->devnum);
 
-        if (dev->actconfig) {
-                for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
-                        struct usb_interface *interface = &dev->actconfig->interface[i];
+	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) {
-			        driver->disconnect(dev, interface->private_data);
+			if (driver) {
+				driver->disconnect(dev, interface->private_data);
 				usb_driver_release_interface(driver, interface);
-                        }
-                }
-        }
+			}
+		}
+	}
 
 	/* Free up all the children.. */
 	for (i = 0; i < USB_MAXCHILDREN; i++) {
@@ -838,8 +1219,9 @@
 	/* Free up the device itself, including its device number */
 	if (dev->devnum > 0)
 		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
-
+	
 	usb_free_dev(dev);
+	
 }
 
 /*
@@ -851,9 +1233,8 @@
 void usb_connect(struct usb_device *dev)
 {
 	int devnum;
-
-	dev->descriptor.bMaxPacketSize0 = 8;	/* Start off at 8 bytes */
-
+	// FIXME needs locking for SMP!!
+	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);
@@ -865,10 +1246,14 @@
  * These are the actual routines to send
  * and receive control messages.
  */
+
+#define GET_TIMEOUT 3
+#define SET_TIMEOUT 3
+
 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);
+		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)
@@ -879,8 +1264,8 @@
 	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)) >= 0 ||
-		    result == USB_ST_STALL)
+			(type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 ||
+		     result == -EPIPE)
 			break;
 	}
 	return result;
@@ -890,7 +1275,7 @@
 {
 	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);
+		(USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
 }
 
 int usb_get_device_descriptor(struct usb_device *dev)
@@ -909,7 +1294,7 @@
 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);
+		USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT);
 }
 
 int usb_get_protocol(struct usb_device *dev)
@@ -919,7 +1304,7 @@
 
 	if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 	    USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_RT_HIDD,
-	    0, 1, &type, 1, HZ)) < 0)
+	    0, 1, &type, 1, HZ * GET_TIMEOUT)) < 0)
 		return ret;
 
 	return type;
@@ -928,7 +1313,7 @@
 int usb_set_protocol(struct usb_device *dev, int protocol)
 {
 	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-		USB_REQ_SET_PROTOCOL, USB_RT_HIDD, protocol, 1, NULL, 0, HZ);
+		USB_REQ_SET_PROTOCOL, USB_RT_HIDD, protocol, 1, NULL, 0, HZ * SET_TIMEOUT);
 }
 
 /* keyboards want a nonzero duration according to HID spec, but
@@ -936,7 +1321,7 @@
 int usb_set_idle(struct usb_device *dev,  int duration, int report_id)
 {
 	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_IDLE,
-		USB_RT_HIDD, (duration << 8) | report_id, 1, NULL, 0, HZ);
+		USB_RT_HIDD, (duration << 8) | report_id, 1, NULL, 0, HZ * SET_TIMEOUT);
 }
 
 static void usb_set_maxpacket(struct usb_device *dev)
@@ -979,7 +1364,7 @@
 */
 
 	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-		USB_REQ_CLEAR_FEATURE, USB_RT_ENDPOINT, 0, endp, NULL, 0, HZ);
+		USB_REQ_CLEAR_FEATURE, USB_RT_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT);
 
 	/* don't clear if failed */
 	if (result < 0)
@@ -987,12 +1372,12 @@
 
 	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_ENDPOINT, 0, endp,
-		&status, sizeof(status), HZ);
+		&status, sizeof(status), HZ * SET_TIMEOUT);
 	if (result < 0)
 		return result;
 
 	if (status & 1)
-		return USB_ST_STALL;		/* still halted */
+		return -EPIPE;		/* still halted */
 
 	usb_endpoint_running(dev, endp & 0x0f, usb_endpoint_out(endp));
 
@@ -1005,14 +1390,26 @@
 
 int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 {
-	int ret;
+	struct usb_interface *iface = NULL;
+	int ret, i;
+
+	for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+		if (dev->actconfig->interface[i].altsetting->bInterfaceNumber == interface) {
+			iface = &dev->actconfig->interface[i];
+			break;
+		}
+	}
+	if (!iface) {
+		printk(KERN_INFO "usb: selecting invalid interface %d\n", interface);
+		return -EINVAL;
+	}
 
 	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 	    USB_REQ_SET_INTERFACE, USB_RT_INTERFACE, alternate,
-	    interface, NULL, 0, HZ)) < 0)
+	    interface, NULL, 0, HZ * 5)) < 0)
 		return ret;
 
-	dev->actconfig->interface[interface].act_altsetting = alternate;
+	iface->act_altsetting = alternate;
 	usb_set_maxpacket(dev);
 	return 0;
 }
@@ -1034,7 +1431,7 @@
 	}
 
 	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-	    USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ)) < 0)
+	    USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0)
 		return ret;
 
 	dev->actconfig = cp;
@@ -1049,11 +1446,12 @@
 {
 	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 		USB_REQ_GET_REPORT, USB_DIR_IN | USB_RT_HIDD,
-		(type << 8) + id, index, buf, size, HZ);
+		(type << 8) + id, index, buf, size, HZ * GET_TIMEOUT);
 }
 
 int usb_get_configuration(struct usb_device *dev)
 {
+       	int result;
 	unsigned int cfgno;
 	unsigned char buffer[8];
 	unsigned char *bigbuffer;
@@ -1076,14 +1474,14 @@
 		sizeof(struct usb_config_descriptor));
 
 	for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
-        	int result;
+
 
 		/* 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 < 0) {
 			printk(KERN_ERR "usb: unable to get descriptor\n");
-			return result;
+			goto err;
 		}
 
   	  	/* Get the full buffer */
@@ -1092,7 +1490,8 @@
 		bigbuffer = kmalloc(desc->wTotalLength, GFP_KERNEL);
 		if (!bigbuffer) {
 			printk(KERN_ERR "unable to allocate memory for configuration descriptors\n");
-			return USB_ST_INTERNALERROR;
+			result=-ENOMEM;
+			goto err;
 		}
 
 		/* Now that we know the length, get the whole thing */
@@ -1100,19 +1499,24 @@
 		if (result < 0) {
 			printk(KERN_ERR "couldn't get all of config descriptors\n");
 			kfree(bigbuffer);
-			return result;
-		}
-			
+			goto err;
+		}		
 		result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
 		kfree(bigbuffer);
 
 		if (result > 0)
 			printk(KERN_INFO "usb: descriptor data left\n");
 		else if (result < 0)
-			return -1;
+		{
+			result=-1;
+			goto err;
+		}
 	}
 
 	return 0;
+	err:
+	dev->descriptor.bNumConfigurations=cfgno;
+	return result;
 }
 
 char *usb_string(struct usb_device *dev, int index)
@@ -1178,7 +1582,7 @@
 
 	printk(KERN_INFO "USB new device connect, assigned device number %d\n",
 		dev->devnum);
-
+ 
 	dev->maxpacketsize = 0;		/* Default to 8 byte max packet size */
 	dev->epmaxpacketin [0] = 8;
 	dev->epmaxpacketout[0] = 8;
@@ -1190,6 +1594,7 @@
 	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
 	if (err < 0) {
 		printk(KERN_ERR "usbcore: USB device not responding, giving up (error=%d)\n", err);
+		clear_bit(addr, &dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -1206,8 +1611,10 @@
 	dev->devnum = addr;
 
 	err = usb_set_address(dev);
+	
 	if (err < 0) {
 		printk(KERN_ERR "usbcore: USB device not accepting new address (error=%d)\n", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -1216,14 +1623,17 @@
 
 	err = usb_get_device_descriptor(dev);
 	if (err < 0) {
-		printk(KERN_ERR "usbcore: unable to get device descriptor (error=%d)\n", err);
+		printk(KERN_ERR "usbcore: unable to get device descriptor (error=%d)\n",err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
 
-	err = usb_get_configuration(dev);
+	err=usb_get_configuration(dev);
+	
 	if (err < 0) {
 		printk(KERN_ERR "usbcore: unable to get configuration (error=%d)\n", err);
+		clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
 		dev->devnum = -1;
 		return 1;
 	}
@@ -1245,189 +1655,11 @@
 	proc_usb_add_device(dev);
 
 	/* find drivers willing to handle this device */
-        usb_find_drivers(dev);
+	usb_find_drivers(dev);
 
 	return 0;
 }
 
-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;
-	int ret;
-
-        dr.requesttype = requesttype;
-        dr.request = request;
-        dr.value = cpu_to_le16p(&value);
-        dr.index = cpu_to_le16p(&index);
-        dr.length = cpu_to_le16p(&size);
-
-        ret = dev->bus->op->control_msg(dev, pipe, &dr, data, size, timeout);
-
-	if (ret < 0 && usb_debug) {
-		unsigned char *p = (unsigned char *)&dr;
-
-		printk(KERN_DEBUG "Failed control msg - r:%02X rt:%02X v:%04X i:%04X s:%04X - ret: %d\n",
-			request, requesttype, value, index, size, ret);
-		printk(KERN_DEBUG "  %02X %02X %02X %02X %02X %02X %02X %02X\n",
-			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
-	}
-
-	return ret;
-}
-
-int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle)
-{
-	long    bustime;
-	int	ret;
-
-	*handle = NULL;
-
-	/* Check host controller's bandwidth for this int. request. */
-	bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0,
-			usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
-	bustime = NS_TO_US(bustime);	/* work in microseconds */
-	if (check_bandwidth_alloc (dev->bus->bandwidth_allocated, bustime))
-		return (USB_ST_BANDWIDTH_ERROR);
-
-	ret = dev->bus->op->request_irq(dev, pipe, handler, period, dev_id, handle, bustime);
-
-	/* Claim the USB bandwidth if no error. */
-	if (!ret) {
-		dev->bus->bandwidth_allocated += bustime;
-		dev->bus->bandwidth_int_reqs++;
-		PRINTD ("bw_alloc bumped to %d for %d requesters",
-			dev->bus->bandwidth_allocated,
-			dev->bus->bandwidth_int_reqs +
-			dev->bus->bandwidth_isoc_reqs);
-	}
-
-	return ret;
-}
-
-int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, unsigned long *rval, int timeout)
-{
-	return dev->bus->op->bulk_msg(dev, pipe, data, len, rval, timeout);
-}
-
-void *usb_request_bulk(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, void *data, int len, void *dev_id)
-{
-	return dev->bus->op->request_bulk(dev, pipe, handler, data, len, dev_id);
-}
-
-int usb_terminate_bulk(struct usb_device *dev, void *first)
-{
-	return dev->bus->op->terminate_bulk(dev, first);
-}
-
-/*
- * usb_release_bandwidth():
- *
- * called to release an interrupt pipe's bandwidth (in microseconds)
- */
-void usb_release_bandwidth(struct usb_device *dev, int bw_alloc)
-{
-	dev->bus->bandwidth_allocated -= bw_alloc;
-	dev->bus->bandwidth_int_reqs--;
-	PRINTD ("bw_alloc reduced to %d for %d requesters",
-		dev->bus->bandwidth_allocated,
-		dev->bus->bandwidth_int_reqs +
-		dev->bus->bandwidth_isoc_reqs);
-}
-
-int usb_release_irq(struct usb_device *dev, void *handle, unsigned int pipe)
-{
-	long    bustime;
-	int	err;
-
-	err = dev->bus->op->release_irq(dev, handle);
-
-	/* Return the USB bandwidth if no error. */
-	if (!err) {
-		bustime = calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), 0,
-				usb_maxpacket(dev, pipe, usb_pipeout(pipe)));
-		bustime = NS_TO_US(bustime);	/* work in microseconds */
-		usb_release_bandwidth(dev, bustime);
-	}
-
-	return err;
-}
-
-/*
- * 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);
-}
-
-int usb_init_isoc(struct usb_device *usb_dev,
-			unsigned int pipe,
-			int frame_count,
-			void *context,
-			struct usb_isoc_desc **isocdesc)
-{
-	long    bustime;
-	int	err;
-
-	if (frame_count <= 0)
-		return -EINVAL;
-
-	/* Check host controller's bandwidth for this Isoc. request. */
-	/* TBD: some way to factor in frame_spacing ??? */
-	bustime = calc_bus_time (0, usb_pipein(pipe), 1,
-			usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)));
-	bustime = NS_TO_US(bustime) / frame_count;	/* work in microseconds */
-	if (check_bandwidth_alloc (usb_dev->bus->bandwidth_allocated, bustime))
-		return USB_ST_BANDWIDTH_ERROR;
-
-	err = usb_dev->bus->op->init_isoc (usb_dev, pipe, frame_count, context, isocdesc);
-
-	/* Claim the USB bandwidth if no error. */
-	if (!err) {
-		usb_dev->bus->bandwidth_allocated += bustime;
-		usb_dev->bus->bandwidth_isoc_reqs++;
-		PRINTD ("bw_alloc bumped to %d for %d requesters",
-			usb_dev->bus->bandwidth_allocated,
-			usb_dev->bus->bandwidth_int_reqs +
-			usb_dev->bus->bandwidth_isoc_reqs);
-	}
-
-	return err;
-}
-
-void usb_free_isoc(struct usb_isoc_desc *isocdesc)
-{
-	long    bustime;
-
-	/* Return the USB bandwidth. */
-	bustime = calc_bus_time (0, usb_pipein(isocdesc->pipe), 1,
-			usb_maxpacket(isocdesc->usb_dev, isocdesc->pipe,
-			usb_pipeout(isocdesc->pipe)));
-	bustime = NS_TO_US(bustime) / isocdesc->frame_count;
-	isocdesc->usb_dev->bus->bandwidth_allocated -= bustime;
-	isocdesc->usb_dev->bus->bandwidth_isoc_reqs--;
-	PRINTD ("bw_alloc reduced to %d for %d requesters",
-		isocdesc->usb_dev->bus->bandwidth_allocated,
-		isocdesc->usb_dev->bus->bandwidth_int_reqs +
-		isocdesc->usb_dev->bus->bandwidth_isoc_reqs);
-
-	isocdesc->usb_dev->bus->op->free_isoc (isocdesc);
-}
-
-int usb_run_isoc(struct usb_isoc_desc *isocdesc,
-			struct usb_isoc_desc *pr_isocdesc)
-{
-	return isocdesc->usb_dev->bus->op->run_isoc (isocdesc, pr_isocdesc);
-}
-
-int usb_kill_isoc(struct usb_isoc_desc *isocdesc)
-{
-	return isocdesc->usb_dev->bus->op->kill_isoc (isocdesc);
-}
-
 static int usb_open(struct inode * inode, struct file * file)
 {
 	int minor = MINOR(inode->i_rdev);
@@ -1442,16 +1674,16 @@
 }
 
 static struct file_operations usb_fops = {
-        NULL,		/* seek */
+	NULL,		/* seek */
 	NULL,		/* read */
 	NULL,		/* write */
 	NULL,		/* readdir */
 	NULL,		/* poll */
 	NULL,		/* ioctl */
 	NULL,		/* mmap */
-        usb_open,
+	usb_open,
 	NULL,		/* flush */
-        NULL		/* release */
+	NULL		/* release */
 };
 
 void usb_major_init(void)
@@ -1519,15 +1751,16 @@
 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_request_irq);
 EXPORT_SYMBOL(usb_release_irq);
 EXPORT_SYMBOL(usb_bulk_msg);
 EXPORT_SYMBOL(usb_request_bulk);
 EXPORT_SYMBOL(usb_terminate_bulk);
-
-EXPORT_SYMBOL(usb_get_current_frame_number);
-EXPORT_SYMBOL(usb_init_isoc);
-EXPORT_SYMBOL(usb_free_isoc);
-EXPORT_SYMBOL(usb_run_isoc);
-EXPORT_SYMBOL(usb_kill_isoc);

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