# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.545   -> 1.546  
#	drivers/usb/core/hcd.h	1.6     -> 1.7    
#	 include/linux/usb.h	1.31    -> 1.32   
#	drivers/usb/core/usb.c	1.52    -> 1.53   
#	drivers/usb/host/uhci.c	1.41    -> 1.42   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/13	johannes@erdfelt.com	1.546
# [PATCH] USB device reference counting fix for uhci.c and usb core
# 
# Earlier in the 2.5 development cycle a patch was applied that changed
# the reference counting behaviour for USB devices.
# 
# There are a couple of problems with the change:
# - It made the USB code more complicated as a whole with the introduction
#   of an additional cleanup path for devices. Using the traditional method
#   of reference counting, cleanup is handled implictly
# - It reduces functionality by requiring a callback for all references to
#   the device, but doesn't provide a method of providing callbacks for
#   references. It relies on the hardcoded device driver ->disconnect and
#   HCD ->deallocate method for callbacks
# 
# The traditional method of using reference counting supports as many
# reference users as needed, without complicating it with mandatory
# callbacks to cleanup references.
# 
# The change in 2.5 also only helps catch one subset of programming
# problem in device drivers, the case where it decrements too many times.
# That is of dubious debugging value.
# 
# So, this patch reverts the change and makes the reference counting
# behave like it does in the rest of the kernel as well as how the USB
# code does in 2.4.
# 
# This patch doesn't remove all of the superfluous code. Some drivers,
# like usb-ohci, ohci-hcd and ehci-hcd have some code that is no longer
# needed. I wanted to spend some more time with those drivers since the
# changes weren't as trivial as uhci.c and usb-uhci.c.
# 
# I've tested with uhci and usb-ohci with no adverse effects.
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Mon May 13 15:53:48 2002
+++ b/drivers/usb/core/hcd.h	Mon May 13 15:53:49 2002
@@ -182,9 +182,6 @@
 /* -------------------------------------------------------------------------- */
 
 /* Enumeration is only for the hub driver, or HCD virtual root hubs */
-extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
-	struct usb_bus *);
-extern void usb_free_dev(struct usb_device *);
 extern int usb_new_device(struct usb_device *dev);
 extern void usb_connect(struct usb_device *dev);
 extern void usb_disconnect(struct usb_device **);
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Mon May 13 15:53:49 2002
+++ b/drivers/usb/core/usb.c	Mon May 13 15:53:49 2002
@@ -962,7 +962,8 @@
 
 	init_MUTEX(&dev->serialize);
 
-	dev->bus->op->allocate(dev);
+	if (dev->bus->op->allocate)
+		dev->bus->op->allocate(dev);
 
 	return dev;
 }
@@ -978,16 +979,8 @@
  */
 void usb_free_dev(struct usb_device *dev)
 {
-	if (in_interrupt ())
-		BUG ();
-	if (!atomic_dec_and_test (&dev->refcnt)) {
-		/* MUST go to zero here, else someone's hanging on to
-		 * a device that's supposed to have been cleaned up!!
-		 */
-		BUG ();
-	}
-
-	dev->bus->op->deallocate (dev);
+	if (dev->bus->op->deallocate)
+		dev->bus->op->deallocate(dev);
 	usb_destroy_configuration (dev);
 	usb_bus_put (dev->bus);
 	kfree (dev);
@@ -1933,8 +1926,9 @@
 		put_device(&dev->dev);
 	}
 
-	/* Free up the device itself */
-	usb_free_dev(dev);
+	/* Decrement the reference count, it'll auto free everything when */
+	/* it hits 0 which could very well be now */
+	usb_dec_dev_use(dev);
 }
 
 /**
diff -Nru a/drivers/usb/host/uhci.c b/drivers/usb/host/uhci.c
--- a/drivers/usb/host/uhci.c	Mon May 13 15:53:49 2002
+++ b/drivers/usb/host/uhci.c	Mon May 13 15:53:49 2002
@@ -109,19 +109,6 @@
 #define MAX_URB_LOOP	2048		/* Maximum number of linked URB's */
 
 /*
- * Only the USB core should call uhci_alloc_dev and uhci_free_dev
- */
-static int uhci_alloc_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
-static int uhci_free_dev(struct usb_device *dev)
-{
-	return 0;
-}
-
-/*
  * Technically, updating td->status here is a race, but it's not really a
  * problem. The worst that can happen is that we set the IOC bit again
  * generating a spurios interrupt. We could fix this by creating another
@@ -1882,8 +1869,6 @@
 }
 
 struct usb_operations uhci_device_operations = {
-	allocate:		uhci_alloc_dev,
-	deallocate:		uhci_free_dev,
 	get_frame_number:	uhci_get_current_frame_number,
 	submit_urb:		uhci_submit_urb,
 	unlink_urb:		uhci_unlink_urb,
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Mon May 13 15:53:49 2002
+++ b/include/linux/usb.h	Mon May 13 15:53:49 2002
@@ -426,6 +426,11 @@
 	struct usb_device *children[USB_MAXCHILDREN];
 };
 
+/* usb_free_dev can be called anywhere from usb_dec_dev_use */
+extern struct usb_device *usb_alloc_dev(struct usb_device *parent,
+	struct usb_bus *);
+extern void usb_free_dev(struct usb_device *);
+
 /* for when layers above USB add new non-USB drivers */
 extern void usb_scan_devices(void);
 
@@ -455,27 +460,11 @@
  * @dev: the device no longer being referenced
  *
  * Each live reference to a device should be refcounted.
- *
- * Drivers for USB interfaces should normally release such references in
- * their disconnect() methods, and record them in probe().
- *
- * Note that driver disconnect() methods must guarantee that when they
- * return, all of their outstanding references to the device (and its
- * interfaces) are cleaned up.  That means that all pending URBs from
- * this driver must have completed, and that no more copies of the device
- * handle are saved in driver records (including other kernel threads).
  */
 static inline void usb_dec_dev_use (struct usb_device *dev)
 {
-	if (atomic_dec_and_test (&dev->refcnt)) {
-		/* May only go to zero when usbcore finishes
-		 * usb_disconnect() processing:  khubd or HCDs.
-		 *
-		 * If you hit this BUG() it's likely a problem
-		 * with some driver's disconnect() routine.
-		 */
-		BUG ();
-	}
+	if (atomic_dec_and_test(&dev->refcnt))
+		usb_free_dev(dev);
 }
 
 
