diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-24 21:22:33 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-24 21:22:33 +0100 |
commit | baea7b946f00a291b166ccae7fcfed6c01530cc6 (patch) | |
tree | 4aa275fbdbec9c7b9b4629e8bee2bbecd3c6a6af /drivers/usb/core/devio.c | |
parent | ae19ffbadc1b2100285a5b5b3d0a4e0a11390904 (diff) | |
parent | 94e0fb086fc5663c38bbc0fe86d698be8314f82f (diff) |
Merge branch 'origin' into for-linus
Conflicts:
MAINTAINERS
Diffstat (limited to 'drivers/usb/core/devio.c')
-rw-r--r-- | drivers/usb/core/devio.c | 247 |
1 files changed, 176 insertions, 71 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 4247eccf858..181f78c8410 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -52,6 +52,7 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +#include "hub.h" #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 @@ -73,6 +74,7 @@ struct dev_state { void __user *disccontext; unsigned long ifclaimed; u32 secid; + u32 disabled_bulk_eps; }; struct async { @@ -87,6 +89,8 @@ struct async { struct urb *urb; int status; u32 secid; + u8 bulk_addr; + u8 bulk_status; }; static int usbfs_snoop; @@ -99,11 +103,15 @@ MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic"); dev_info(dev , format , ## arg); \ } while (0) -#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) +enum snoop_when { + SUBMIT, COMPLETE +}; +#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) #define MAX_USBFS_BUFFER_SIZE 16384 + static int connected(struct dev_state *ps) { return (!list_empty(&ps->list) && @@ -300,24 +308,79 @@ static struct async *async_getpending(struct dev_state *ps, return NULL; } -static void snoop_urb(struct urb *urb, void __user *userurb) +static void snoop_urb(struct usb_device *udev, + void __user *userurb, int pipe, unsigned length, + int timeout_or_status, enum snoop_when when) { - unsigned j; - unsigned char *data = urb->transfer_buffer; + static const char *types[] = {"isoc", "int", "ctrl", "bulk"}; + static const char *dirs[] = {"out", "in"}; + int ep; + const char *t, *d; if (!usbfs_snoop) return; - dev_info(&urb->dev->dev, "direction=%s\n", - usb_urb_dir_in(urb) ? "IN" : "OUT"); - dev_info(&urb->dev->dev, "userurb=%p\n", userurb); - dev_info(&urb->dev->dev, "transfer_buffer_length=%u\n", - urb->transfer_buffer_length); - dev_info(&urb->dev->dev, "actual_length=%u\n", urb->actual_length); - dev_info(&urb->dev->dev, "data: "); - for (j = 0; j < urb->transfer_buffer_length; ++j) - printk("%02x ", data[j]); - printk("\n"); + ep = usb_pipeendpoint(pipe); + t = types[usb_pipetype(pipe)]; + d = dirs[!!usb_pipein(pipe)]; + + if (userurb) { /* Async */ + if (when == SUBMIT) + dev_info(&udev->dev, "userurb %p, ep%d %s-%s, " + "length %u\n", + userurb, ep, t, d, length); + else + dev_info(&udev->dev, "userurb %p, ep%d %s-%s, " + "actual_length %u status %d\n", + userurb, ep, t, d, length, + timeout_or_status); + } else { + if (when == SUBMIT) + dev_info(&udev->dev, "ep%d %s-%s, length %u, " + "timeout %d\n", + ep, t, d, length, timeout_or_status); + else + dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, " + "status %d\n", + ep, t, d, length, timeout_or_status); + } +} + +#define AS_CONTINUATION 1 +#define AS_UNLINK 2 + +static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr) +__releases(ps->lock) +__acquires(ps->lock) +{ + struct async *as; + + /* Mark all the pending URBs that match bulk_addr, up to but not + * including the first one without AS_CONTINUATION. If such an + * URB is encountered then a new transfer has already started so + * the endpoint doesn't need to be disabled; otherwise it does. + */ + list_for_each_entry(as, &ps->async_pending, asynclist) { + if (as->bulk_addr == bulk_addr) { + if (as->bulk_status != AS_CONTINUATION) + goto rescan; + as->bulk_status = AS_UNLINK; + as->bulk_addr = 0; + } + } + ps->disabled_bulk_eps |= (1 << bulk_addr); + + /* Now carefully unlink all the marked pending URBs */ + rescan: + list_for_each_entry(as, &ps->async_pending, asynclist) { + if (as->bulk_status == AS_UNLINK) { + as->bulk_status = 0; /* Only once */ + spin_unlock(&ps->lock); /* Allow completions */ + usb_unlink_urb(as->urb); + spin_lock(&ps->lock); + goto rescan; + } + } } static void async_completed(struct urb *urb) @@ -346,7 +409,11 @@ static void async_completed(struct urb *urb) secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); - snoop_urb(urb, as->userurb); + snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, + as->status, COMPLETE); + if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && + as->status != -ENOENT) + cancel_bulk_urbs(ps, as->bulk_addr); spin_unlock(&ps->lock); if (signr) @@ -655,6 +722,7 @@ static int usbdev_release(struct inode *inode, struct file *file) struct async *as; usb_lock_device(dev); + usb_hub_release_all_ports(dev, ps); /* Protect against simultaneous open */ mutex_lock(&usbfs_mutex); @@ -688,7 +756,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) unsigned int tmo; unsigned char *tbuf; unsigned wLength; - int i, j, ret; + int i, pipe, ret; if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; @@ -708,24 +776,17 @@ static int proc_control(struct dev_state *ps, void __user *arg) free_page((unsigned long)tbuf); return -EINVAL; } - snoop(&dev->dev, "control read: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, - ctrl.wIndex, ctrl.wLength); + pipe = usb_rcvctrlpipe(dev, 0); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); usb_unlock_device(dev); - i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest, + i = usb_control_msg(dev, pipe, ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); + if ((i > 0) && ctrl.wLength) { - if (usbfs_snoop) { - dev_info(&dev->dev, "control read: data "); - for (j = 0; j < i; ++j) - printk("%02x ", (u8)(tbuf)[j]); - printk("\n"); - } if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); return -EFAULT; @@ -738,22 +799,15 @@ static int proc_control(struct dev_state *ps, void __user *arg) return -EFAULT; } } - snoop(&dev->dev, "control write: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, - ctrl.wIndex, ctrl.wLength); - if (usbfs_snoop) { - dev_info(&dev->dev, "control write: data: "); - for (j = 0; j < ctrl.wLength; ++j) - printk("%02x ", (unsigned char)(tbuf)[j]); - printk("\n"); - } + pipe = usb_sndctrlpipe(dev, 0); + snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest, ctrl.bRequestType, ctrl.wValue, ctrl.wIndex, tbuf, ctrl.wLength, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE); } free_page((unsigned long)tbuf); if (i < 0 && i != -EPIPE) { @@ -772,7 +826,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) unsigned int tmo, len1, pipe; int len2; unsigned char *tbuf; - int i, j, ret; + int i, ret; if (copy_from_user(&bulk, arg, sizeof(bulk))) return -EFAULT; @@ -799,18 +853,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) kfree(tbuf); return -EINVAL; } - snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n", - bulk.len, bulk.timeout); + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); + if (!i && len2) { - if (usbfs_snoop) { - dev_info(&dev->dev, "bulk read: data "); - for (j = 0; j < len2; ++j) - printk("%02x ", (u8)(tbuf)[j]); - printk("\n"); - } if (copy_to_user(bulk.data, tbuf, len2)) { kfree(tbuf); return -EFAULT; @@ -823,17 +873,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) return -EFAULT; } } - snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n", - bulk.len, bulk.timeout); - if (usbfs_snoop) { - dev_info(&dev->dev, "bulk write: data: "); - for (j = 0; j < len1; ++j) - printk("%02x ", (unsigned char)(tbuf)[j]); - printk("\n"); - } + snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT); + usb_unlock_device(dev); i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); usb_lock_device(dev); + snoop_urb(dev, NULL, pipe, len2, i, COMPLETE); } kfree(tbuf); if (i < 0) @@ -991,6 +1036,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP | USBDEVFS_URB_SHORT_NOT_OK | + USBDEVFS_URB_BULK_CONTINUATION | USBDEVFS_URB_NO_FSBR | USBDEVFS_URB_ZERO_PACKET | USBDEVFS_URB_NO_INTERRUPT)) @@ -1051,13 +1097,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, is_in = 0; uurb->endpoint &= ~USB_DIR_IN; } - snoop(&ps->dev->dev, "control urb: bRequest=%02x " - "bRrequestType=%02x wValue=%04x " - "wIndex=%04x wLength=%04x\n", - dr->bRequest, dr->bRequestType, - __le16_to_cpup(&dr->wValue), - __le16_to_cpup(&dr->wIndex), - __le16_to_cpup(&dr->wLength)); break; case USBDEVFS_URB_TYPE_BULK: @@ -1070,7 +1109,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, uurb->number_of_packets = 0; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - snoop(&ps->dev->dev, "bulk urb\n"); break; case USBDEVFS_URB_TYPE_ISO: @@ -1097,12 +1135,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, } totlen += isopkt[u].length; } - if (totlen > 32768) { + /* 3072 * 64 microframes */ + if (totlen > 196608) { kfree(isopkt); return -EINVAL; } uurb->buffer_length = totlen; - snoop(&ps->dev->dev, "iso urb\n"); break; case USBDEVFS_URB_TYPE_INTERRUPT: @@ -1111,7 +1149,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - snoop(&ps->dev->dev, "interrupt urb\n"); break; default: @@ -1198,11 +1235,46 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EFAULT; } } - snoop_urb(as->urb, as->userurb); + snoop_urb(ps->dev, as->userurb, as->urb->pipe, + as->urb->transfer_buffer_length, 0, SUBMIT); async_newpending(as); - if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) { + + if (usb_endpoint_xfer_bulk(&ep->desc)) { + spin_lock_irq(&ps->lock); + + /* Not exactly the endpoint address; the direction bit is + * shifted to the 0x10 position so that the value will be + * between 0 and 31. + */ + as->bulk_addr = usb_endpoint_num(&ep->desc) | + ((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) + >> 3); + + /* If this bulk URB is the start of a new transfer, re-enable + * the endpoint. Otherwise mark it as a continuation URB. + */ + if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION) + as->bulk_status = AS_CONTINUATION; + else + ps->disabled_bulk_eps &= ~(1 << as->bulk_addr); + + /* Don't accept continuation URBs if the endpoint is + * disabled because of an earlier error. + */ + if (ps->disabled_bulk_eps & (1 << as->bulk_addr)) + ret = -EREMOTEIO; + else + ret = usb_submit_urb(as->urb, GFP_ATOMIC); + spin_unlock_irq(&ps->lock); + } else { + ret = usb_submit_urb(as->urb, GFP_KERNEL); + } + + if (ret) { dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret); + snoop_urb(ps->dev, as->userurb, as->urb->pipe, + 0, ret, COMPLETE); async_removepending(as); free_async(as); return ret; @@ -1548,6 +1620,29 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg) } #endif +static int proc_claim_port(struct dev_state *ps, void __user *arg) +{ + unsigned portnum; + int rc; + + if (get_user(portnum, (unsigned __user *) arg)) + return -EFAULT; + rc = usb_hub_claim_port(ps->dev, portnum, ps); + if (rc == 0) + snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n", + portnum, task_pid_nr(current), current->comm); + return rc; +} + +static int proc_release_port(struct dev_state *ps, void __user *arg) +{ + unsigned portnum; + + if (get_user(portnum, (unsigned __user *) arg)) + return -EFAULT; + return usb_hub_release_port(ps->dev, portnum, ps); +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -1645,7 +1740,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; case USBDEVFS_REAPURBNDELAY32: - snoop(&dev->dev, "%s: REAPURBDELAY32\n", __func__); + snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__); ret = proc_reapurbnonblock_compat(ps, p); break; @@ -1666,7 +1761,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, break; case USBDEVFS_REAPURBNDELAY: - snoop(&dev->dev, "%s: REAPURBDELAY\n", __func__); + snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__); ret = proc_reapurbnonblock(ps, p); break; @@ -1689,6 +1784,16 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, snoop(&dev->dev, "%s: IOCTL\n", __func__); ret = proc_ioctl_default(ps, p); break; + + case USBDEVFS_CLAIM_PORT: + snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__); + ret = proc_claim_port(ps, p); + break; + + case USBDEVFS_RELEASE_PORT: + snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__); + ret = proc_release_port(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) |