aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/usbtest.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/misc/usbtest.c')
-rw-r--r--drivers/usb/misc/usbtest.c133
1 files changed, 127 insertions, 6 deletions
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 388cc128072a..bb10846affc3 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(&param->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;