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