Hi Alan, list, here's the patch to introduce anchors for use with "fire-and-forget" techniques and a modification for the skeleton driver to use them. This should make implementing pre/post_reset and suspend/resume much easier.
Regards Oliver -- --- a/include/linux/usb.h 2007-05-04 10:50:18.000000000 +0200 +++ b/include/linux/usb.h 2007-05-08 12:47:27.000000000 +0200 @@ -958,11 +958,26 @@ struct usb_iso_packet_descriptor { struct urb; +struct usb_anchor { + struct list_head urb_list; + wait_queue_head_t wait; + spinlock_t lock; +}; + +static inline void init_usb_anchor(struct usb_anchor *anchor) +{ + INIT_LIST_HEAD(&anchor->urb_list); + init_waitqueue_head(&anchor->wait); + spin_lock_init(&anchor->lock); +} + typedef void (*usb_complete_t)(struct urb *); /** * struct urb - USB Request Block * @urb_list: For use by current owner of the URB. + * @anchor_list: membership in the list of an anchor + * @anchor: to anchor URBs to a common mooring * @pipe: Holds endpoint number, direction, type, and more. * Create these values with the eight macros available; * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl" @@ -1135,6 +1150,8 @@ struct urb /* public: documented fields in the urb that can be used by drivers */ struct list_head urb_list; /* list head for use by the urb's * current owner */ + struct list_head anchor_list; /* the URB may be anchored by the driver */ + struct usb_anchor *anchor; struct usb_device *dev; /* (in) pointer to associated device */ unsigned int pipe; /* (in) pipe information */ int status; /* (return) non-ISO status */ @@ -1270,6 +1287,10 @@ extern struct urb *usb_get_urb(struct ur extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_unlink_urb(struct urb *urb); extern void usb_kill_urb(struct urb *urb); +extern void usb_kill_anchored_urbs(struct usb_anchor *anchor); +extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor); +extern void usb_unanchor_urb(struct urb *urb); +extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout); void *usb_buffer_alloc (struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); --- a/drivers/usb/core/hcd.c 2007-05-04 12:04:29.000000000 +0200 +++ b/drivers/usb/core/hcd.c 2007-05-07 15:19:01.000000000 +0200 @@ -1408,6 +1408,8 @@ void usb_hcd_giveback_urb (struct usb_hc } usbmon_urb_complete (&hcd->self, urb); + usb_unanchor_urb(urb); + /* pass ownership to the completion handler */ urb->complete (urb); atomic_dec (&urb->use_count); --- a/drivers/usb/core/urb.c 2007-05-04 10:53:14.000000000 +0200 +++ b/drivers/usb/core/urb.c 2007-05-16 11:32:15.000000000 +0200 @@ -4,6 +4,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/usb.h> +#include <linux/wait.h> #include "hcd.h" #define to_urb(d) container_of(d, struct urb, kref) @@ -11,6 +12,7 @@ static void urb_destroy(struct kref *kref) { struct urb *urb = to_urb(kref); + kfree(urb); } @@ -34,6 +36,7 @@ void usb_init_urb(struct urb *urb) memset(urb, 0, sizeof(*urb)); kref_init(&urb->kref); spin_lock_init(&urb->lock); + INIT_LIST_HEAD(&urb->anchor_list); } } @@ -100,8 +103,55 @@ struct urb * usb_get_urb(struct urb *urb kref_get(&urb->kref); return urb; } - - + +/** + * usb_anchor_urb - anchors an URB while it is processed + * @urb: pointer to the urb to anchor + * @anchor: pointer to the anchor + * + * This can be called to have access to URBs which are to be executed + * without bothering to track them + */ + +void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) +{ + unsigned long flags; + + spin_lock_irqsave(&anchor->lock, flags); + usb_get_urb(urb); + list_add_tail(&urb->anchor_list, &anchor->urb_list); + urb->anchor = anchor; + spin_unlock_irqrestore(&anchor->lock, flags); +} + +/** + * usb_unanchor_urb - unanchors an URB + * @urb: pointer to the urb to anchor + * + * Call this to stop the system keeping track of this URB + */ + +void usb_unanchor_urb(struct urb *urb) +{ + unsigned long flags; + struct usb_anchor *anchor; + + if (!urb) + return; + + anchor = urb->anchor; + if (!anchor) + return; + + spin_lock_irqsave(&anchor->lock, flags); + urb->anchor = NULL; + list_del(&urb->anchor_list); + spin_unlock_irqrestore(&anchor->lock, flags); + usb_put_urb(urb); + if (list_empty(&anchor->urb_list)) + wake_up(&anchor->wait); +} + /*-------------------------------------------------------------------*/ /** @@ -477,12 +527,56 @@ void usb_kill_urb(struct urb *urb) --urb->reject; spin_unlock_irq(&urb->lock); } +/** + * usb_kill_anchored_urbs - cancel transfer requests en masse + * @anchor: anchor the requests are bound to + * + * this allows all outstanding URBs to be killed starting + * from the back of the queue + */ + +void usb_kill_anchored_urbs(struct usb_anchor *anchor) +{ + struct urb *victim; + + spin_lock_irq(&anchor->lock); + while (!list_empty(&anchor->urb_list)) { + victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); + /* we must make sure the URB isn't freed before we kill it*/ + usb_get_urb(victim); + spin_unlock_irq(&anchor->lock); + /* this will unanchor the URB */ + usb_kill_urb(victim); + usb_put_urb(victim); + spin_lock_irq(&anchor->lock); + } + spin_unlock_irq(&anchor->lock); +} + +/** + * usb_wait_anchor_empty_timeout - wait for an anchor to be unused + * @anchor: the anchor you want to become unused + * @timeout: how long you are willing to wait in jiffies + * + * Call this is you want to be sure all an anchor's + * URBs have finished + */ + +int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, int timeout) +{ + int res; + + res = wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), timeout); + return res; +} EXPORT_SYMBOL(usb_init_urb); EXPORT_SYMBOL(usb_alloc_urb); EXPORT_SYMBOL(usb_free_urb); EXPORT_SYMBOL(usb_get_urb); +EXPORT_SYMBOL(usb_anchor_urb); EXPORT_SYMBOL(usb_submit_urb); EXPORT_SYMBOL(usb_unlink_urb); EXPORT_SYMBOL(usb_kill_urb); - +EXPORT_SYMBOL(usb_kill_anchored_urbs); +EXPORT_SYMBOL(usb_wait_anchor_empty_timeout); --- a/drivers/usb/usb-skeleton.c 2007-05-04 13:07:30.000000000 +0200 +++ b/drivers/usb/usb-skeleton.c 2007-05-16 11:18:22.000000000 +0200 @@ -54,16 +54,20 @@ struct usb_skel { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ + struct usb_anchor submitted; /* in case we need to retract our submissions */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + int errors; /* the last request tanked */ + spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) static struct usb_driver skel_driver; +static void skel_draw_down(struct usb_skel *dev); static void skel_delete(struct kref *kref) { @@ -102,18 +106,22 @@ static int skel_open(struct inode *inode /* increment our usage count for the device */ kref_get(&dev->kref); - /* now we can drop the lock */ + /* to guard against an ongoing reset */ + mutex_lock(&dev->io_mutex); + /* now we can drop the common lock */ mutex_unlock(&skel_open_lock); /* prevent the device from being autosuspended */ retval = usb_autopm_get_interface(interface); if (retval) { + mutex_unlock(&dev->io_mutex); kref_put(&dev->kref, skel_delete); goto exit; } /* save our object in the file's private structure */ file->private_data = dev; + mutex_unlock(&dev->io_mutex); exit: return retval; @@ -138,6 +146,29 @@ static int skel_release(struct inode *in return 0; } +static int skel_flush(struct file *file, fl_owner_t id) +{ + struct usb_skel *dev; + unsigned long flags; + int res; + + dev = (struct usb_skel *)file->private_data; + if (dev == NULL) + return -ENODEV; + + /* wait for io to stop */ + skel_draw_down(dev); + + /* read out errors, leave subsequent opens a clean slate */ + spin_lock_irqsave(&dev->err_lock, flags); + res = dev->errors ? -EIO : 0; + if (res) + dev->errors = 0; + spin_unlock_irqrestore(&dev->err_lock, flags); + + return res; +} + static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; @@ -179,14 +210,17 @@ static void skel_write_bulk_callback(str dev = (struct usb_skel *)urb->context; /* sync/async unlink faults aren't errors */ - if (urb->status && - !(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) { - err("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + err("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } + spin_lock(&dev->err_lock); + dev->errors = urb->status; + spin_unlock(&dev->err_lock); } - /* free up our allocated buffer */ usb_buffer_free(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); @@ -213,6 +247,15 @@ static ssize_t skel_write(struct file *f goto exit; } + spin_lock_irq(&dev->err_lock); + if (dev->errors < 0) { + dev->errors = 0; + retval = -EIO; + } + spin_unlock_irq(&dev->err_lock); + if (retval < 0) + goto error; + /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { @@ -244,13 +287,14 @@ static ssize_t skel_write(struct file *f usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->submitted); /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); mutex_unlock(&dev->io_mutex); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); - goto error; + goto error_unanchor; } /* release our reference to this urb, the USB core will eventually free it entirely */ @@ -259,6 +303,8 @@ static ssize_t skel_write(struct file *f return writesize; +error_unanchor: + usb_unanchor_urb(urb); error: if (urb) { usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); @@ -276,6 +322,7 @@ static const struct file_operations skel .write = skel_write, .open = skel_open, .release = skel_release, + .flush = skel_flush, }; /* @@ -306,6 +353,8 @@ static int skel_probe(struct usb_interfa kref_init(&dev->kref); sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); mutex_init(&dev->io_mutex); + spin_lock_init(&dev->err_lock); + init_usb_anchor(&dev->submitted); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -383,7 +432,7 @@ static void skel_disconnect(struct usb_i dev->interface = NULL; mutex_unlock(&dev->io_mutex); - + usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ kref_put(&dev->kref, skel_delete); @@ -391,10 +440,60 @@ static void skel_disconnect(struct usb_i info("USB Skeleton #%d now disconnected", minor); } +static void __skel_draw_down(struct usb_skel *dev) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1 * HZ); + if (!time) + usb_kill_anchored_urbs(&dev->submitted); +} + +static void skel_draw_down(struct usb_skel *dev) +{ + mutex_lock(&dev->io_mutex); + __skel_draw_down(dev); + mutex_unlock(&dev->io_mutex); +} + +static int skel_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + __skel_draw_down(dev); + return 0; +} + +static void skel_pre_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->io_mutex); + __skel_draw_down(dev); +} + +static int skel_resume (struct usb_interface *intf) +{ + return 0; +} + +static void skel_post_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + /* we are sure no URBs are active - no locking needed */ + dev->errors = -EPIPE; + mutex_unlock(&dev->io_mutex); +} + static struct usb_driver skel_driver = { .name = "skeleton", .probe = skel_probe, .disconnect = skel_disconnect, + .suspend = skel_suspend, + .resume = skel_resume, + .pre_reset = skel_pre_reset, + .post_reset = skel_post_reset, .id_table = skel_table, .supports_autosuspend = 1, }; ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel