diff options
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
-rw-r--r-- | drivers/usb/misc/usbtest.c | 133 |
1 files changed, 127 insertions, 6 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 388cc128072..bb10846affc 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -104,7 +104,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) alt = intf->altsetting + tmp; /* take the first altsetting with in-bulk + out-bulk; - * ignore other endpoints and altsetttings. + * ignore other endpoints and altsettings. */ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) { struct usb_host_endpoint *e; @@ -268,9 +268,9 @@ static inline void simple_fill_buf(struct urb *urb) } } -static inline unsigned buffer_offset(void *buf) +static inline unsigned long buffer_offset(void *buf) { - return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1); + return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1); } static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb) @@ -329,7 +329,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) static void simple_free_urb(struct urb *urb) { - unsigned offset = buffer_offset(urb->transfer_buffer); + unsigned long offset = buffer_offset(urb->transfer_buffer); if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) usb_free_coherent( @@ -1030,6 +1030,8 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0); /* device descriptor size == 18 bytes */ len = udev->descriptor.bMaxPacketSize0; + if (udev->speed == USB_SPEED_SUPER) + len = 512; switch (len) { case 8: len = 24; @@ -1195,6 +1197,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len) /*-------------------------------------------------------------------------*/ +struct queued_ctx { + struct completion complete; + atomic_t pending; + unsigned num; + int status; + struct urb **urbs; +}; + +static void unlink_queued_callback(struct urb *urb) +{ + int status = urb->status; + struct queued_ctx *ctx = urb->context; + + if (ctx->status) + goto done; + if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) { + if (status == -ECONNRESET) + goto done; + /* What error should we report if the URB completed normally? */ + } + if (status != 0) + ctx->status = status; + + done: + if (atomic_dec_and_test(&ctx->pending)) + complete(&ctx->complete); +} + +static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num, + unsigned size) +{ + struct queued_ctx ctx; + struct usb_device *udev = testdev_to_usbdev(dev); + void *buf; + dma_addr_t buf_dma; + int i; + int retval = -ENOMEM; + + init_completion(&ctx.complete); + atomic_set(&ctx.pending, 1); /* One more than the actual value */ + ctx.num = num; + ctx.status = 0; + + buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma); + if (!buf) + return retval; + memset(buf, 0, size); + + /* Allocate and init the urbs we'll queue */ + ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL); + if (!ctx.urbs) + goto free_buf; + for (i = 0; i < num; i++) { + ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ctx.urbs[i]) + goto free_urbs; + usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size, + unlink_queued_callback, &ctx); + ctx.urbs[i]->transfer_dma = buf_dma; + ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + /* Submit all the URBs and then unlink URBs num - 4 and num - 2. */ + for (i = 0; i < num; i++) { + atomic_inc(&ctx.pending); + retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL); + if (retval != 0) { + dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n", + i, retval); + atomic_dec(&ctx.pending); + ctx.status = retval; + break; + } + } + if (i == num) { + usb_unlink_urb(ctx.urbs[num - 4]); + usb_unlink_urb(ctx.urbs[num - 2]); + } else { + while (--i >= 0) + usb_unlink_urb(ctx.urbs[i]); + } + + if (atomic_dec_and_test(&ctx.pending)) /* The extra count */ + complete(&ctx.complete); + wait_for_completion(&ctx.complete); + retval = ctx.status; + + free_urbs: + for (i = 0; i < num; i++) + usb_free_urb(ctx.urbs[i]); + kfree(ctx.urbs); + free_buf: + usb_free_coherent(udev, size, buf, buf_dma); + return retval; +} + +/*-------------------------------------------------------------------------*/ + static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) { int retval; @@ -1970,8 +2070,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 0); break; - /* FIXME unlink from queue (ring with N urbs) */ - /* FIXME scatterlist cancel (needs helper thread) */ /* Tests for bulk I/O using DMA mapping by core and odd address */ @@ -2064,6 +2162,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 1); break; + /* unlink URBs from a bulk-OUT queue */ + case 24: + if (dev->out_pipe == 0 || !param->length || param->sglen < 4) + break; + retval = 0; + dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + "%d %d-byte writes\n", + param->iterations, param->sglen, param->length); + for (i = param->iterations; retval == 0 && i > 0; --i) { + retval = unlink_queued(dev, dev->out_pipe, + param->sglen, param->length); + if (retval) { + dev_err(&intf->dev, + "unlink queued writes failed %d, " + "iterations left %d\n", retval, i); + break; + } + } + break; + } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; @@ -2192,6 +2310,9 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) case USB_SPEED_HIGH: tmp = "high"; break; + case USB_SPEED_SUPER: + tmp = "super"; + break; default: tmp = "unknown"; break; |