Nobody has paid any attention to this for a while, so here's my
implementation of usb_kill_urb(). The patch to follow (as278) shows how
using it can simplify the hub driver.
If anyone objects to the two new fields added to struct urb, I've got two
variations on this patch. One uses a single wait_queue_header for all
URBs, removing those two fields at the cost of some spurious wakeups. The
other uses a polling loop rather than wait_queues, at the cost of a
longer-than-necessary delay. Arguments over which approach is best should
be directed to Oliver. :-)
Greg, this patch alone should be harmless. Unless someone objects to it
in the next day or two or asks to see the variations, I say go ahead and
apply it.
Alan Stern
===== Documentation/usb/error-codes.txt 1.9 vs edited =====
--- 1.9/Documentation/usb/error-codes.txt Wed Mar 17 14:16:42 2004
+++ edited/Documentation/usb/error-codes.txt Wed May 12 14:17:46 2004
@@ -47,6 +47,9 @@
-ESHUTDOWN The host controller has been disabled due to some
problem that could not be worked around.
+-EPERM Submission failed because URB_REJECT was set in the
+ URB's transfer_flags.
+
**************************************************************************
* Error codes returned by in urb->status *
===== include/linux/usb.h 1.185 vs edited =====
--- 1.185/include/linux/usb.h Mon May 3 02:42:51 2004
+++ edited/include/linux/usb.h Wed May 12 14:00:09 2004
@@ -586,6 +586,7 @@
#define URB_NO_FSBR 0x0020 /* UHCI-specific */
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet
*/
#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */
+#define URB_REJECT 0x0100 /* Submissions will fail */
struct usb_iso_packet_descriptor {
unsigned int offset;
@@ -667,7 +668,7 @@
* calling usb_alloc_urb() and freed with a call to usb_free_urb().
* Initialization may be done using various usb_fill_*_urb() functions. URBs
* are submitted using usb_submit_urb(), and pending requests may be canceled
- * using usb_unlink_urb().
+ * using usb_unlink_urb() or usb_kill_urb().
*
* Data Transfer Buffers:
*
@@ -772,6 +773,8 @@
void *hcpriv; /* private data for host controller */
struct list_head urb_list; /* list pointer to all active urbs */
int bandwidth; /* bandwidth for INT/ISO request */
+ wait_queue_head_t handler_queue;/* waiting for completion handlers */
+ atomic_t use_count; /* count of concurrent susbmissions */
/* public, documented fields in the urb that can be used by drivers */
struct usb_device *dev; /* (in) pointer to associated device */
@@ -907,6 +910,7 @@
extern struct urb *usb_get_urb(struct urb *urb);
extern int usb_submit_urb(struct urb *urb, int mem_flags);
extern int usb_unlink_urb(struct urb *urb);
+extern void usb_kill_urb(struct urb *urb);
#define HAVE_USB_BUFFERS
void *usb_buffer_alloc (struct usb_device *dev, size_t size,
===== drivers/usb/core/hcd.c 1.137 vs edited =====
--- 1.137/drivers/usb/core/hcd.c Wed Apr 21 06:46:29 2004
+++ edited/drivers/usb/core/hcd.c Wed May 12 13:37:57 2004
@@ -1074,23 +1074,28 @@
// FIXME: verify that quiescing hc works right (RH cleans up)
spin_lock_irqsave (&hcd_data_lock, flags);
- if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+ if (unlikely(urb->transfer_flags & URB_REJECT))
+ status = -EPERM;
+ else if (HCD_IS_RUNNING (hcd->state) &&
+ hcd->state != USB_STATE_QUIESCING) {
usb_get_dev (urb->dev);
list_add_tail (&urb->urb_list, &dev->urb_list);
status = 0;
- } else {
- INIT_LIST_HEAD (&urb->urb_list);
+ } else
status = -ESHUTDOWN;
- }
spin_unlock_irqrestore (&hcd_data_lock, flags);
- if (status)
+ if (status) {
+ INIT_LIST_HEAD (&urb->urb_list);
return status;
+ }
/* increment urb's reference count as part of giving it to the HCD
* (which now controls it). HCD guarantees that it either returns
* an error or calls giveback(), but not both.
*/
urb = usb_get_urb (urb);
+ atomic_inc (&urb->use_count);
+
if (urb->dev == hcd->self.root_hub) {
/* NOTE: requirement on hub callers (usbfs and the hub
* driver, for now) that URBs' urb->transfer_buffer be
@@ -1128,8 +1133,10 @@
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
done:
if (status) {
- usb_put_urb (urb);
urb_unlink (urb);
+ if (atomic_dec_and_test (&urb->use_count))
+ wake_up (&urb->handler_queue);
+ usb_put_urb (urb);
}
return status;
}
@@ -1531,6 +1538,8 @@
/* pass ownership to the completion handler */
urb->complete (urb, regs);
+ if (atomic_dec_and_test (&urb->use_count))
+ wake_up (&urb->handler_queue);
usb_put_urb (urb);
}
EXPORT_SYMBOL (usb_hcd_giveback_urb);
===== drivers/usb/core/urb.c 1.41 vs edited =====
--- 1.41/drivers/usb/core/urb.c Wed Apr 28 10:00:02 2004
+++ edited/drivers/usb/core/urb.c Wed May 12 14:15:42 2004
@@ -41,6 +41,7 @@
memset(urb, 0, sizeof(*urb));
kref_init(&urb->kref, urb_destroy);
spin_lock_init(&urb->lock);
+ init_waitqueue_head(&urb->handler_queue);
}
}
@@ -461,10 +462,37 @@
return -ENODEV;
}
+/**
+ * usb_kill_urb - cancel a transfer request and wait for it to finish
+ * @urb: pointer to URB describing a previously submitted request
+ *
+ * This routine cancels an in-progress request. Unlike a synchronous
+ * call to usb_unlink_urb(), this also guarantees that upon return all
+ * completion handlers will have finished and the URB will be totally
+ * idle and available for reuse. These features make this an ideal
+ * way to stop I/O in a disconnect() callback or close() function.
+ *
+ * While the routine is running, attempts to resubmit the URB will fail
+ * with error -EPERM. Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * This routine may not be used in an interrupt context, such as a bottom
+ * half or a completion handler; or when holding a spinlock; or in other
+ * situations where the caller can't schedule().
+ */
+void usb_kill_urb(struct urb *urb)
+{
+ urb->transfer_flags |= (URB_ASYNC_UNLINK | URB_REJECT);
+ usb_unlink_urb(urb);
+ wait_event(urb->handler_queue, atomic_read(&urb->use_count) == 0);
+ urb->transfer_flags &= ~(URB_ASYNC_UNLINK | URB_REJECT);
+}
+
EXPORT_SYMBOL(usb_init_urb);
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);
+EXPORT_SYMBOL(usb_kill_urb);
-------------------------------------------------------
This SF.Net email is sponsored by: SourceForge.net Broadband
Sign-up now for SourceForge Broadband and get the fastest
6.0/768 connection for only $19.95/mo for the first 3 months!
http://ads.osdn.com/?ad_id=2562&alloc_id=6184&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel