# 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.596   -> 1.597  
#	drivers/usb/core/hcd.h	1.4     -> 1.5    
#	 include/linux/usb.h	1.25    -> 1.26   
#	drivers/usb/core/hub.c	1.21    -> 1.22   
#	drivers/usb/core/usb.c	1.39    -> 1.40   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/04/08	david-b@pacbell.net	1.597
# This patch is a more complete fix for the device refcount
# sanity checking and cleanup on device disconnect.
#   
#     - Splits apart usb_dec_dev_use(), for driver use, and
#       usb_free_dev(), for hub/hcd use.  Both now have
#       kerneldoc, and will BUG() if the refcount and the
#       device tree get out of sync.  (Except for cleanup of
#       root hub init errors, refcount must go to zero only
#       at the instant disconnect processing completes.)
#   
#     - More usbcore-internal function declarations are
#       now moved out of <linux/usb.h> into hcd.h
#   
#     - Driver-accessible refcounting is now inlined; minor
#       code shrinkage, it's using atomic inc/dec instructions
#       not function calls.
# 
# <note from greg k-h, there is still some work to be done with USB device
#  reference counting, but this patch is a step in the right direction.>
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h	Mon Apr  8 15:49:08 2002
+++ b/drivers/usb/core/hcd.h	Mon Apr  8 15:49:08 2002
@@ -181,6 +181,24 @@
 
 #endif /* CONFIG_PCI */
 
+/* -------------------------------------------------------------------------- */
+
+/* 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 **);
+
+#ifndef _LINUX_HUB_H
+/* exported to hub driver ONLY to support usb_reset_device () */
+extern int usb_get_configuration(struct usb_device *dev);
+extern void usb_set_maxpacket(struct usb_device *dev);
+extern void usb_destroy_configuration(struct usb_device *dev);
+extern int usb_set_address(struct usb_device *dev);
+#endif /* _LINUX_HUB_H */
+
 /*-------------------------------------------------------------------------*/
 
 /*
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Mon Apr  8 15:49:08 2002
+++ b/drivers/usb/core/hub.c	Mon Apr  8 15:49:08 2002
@@ -29,6 +29,7 @@
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
+#include "hcd.h"
 #include "hub.h"
 
 /* Wakes up khubd */
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Mon Apr  8 15:49:08 2002
+++ b/drivers/usb/core/usb.c	Mon Apr  8 15:49:08 2002
@@ -826,49 +826,31 @@
 	return dev;
 }
 
-// usbcore-internal ...
-// but usb_dec_dev_use() is #defined to this, and that's public!!
-// FIXME the public call should BUG() whenever count goes to zero,
-// the usbcore-internal one should do so _unless_ it does so...
+/**
+ * usb_free_dev - free a usb device structure (usbcore-internal)
+ * @dev: device that's been disconnected
+ * Context: !in_interrupt ()
+ *
+ * Used by hub and virtual root hub drivers.  The device is completely
+ * gone, everything is cleaned up, so it's time to get rid of these last
+ * records of this device.
+ */
 void usb_free_dev(struct usb_device *dev)
 {
-	if (atomic_dec_and_test(&dev->refcnt)) {
-		/* Normally only goes to zero in usb_disconnect(), from
-		 * khubd or from roothub shutdown (rmmod/apmd/... thread).
-		 * Abnormally, roothub init errors can happen, so HCDs
-		 * call this directly.
-		 *
-		 * Otherwise this is a nasty device driver bug, often in
-		 * disconnect processing.
+	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!!
 		 */
-		if (in_interrupt ())
-			BUG ();
-
-		dev->bus->op->deallocate(dev);
-		usb_destroy_configuration(dev);
-
-		usb_bus_put(dev->bus);
-
-		kfree(dev);
+		BUG ();
 	}
-}
 
-/**
- * usb_inc_dev_use - record another reference to a device
- * @dev: the device being referenced
- *
- * Each live reference to a device should be refcounted.
- *
- * Device drivers should normally record such references in their
- * open() methods.
- * Drivers should then release them, using usb_dec_dev_use(), in their
- * close() methods.
- */
-void usb_inc_dev_use(struct usb_device *dev)
-{
-	atomic_inc(&dev->refcnt);
+	dev->bus->op->deallocate (dev);
+	usb_destroy_configuration (dev);
+	usb_bus_put (dev->bus);
+	kfree (dev);
 }
-
 
 /**
  * usb_alloc_urb - creates a new urb for a USB driver to use
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Mon Apr  8 15:49:08 2002
+++ b/include/linux/usb.h	Mon Apr  8 15:49:08 2002
@@ -964,24 +964,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 **);
-
-#ifndef _LINUX_HUB_H
-/* exported to hub driver ONLY to support usb_reset_device () */
-extern int usb_get_configuration(struct usb_device *dev);
-extern void usb_set_maxpacket(struct usb_device *dev);
-extern void usb_destroy_configuration(struct usb_device *dev);
-extern int usb_set_address(struct usb_device *dev);
-#endif /* _LINUX_HUB_H */
-
-/* -------------------------------------------------------------------------- */
-
 /* This is arbitrary.
  * From USB 2.0 spec Table 11-13, offset 7, a hub can
  * have up to 255 ports. The most yet reported is 10.
@@ -1050,9 +1032,49 @@
 /* for drivers using iso endpoints */
 extern int usb_get_current_frame_number (struct usb_device *usb_dev);
 
-/* drivers must track when they bind to a device's interfaces */
-extern void usb_inc_dev_use(struct usb_device *);
-#define usb_dec_dev_use usb_free_dev
+/**
+ * usb_inc_dev_use - record another reference to a device
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them usb_dec_dev_use(), in their disconnect() methods.
+ */
+static inline void usb_inc_dev_use (struct usb_device *dev)
+{
+	atomic_inc (&dev->refcnt);
+}
+
+/**
+ * usb_dec_dev_use - drop a reference to a device
+ * @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 ();
+	}
+}
+
 
 /* used these for multi-interface device registration */
 extern int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned int ifnum);
