Am Freitag, 4. Mai 2007 06:47 schrieb Greg KH:
> On Thu, May 03, 2007 at 11:05:54AM -0400, Alan Stern wrote:
> > On Thu, 3 May 2007, Oliver Neukum wrote:
> >
> > > Am Donnerstag, 3. Mai 2007 16:03 schrieb Alan Stern:
> > > > On Thu, 3 May 2007, Oliver Neukum wrote:
> > > >
> > > > > > > How about exporting usb_disable_interface() and
> > > > > > > usb_enable_interface()?
> > > > > > > It doesn't solve the problem for endpoint 0, but this case is
> > > > > > > rare.
> > > > > >
> > > > > > We could; it would save drivers the trouble of keeping track of
> > > > > > their own
> > > > > > URBs. Do you have a particular driver in mind for this?
> > > > >
> > > > > Everything derived from newer versions of the skeleton driver.
> > > >
> > > > Would we be better off modifying the skeleton driver to keep track of
> > > > its
> > > > own URBs?
> > >
> > > I am unsure. The "fire-and-forget" technique is certainly simple for
> > > driver writers. Perhaps we should make it easier to keep track of URBs.
> > > A list_head in the URB?
> >
> > People have discussed adding one in the past. Greg might have some
> > opinions.
>
> My opinion is that we need to ensure that we can still do the
> "fire-and-forget" type driver :)
And this can be done. What do you think:
--- a/include/linux/usb.h 2007-05-04 10:50:18.000000000 +0200
+++ b/include/linux/usb.h 2007-05-04 14:10:49.000000000 +0200
@@ -958,11 +958,23 @@ struct usb_iso_packet_descriptor {
struct urb;
+struct usb_anchor {
+ struct list_head urb_list;
+ spinlock_t lock;
+};
+
+static inline void init_usb_anchor(struct usb_anchor *anchor)
+{
+ INIT_LIST_HEAD(&anchor->urb_list);
+ 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: 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 +1147,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 +1284,8 @@ 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_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a);
+extern void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor
*new);
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-04 12:16:28.000000000 +0200
@@ -1408,6 +1408,15 @@ void usb_hcd_giveback_urb (struct usb_hc
}
usbmon_urb_complete (&hcd->self, urb);
+ if (urb->anchor) {
+ struct usb_anchor *anchor = urb->anchor;
+
+ spin_lock(&anchor->lock);
+ list_del(&urb->anchor_list);
+ urb->anchor = NULL;
+ spin_unlock(&anchor->lock);
+ }
+
/* 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-04 14:28:29.000000000 +0200
@@ -11,6 +11,14 @@
static void urb_destroy(struct kref *kref)
{
struct urb *urb = to_urb(kref);
+ unsigned long flags;
+
+ if (urb->anchor) {
+ spin_lock_irqsave(&urb->anchor->lock, flags);
+ list_del(&urb->anchor_list);
+ spin_unlock_irqrestore(&urb->anchor->lock, flags);
+ }
+
kfree(urb);
}
@@ -34,6 +42,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,6 +109,25 @@ struct urb * usb_get_urb(struct urb *urb
kref_get(&urb->kref);
return urb;
}
+
+/**
+ * usb_associate_urb_to_anchor - anchors an URB while it is processed
+ * @u: pointer to the urb to anchor
+ * @a: 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_associate_urb_to_anchor(struct urb *u, struct usb_anchor *a)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&a->lock, flags);
+ list_add_tail(&u->anchor_list, &a->urb_list);
+ u->anchor = a;
+ spin_unlock_irqrestore(&a->lock, flags);
+}
/*-------------------------------------------------------------------*/
@@ -477,12 +505,45 @@ void usb_kill_urb(struct urb *urb)
--urb->reject;
spin_unlock_irq(&urb->lock);
}
+/**
+ * usb_kill_associated_urbs - cancel transfer requests en masse
+ * @old: anchor the requests are bound to
+ * @new: anchor the request which were canceled shall be bound to, may be NULL
+ *
+ * this allows all outstanding URBs to be cancelled and bound to an anchor
+ * for later processing
+ */
+
+void usb_kill_associated_urbs(struct usb_anchor *old, struct usb_anchor *new)
+{
+ unsigned long flags;
+ struct urb *victim;
+
+ spin_lock_irqsave(&old->lock, flags);
+ while (!list_empty(&old->urb_list)) {
+ victim = list_entry(old->urb_list.prev, struct urb,
anchor_list);
+ list_del(&victim->anchor_list);
+ /* we must make sure the URB isn't freed before we kill it*/
+ usb_get_urb(victim);
+ spin_unlock_irqrestore(&old->lock, flags);
+ usb_kill_urb(victim);
+ /* we may transfer only if we killed */
+ if (new && victim->status == -ENOENT)
+ list_add(&victim->anchor_list, &new->urb_list);
+ else
+ usb_put_urb(victim);
+ spin_lock_irqsave(&old->lock, flags);
+ }
+ spin_unlock_irqrestore(&old->lock, flags);
+}
EXPORT_SYMBOL(usb_init_urb);
EXPORT_SYMBOL(usb_alloc_urb);
EXPORT_SYMBOL(usb_free_urb);
EXPORT_SYMBOL(usb_get_urb);
+EXPORT_SYMBOL(usb_associate_urb_to_anchor);
EXPORT_SYMBOL(usb_submit_urb);
EXPORT_SYMBOL(usb_unlink_urb);
EXPORT_SYMBOL(usb_kill_urb);
+EXPORT_SYMBOL(usb_kill_associated_urbs);
--- a/drivers/usb/usb-skeleton.c 2007-05-04 13:07:30.000000000 +0200
+++ b/drivers/usb/usb-skeleton.c 2007-05-04 14:11:50.000000000 +0200
@@ -54,10 +54,13 @@ 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 */
+ struct usb_anchor redo; /* to be redone in case
of suspend or reset */
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 */
+ unsigned char revoking; /* this is set while
submissions are revoked */
struct kref kref;
struct mutex io_mutex; /* synchronize I/O with
disconnect */
};
@@ -185,11 +188,17 @@ static void skel_write_bulk_callback(str
urb->status == -ESHUTDOWN)) {
err("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
+ /* the device is to be suspend,
+ this urb must be redone,
+ the buffer cannot be freed */
+ if (urb->status == -ENOENT && dev->revoking)
+ goto spare_buffer;
}
/* free up our allocated buffer */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
+spare_buffer:
up(&dev->limit_sem);
}
@@ -244,6 +253,7 @@ 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_associate_urb_to_anchor(urb, &dev->submitted);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
@@ -306,6 +316,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);
+ init_usb_anchor(&dev->submitted);
+ init_usb_anchor(&dev->redo);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
@@ -391,10 +403,57 @@ static void skel_disconnect(struct usb_i
info("USB Skeleton #%d now disconnected", minor);
}
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_skel *dev= usb_get_intfdata(intf);
+
+ mutex_lock(&dev->io_mutex);
+ dev->revoking = 1;
+ usb_kill_associated_urbs(&dev->submitted, &dev->redo);
+ mutex_unlock(&dev->io_mutex);
+
+ return 0;
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+ struct usb_skel *dev= usb_get_intfdata(intf);
+ struct urb *lazarus, *n;
+ int r;
+
+ mutex_lock(&dev->io_mutex);
+ dev->revoking = 0;
+ list_for_each_entry_safe(lazarus, n, &dev->redo.urb_list, anchor_list) {
+ list_del(&lazarus->anchor_list);
+ usb_associate_urb_to_anchor(lazarus, &dev->submitted);
+ r = usb_submit_urb(lazarus, GFP_NOIO);
+ if (r < 0) {
+ /* if URB submission fails, it, its buffer and those
+ after it and their buffers must be freed */
+ err("Could not redo URBs during resume");
+ usb_buffer_free(lazarus->dev,
lazarus->transfer_buffer_length,
+ lazarus->transfer_buffer, lazarus->transfer_dma);
+ usb_free_urb(lazarus);
+ list_for_each_entry_safe(lazarus, n,
&dev->redo.urb_list, anchor_list) {
+ usb_buffer_free(lazarus->dev,
lazarus->transfer_buffer_length,
+ lazarus->transfer_buffer,
lazarus->transfer_dma);
+ usb_free_urb(lazarus);
+ }
+ mutex_unlock(&dev->io_mutex);
+ return r;
+ }
+ usb_free_urb(lazarus);
+ }
+ mutex_unlock(&dev->io_mutex);
+ return 0;
+}
+
static struct usb_driver skel_driver = {
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
+ .suspend = skel_suspend,
+ .resume = skel_resume,
.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