X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Furb.c;h=9801d08edacf8db26056175116d28df45af985ed;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=d5a75b1aca522d39f22b985ab17977137c1c1d18;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index d5a75b1ac..9801d08ed 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -1,18 +1,19 @@ -#include #include #include #include #include #include - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif #include #include "hcd.h" +#define to_urb(d) container_of(d, struct urb, kref) + +static void urb_destroy(struct kref *kref) +{ + struct urb *urb = to_urb(kref); + kfree(urb); +} + /** * usb_init_urb - initializes a urb so that it can be used by a USB driver * @urb: pointer to the urb to initialize @@ -31,7 +32,7 @@ void usb_init_urb(struct urb *urb) { if (urb) { memset(urb, 0, sizeof(*urb)); - urb->count = (atomic_t)ATOMIC_INIT(1); + kref_init(&urb->kref); spin_lock_init(&urb->lock); } } @@ -52,11 +53,11 @@ void usb_init_urb(struct urb *urb) * * The driver must call usb_free_urb() when it is finished with the urb. */ -struct urb *usb_alloc_urb(int iso_packets, int mem_flags) +struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) { struct urb *urb; - urb = (struct urb *)kmalloc(sizeof(struct urb) + + urb = kmalloc(sizeof(struct urb) + iso_packets * sizeof(struct usb_iso_packet_descriptor), mem_flags); if (!urb) { @@ -69,7 +70,7 @@ struct urb *usb_alloc_urb(int iso_packets, int mem_flags) /** * usb_free_urb - frees the memory used by a urb when all users of it are finished - * @urb: pointer to the urb to free + * @urb: pointer to the urb to free, may be NULL * * Must be called when a user of a urb is finished with it. When the last user * of the urb calls this function, the memory of the urb is freed. @@ -80,13 +81,12 @@ struct urb *usb_alloc_urb(int iso_packets, int mem_flags) void usb_free_urb(struct urb *urb) { if (urb) - if (atomic_dec_and_test(&urb->count)) - kfree(urb); + kref_put(&urb->kref, urb_destroy); } /** * usb_get_urb - increments the reference count of the urb - * @urb: pointer to the urb to modify + * @urb: pointer to the urb to modify, may be NULL * * This must be called whenever a urb is transferred from a device driver to a * host controller driver. This allows proper reference counting to happen @@ -96,11 +96,9 @@ void usb_free_urb(struct urb *urb) */ struct urb * usb_get_urb(struct urb *urb) { - if (urb) { - atomic_inc(&urb->count); - return urb; - } else - return NULL; + if (urb) + kref_get(&urb->kref); + return urb; } @@ -116,7 +114,7 @@ struct urb * usb_get_urb(struct urb *urb) * describing that request to the USB subsystem. Request completion will * be indicated later, asynchronously, by calling the completion handler. * The three types of completion are success, error, and unlink - * (a software-induced fault, also called "request cancelation"). + * (a software-induced fault, also called "request cancellation"). * * URBs may be submitted in interrupt context. * @@ -165,7 +163,7 @@ struct urb * usb_get_urb(struct urb *urb) * As of Linux 2.6, all USB endpoint transfer queues support depths greater * than one. This was previously a HCD-specific behavior, except for ISO * transfers. Non-isochronous endpoint queues are inactive during cleanup - * after faults (transfer errors or cancelation). + * after faults (transfer errors or cancellation). * * Reserved Bandwidth Transfers: * @@ -219,11 +217,10 @@ struct urb * usb_get_urb(struct urb *urb) * GFP_NOIO, unless b) or c) apply * */ -int usb_submit_urb(struct urb *urb, int mem_flags) +int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { int pipe, temp, max; struct usb_device *dev; - struct usb_operations *op; int is_out; if (!urb || urb->hcpriv || !urb->complete) @@ -232,8 +229,9 @@ int usb_submit_urb(struct urb *urb, int mem_flags) (dev->state < USB_STATE_DEFAULT) || (!dev->bus) || (dev->devnum <= 0)) return -ENODEV; - if (!(op = dev->bus->op) || !op->submit_urb) - return -ENODEV; + if (dev->bus->controller->power.power_state.event != PM_EVENT_ON + || dev->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; urb->status = -EINPROGRESS; urb->actual_length = 0; @@ -249,13 +247,6 @@ int usb_submit_urb(struct urb *urb, int mem_flags) if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED) return -ENODEV; - /* (actually HCDs may need to duplicate this, endpoint might yet - * stall due to queued bulk/intr transactions that complete after - * we check) - */ - if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out)) - return -EPIPE; - /* FIXME there should be a sharable lock protecting us against * config/altsetting changes and disconnects, kicking in here. * (here == before maxpacket, and eventually endpoint type, @@ -264,11 +255,10 @@ int usb_submit_urb(struct urb *urb, int mem_flags) max = usb_maxpacket (dev, pipe, is_out); if (max <= 0) { - dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)", - __FUNCTION__, - usb_pipeendpoint (pipe), is_out ? "OUT" : "IN", - dev->bus->bus_name, dev->devpath, - max); + dev_dbg(&dev->dev, + "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", + usb_pipeendpoint (pipe), is_out ? "out" : "in", + __FUNCTION__, max); return -EMSGSIZE; } @@ -310,9 +300,8 @@ int usb_submit_urb(struct urb *urb, int mem_flags) unsigned int allowed; /* enforce simple/standard policy */ - allowed = URB_ASYNC_UNLINK; // affects later unlinks - allowed |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - allowed |= URB_NO_INTERRUPT; + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | + URB_NO_INTERRUPT); switch (temp) { case PIPE_BULK: if (is_out) @@ -384,49 +373,47 @@ int usb_submit_urb(struct urb *urb, int mem_flags) urb->interval = temp; } - return op->submit_urb (urb, mem_flags); + return usb_hcd_submit_urb (urb, mem_flags); } /*-------------------------------------------------------------------*/ /** * usb_unlink_urb - abort/cancel a transfer request for an endpoint - * @urb: pointer to urb describing a previously submitted request + * @urb: pointer to urb describing a previously submitted request, + * may be NULL * * This routine cancels an in-progress request. URBs complete only * once per submission, and may be canceled only once per submission. - * Successful cancelation means the requests's completion handler will + * Successful cancellation means the requests's completion handler will * be called with a status code indicating that the request has been * canceled (rather than any other code) and will quickly be removed * from host controller data structures. * - * When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this - * request is synchronous. Success is indicated by returning zero, - * at which time the urb will have been unlinked and its completion - * handler will have been called with urb->status == -ENOENT. Failure is - * indicated by any other return value. - * - * The synchronous cancelation mode may not be used - * when unlinking an urb from an interrupt context, such as a bottom - * half or a completion handler; or when holding a spinlock; or in - * other cases when the caller can't schedule(). - * - * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this - * request is asynchronous. Success is indicated by returning -EINPROGRESS, - * at which time the urb will normally not have been unlinked. - * The completion function will see urb->status == -ECONNRESET. Failure - * is indicated by any other return value. + * This request is always asynchronous. + * Success is indicated by returning -EINPROGRESS, + * at which time the URB will normally have been unlinked but not yet + * given back to the device driver. When it is called, the completion + * function will see urb->status == -ECONNRESET. Failure is indicated + * by any other return value. Unlinking will fail when the URB is not + * currently "linked" (i.e., it was never submitted, or it was unlinked + * before, or the hardware is already finished with it), even if the + * completion handler has not yet run. * * Unlinking and Endpoint Queues: * - * Host Controller Driver (HCDs) place all the URBs for a particular + * Host Controller Drivers (HCDs) place all the URBs for a particular * endpoint in a queue. Normally the queue advances as the controller - * hardware processes each request. But when an URB terminates with any - * fault (such as an error, or being unlinked) its queue stops, at least - * until that URB's completion routine returns. It is guaranteed that - * the queue will not restart until all its unlinked URBs have been fully - * retired, with their completion routines run, even if that's not until - * some time after the original completion handler returns. + * hardware processes each request. But when an URB terminates with an + * error its queue stops, at least until that URB's completion routine + * returns. It is guaranteed that the queue will not restart until all + * its unlinked URBs have been fully retired, with their completion + * routines run, even if that's not until some time after the original + * completion handler returns. Normally the same behavior and guarantees + * apply when an URB terminates because it was unlinked; however if an + * URB is unlinked before the hardware has started to execute it, then + * its queue is not guaranteed to stop until all the preceding URBs have + * completed. * * This means that USB device drivers can safely build deep queues for * large or complex transfers, and clean them up reliably after any sort @@ -442,16 +429,54 @@ int usb_submit_urb(struct urb *urb, int mem_flags) * An unlinked URB may leave a gap in the stream of packets. It is undefined * whether such gaps can be filled in. * - * When control URBs terminates with an error, it is likely that the + * When a control URB terminates with an error, it is likely that the * status stage of the transfer will not take place, even if it is merely * a soft error resulting from a short-packet with URB_SHORT_NOT_OK set. */ int usb_unlink_urb(struct urb *urb) { - if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) - return urb->dev->bus->op->unlink_urb(urb); - else + if (!urb) + return -EINVAL; + if (!(urb->dev && urb->dev->bus)) return -ENODEV; + return usb_hcd_unlink_urb(urb, -ECONNRESET); +} + +/** + * usb_kill_urb - cancel a transfer request and wait for it to finish + * @urb: pointer to URB describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. It is guaranteed 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. If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * 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) +{ + might_sleep(); + if (!(urb && urb->dev && urb->dev->bus)) + return; + spin_lock_irq(&urb->lock); + ++urb->reject; + spin_unlock_irq(&urb->lock); + + usb_hcd_unlink_urb(urb, -ENOENT); + wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); + + spin_lock_irq(&urb->lock); + --urb->reject; + spin_unlock_irq(&urb->lock); } EXPORT_SYMBOL(usb_init_urb); @@ -460,4 +485,5 @@ 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);