Hi all,
Here's a patch against 2.5.3-pre6 that is a first cut at adding
reference counting logic to the USB urb structure.
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);
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel