diff options
Diffstat (limited to 'drivers/usb/core/message.c')
| -rw-r--r-- | drivers/usb/core/message.c | 157 |
1 files changed, 95 insertions, 62 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 530e854961ce..c021af390372 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,9 +18,17 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +struct api_context { + struct completion done; + int status; +}; + static void usb_api_blocking_completion(struct urb *urb) { - complete((struct completion *)urb->context); + struct api_context *ctx = urb->context; + + ctx->status = urb->status; + complete(&ctx->done); } @@ -32,38 +40,37 @@ static void usb_api_blocking_completion(struct urb *urb) */ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) { - struct completion done; + struct api_context ctx; unsigned long expire; - int status; + int retval; - init_completion(&done); - urb->context = &done; + init_completion(&ctx.done); + urb->context = &ctx; urb->actual_length = 0; - status = usb_submit_urb(urb, GFP_NOIO); - if (unlikely(status)) + retval = usb_submit_urb(urb, GFP_NOIO); + if (unlikely(retval)) goto out; expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; - if (!wait_for_completion_timeout(&done, expire)) { + if (!wait_for_completion_timeout(&ctx.done, expire)) { + usb_kill_urb(urb); + retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status); dev_dbg(&urb->dev->dev, "%s timed out on ep%d%s len=%d/%d\n", current->comm, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", + usb_endpoint_num(&urb->ep->desc), + usb_urb_dir_in(urb) ? "in" : "out", urb->actual_length, urb->transfer_buffer_length); - - usb_kill_urb(urb); - status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status; } else - status = urb->status; + retval = ctx.status; out: if (actual_length) *actual_length = urb->actual_length; usb_free_urb(urb); - return status; + return retval; } /*-------------------------------------------------------------------*/ @@ -243,13 +250,15 @@ static void sg_clean (struct usb_sg_request *io) io->urbs = NULL; } if (io->dev->dev.dma_mask != NULL) - usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents); + usb_buffer_unmap_sg (io->dev, usb_pipein(io->pipe), + io->sg, io->nents); io->dev = NULL; } static void sg_complete (struct urb *urb) { struct usb_sg_request *io = urb->context; + int status = urb->status; spin_lock (&io->lock); @@ -265,21 +274,21 @@ static void sg_complete (struct urb *urb) */ if (io->status && (io->status != -ECONNRESET - || urb->status != -ECONNRESET) + || status != -ECONNRESET) && urb->actual_length) { dev_err (io->dev->bus->controller, "dev %s ep%d%s scatterlist error %d/%d\n", io->dev->devpath, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - urb->status, io->status); + usb_endpoint_num(&urb->ep->desc), + usb_urb_dir_in(urb) ? "in" : "out", + status, io->status); // BUG (); } - if (io->status == 0 && urb->status && urb->status != -ECONNRESET) { - int i, found, status; + if (io->status == 0 && status && status != -ECONNRESET) { + int i, found, retval; - io->status = urb->status; + io->status = status; /* the previous urbs, and this one, completed already. * unlink pending urbs so they won't rx/tx bad data. @@ -290,13 +299,13 @@ static void sg_complete (struct urb *urb) if (!io->urbs [i] || !io->urbs [i]->dev) continue; if (found) { - status = usb_unlink_urb (io->urbs [i]); - if (status != -EINPROGRESS - && status != -ENODEV - && status != -EBUSY) + retval = usb_unlink_urb (io->urbs [i]); + if (retval != -EINPROGRESS && + retval != -ENODEV && + retval != -EBUSY) dev_err (&io->dev->dev, "%s, unlink --> %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } else if (urb == io->urbs [i]) found = 1; } @@ -371,7 +380,8 @@ int usb_sg_init ( */ dma = (dev->dev.dma_mask != NULL); if (dma) - io->entries = usb_buffer_map_sg (dev, pipe, sg, nents); + io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe), + sg, nents); else io->entries = nents; @@ -409,15 +419,22 @@ int usb_sg_init ( * Some systems need to revert to PIO when DMA is temporarily * unavailable. For their sakes, both transfer_buffer and * transfer_dma are set when possible. However this can only - * work on systems without HIGHMEM, since DMA buffers located - * in high memory are not directly addressable by the CPU for - * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL + * work on systems without: + * + * - HIGHMEM, since DMA buffers located in high memory are + * not directly addressable by the CPU for PIO; + * + * - IOMMU, since dma_map_sg() is allowed to use an IOMMU to + * make virtually discontiguous buffers be "dma-contiguous" + * so that PIO and DMA need diferent numbers of URBs. + * + * So when HIGHMEM or IOMMU are in use, transfer_buffer is NULL * to prevent stale pointers and to help spot bugs. */ if (dma) { io->urbs [i]->transfer_dma = sg_dma_address (sg + i); len = sg_dma_len (sg + i); -#ifdef CONFIG_HIGHMEM +#if defined(CONFIG_HIGHMEM) || defined(CONFIG_IOMMU) io->urbs[i]->transfer_buffer = NULL; #else io->urbs[i]->transfer_buffer = @@ -622,12 +639,12 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char memset(buf,0,size); // Make sure we parse really received data for (i = 0; i < 3; ++i) { - /* retry on length 0 or stall; some devices are flakey */ + /* retry on length 0 or error; some devices are flakey */ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (type << 8) + index, 0, buf, size, USB_CTRL_GET_TIMEOUT); - if (result == 0 || result == -EPIPE) + if (result <= 0 && result != -ETIMEDOUT) continue; if (result > 1 && ((u8 *)buf)[1] != type) { result = -EPROTO; @@ -998,8 +1015,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) ep = dev->ep_in[epnum]; dev->ep_in[epnum] = NULL; } - if (ep && dev->bus) - usb_hcd_endpoint_disable(dev, ep); + if (ep) { + ep->enabled = 0; + usb_hcd_flush_endpoint(dev, ep); + usb_hcd_disable_endpoint(dev, ep); + } } /** @@ -1081,23 +1101,21 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. * For control endpoints, both the input and output sides are handled. */ -static void -usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) +void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) { - unsigned int epaddr = ep->desc.bEndpointAddress; - unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; - int is_control; + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + int is_control = usb_endpoint_xfer_control(&ep->desc); - is_control = ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_CONTROL); - if (usb_endpoint_out(epaddr) || is_control) { + if (is_out || is_control) { usb_settoggle(dev, epnum, 1, 0); dev->ep_out[epnum] = ep; } - if (!usb_endpoint_out(epaddr) || is_control) { + if (!is_out || is_control) { usb_settoggle(dev, epnum, 0, 0); dev->ep_in[epnum] = ep; } + ep->enabled = 1; } /* @@ -1156,6 +1174,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) struct usb_host_interface *alt; int ret; int manual = 0; + int changed; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; @@ -1195,7 +1214,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) */ /* prevent submissions using previous endpoint settings */ - if (device_is_registered(&iface->dev)) + changed = (iface->cur_altsetting != alt); + if (changed && device_is_registered(&iface->dev)) usb_remove_sysfs_intf_files(iface); usb_disable_interface(dev, iface); @@ -1232,7 +1252,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * (Likewise, EP0 never "halts" on well designed devices.) */ usb_enable_interface(dev, iface); - if (device_is_registered(&iface->dev)) + if (changed && device_is_registered(&iface->dev)) usb_create_sysfs_intf_files(iface); return 0; @@ -1313,7 +1333,7 @@ int usb_reset_configuration(struct usb_device *dev) return 0; } -void usb_release_interface(struct device *dev) +static void usb_release_interface(struct device *dev) { struct usb_interface *intf = to_usb_interface(dev); struct usb_interface_cache *intfc = @@ -1324,14 +1344,11 @@ void usb_release_interface(struct device *dev) } #ifdef CONFIG_HOTPLUG -static int usb_if_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) +static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) { struct usb_device *usb_dev; struct usb_interface *intf; struct usb_host_interface *alt; - int i = 0; - int length = 0; if (!dev) return -ENODEV; @@ -1343,16 +1360,31 @@ static int usb_if_uevent(struct device *dev, char **envp, int num_envp, usb_dev = interface_to_usbdev(intf); alt = intf->cur_altsetting; - if (add_uevent_var(envp, num_envp, &i, - buffer, buffer_size, &length, - "INTERFACE=%d/%d/%d", +#ifdef CONFIG_USB_DEVICEFS + if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum)) + return -ENOMEM; +#endif + + if (add_uevent_var(env, "PRODUCT=%x/%x/%x", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice))) + return -ENOMEM; + + if (add_uevent_var(env, "TYPE=%d/%d/%d", + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol)) + return -ENOMEM; + + if (add_uevent_var(env, "INTERFACE=%d/%d/%d", alt->desc.bInterfaceClass, alt->desc.bInterfaceSubClass, alt->desc.bInterfaceProtocol)) return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, - buffer, buffer_size, &length, + if (add_uevent_var(env, "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", le16_to_cpu(usb_dev->descriptor.idVendor), le16_to_cpu(usb_dev->descriptor.idProduct), @@ -1365,14 +1397,12 @@ static int usb_if_uevent(struct device *dev, char **envp, int num_envp, alt->desc.bInterfaceProtocol)) return -ENOMEM; - envp[i] = NULL; return 0; } #else -static int usb_if_uevent(struct device *dev, char **envp, - int num_envp, char *buffer, int buffer_size) +static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) { return -ENODEV; } @@ -1442,6 +1472,9 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, * channels are available independently; and choosing between open * standard device protocols (like CDC) or proprietary ones. * + * Note that a non-authorized device (dev->authorized == 0) will only + * be put in unconfigured mode. + * * Note that USB has an additional level of device configurability, * associated with interfaces. That configurability is accessed using * usb_set_interface(). @@ -1463,7 +1496,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - if (configuration == -1) + if (dev->authorized == 0 || configuration == -1) configuration = 0; else { for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { |

