Hi all,

Here's a patch against 2.5.3-pre6 that is a first cut at adding proper
reference counting logic to the USB urb structures.

There are two new functions:
	usb_get_urb
	usb_put_urb

usb_put_urb() maps directly to usb_free_urb() to prevent any device
drivers from having to change at this time.  usb_get_urb() is used by
any driver that gets control over an urb (like a USB host controller
driver.)

So with this scheme, it is now possible to do the following from a USB
device driver:
	urb = usb_alloc_urb(0);
	usb_fill_urb_bulk(...);
	usb_submit_urb(urb);
	usb_free_urb(urb);
and then when the host controller driver is finished with the urb, it
will be destroyed properly.

This patch only modifies the hcd.c file, so only the ohci-hcd and
ehci-hcd drivers will take ownership over a urb, but the other drivers
still work right now.

I haven't really tested this out much yet, but want to give everyone
else a chance for comments on the implementation.

thanks,

greg k-h



diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c
--- a/drivers/usb/hcd.c	Tue Jan 29 00:21:56 2002
+++ b/drivers/usb/hcd.c	Tue Jan 29 00:21:57 2002
@@ -916,8 +922,9 @@
 /* may be called in any context with a valid urb->dev usecount */
 /* caller surrenders "ownership" of urb (and chain at urb->next).  */
 
-static int hcd_submit_urb (struct urb *urb)
+static int hcd_submit_urb (struct urb *new_urb)
 {
+	struct urb		*urb;
 	int			status;
 	struct usb_hcd		*hcd;
 	struct hcd_dev		*dev;
@@ -925,27 +932,40 @@
 	int			pipe;
 	int			mem_flags;
 
-	if (!urb || urb->hcpriv || !urb->complete)
+	if (!new_urb)
 		return -EINVAL;
+	urb = usb_get_urb (new_urb);
+	if (urb->hcpriv || !urb->complete) {
+		usb_put_urb (urb);
+		return -EINVAL;
+	}
 
 	urb->status = -EINPROGRESS;
 	urb->actual_length = 0;
 	INIT_LIST_HEAD (&urb->urb_list);
 
-	if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0)
+	if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0) {
+		usb_put_urb (urb);
 		return -ENODEV;
+	}
 	hcd = urb->dev->bus->hcpriv;
 	dev = urb->dev->hcpriv;
-	if (!hcd || !dev)
+	if (!hcd || !dev) {
+		usb_put_urb (urb);
 		return -ENODEV;
+	}
 
 	/* can't submit new urbs when quiescing, halted, ... */
-	if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state))
+	if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state)) {
+		usb_put_urb (urb);
 		return -ESHUTDOWN;
+	}
 	pipe = urb->pipe;
 	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe),
-			usb_pipeout (pipe)))
+			usb_pipeout (pipe))) {
+		usb_put_urb (urb);
 		return -EPIPE;
+	}
 
 	// FIXME paging/swapping requests over USB should not use GFP_KERNEL
 	// and might even need to use GFP_NOIO ... that flag actually needs
@@ -1328,5 +1348,6 @@
 	/* pass ownership to the completion handler */
 	usb_dec_dev_use (dev);
 	urb->complete (urb);
+	usb_put_urb (urb);
 }
 EXPORT_SYMBOL (usb_hcd_giveback_urb);
diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c
--- a/drivers/usb/usb.c	Tue Jan 29 00:21:56 2002
+++ b/drivers/usb/usb.c	Tue Jan 29 00:21:56 2002
@@ -1098,7 +1099,7 @@
 	}
 
 	memset(urb, 0, sizeof(*urb));
-
+	atomic_inc(&urb->count);
 	spin_lock_init(&urb->lock);
 
 	return urb;
@@ -1115,8 +1116,20 @@
 void usb_free_urb(struct urb *urb)
 {
 	if (urb)
-		kfree(urb);
+		if (atomic_dec_and_test(&urb->count))
+			kfree(urb);
+}
+
+struct urb * usb_get_urb(struct urb *urb)
+{
+	if (urb) {
+		atomic_inc(&urb->count);
+		return urb;
+	} else
+		return NULL;
 }
+		
+		
 /*-------------------------------------------------------------------*/
 
 /**
@@ -2794,6 +2808,7 @@
 // asynchronous request completion model
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
+EXPORT_SYMBOL(usb_get_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Tue Jan 29 00:21:56 2002
+++ b/include/linux/usb.h	Tue Jan 29 00:21:56 2002
@@ -721,6 +723,7 @@
 struct urb
 {
 	spinlock_t lock;		/* lock for the URB */
+	atomic_t count;			/* reference count of the URB */
 	void *hcpriv;			/* private data for host controller */
 	struct list_head urb_list;	/* list pointer to all active urbs */
 	struct urb *next; 		/* (in) pointer to next URB */
@@ -854,6 +857,8 @@
 
 extern struct urb *usb_alloc_urb(int iso_packets);
 extern void usb_free_urb(struct urb *urb);
+#define usb_put_urb usb_free_urb
+extern struct urb *usb_get_urb(struct urb *urb);
 extern int usb_submit_urb(struct urb *urb);
 extern int usb_unlink_urb(struct urb *urb);
 
