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/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel