From 084fb206a91f72b22c4e2f41709730a81e3e0de6 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Sun, 16 Jan 2011 19:17:11 +0100 Subject: USB: usbtest - Add tests to ensure HCDs can accept byte aligned buffers. Add a set of new tests similar to the existing ones but using transfer buffers at an "odd" address [ie offset of +1 from the buffer obtained by kmalloc() or usb_alloc_coherent()] The new tests are: #17 : bulk out (like #1) using kmalloc and DMA mapping by USB core. #18 : bulk in (like #2) using kmalloc and DMA mapping by USB core. #19 : bulk out (like #1) using usb_alloc_coherent() #20 : bulk in (like #2) using usb_alloc_coherent() #21 : control write (like #14) #22 : isochonous out (like #15) #23 : isochonous in (like #16) Signed-off-by: Martin Fuzzey Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 236 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 212 insertions(+), 24 deletions(-) (limited to 'drivers/usb/misc/usbtest.c') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index a35b427..388cc12 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -83,6 +83,8 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test) #define WARNING(tdev, fmt, args...) \ dev_warn(&(tdev)->intf->dev , fmt , ## args) +#define GUARD_BYTE 0xA5 + /*-------------------------------------------------------------------------*/ static int @@ -186,11 +188,12 @@ static void simple_callback(struct urb *urb) complete(urb->context); } -static struct urb *simple_alloc_urb( +static struct urb *usbtest_alloc_urb( struct usb_device *udev, int pipe, - unsigned long bytes -) + unsigned long bytes, + unsigned transfer_flags, + unsigned offset) { struct urb *urb; @@ -201,19 +204,46 @@ static struct urb *simple_alloc_urb( urb->interval = (udev->speed == USB_SPEED_HIGH) ? (INTERRUPT_RATE << 3) : INTERRUPT_RATE; - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = transfer_flags; if (usb_pipein(pipe)) urb->transfer_flags |= URB_SHORT_NOT_OK; - urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL, - &urb->transfer_dma); + + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset, + GFP_KERNEL, &urb->transfer_dma); + else + urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL); + if (!urb->transfer_buffer) { usb_free_urb(urb); - urb = NULL; - } else - memset(urb->transfer_buffer, 0, bytes); + return NULL; + } + + /* To test unaligned transfers add an offset and fill the + unused memory with a guard value */ + if (offset) { + memset(urb->transfer_buffer, GUARD_BYTE, offset); + urb->transfer_buffer += offset; + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + urb->transfer_dma += offset; + } + + /* For inbound transfers use guard byte so that test fails if + data not correctly copied */ + memset(urb->transfer_buffer, + usb_pipein(urb->pipe) ? GUARD_BYTE : 0, + bytes); return urb; } +static struct urb *simple_alloc_urb( + struct usb_device *udev, + int pipe, + unsigned long bytes) +{ + return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0); +} + static unsigned pattern; static unsigned mod_pattern; module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); @@ -238,13 +268,38 @@ static inline void simple_fill_buf(struct urb *urb) } } -static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) +static inline unsigned buffer_offset(void *buf) +{ + return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1); +} + +static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb) +{ + u8 *buf = urb->transfer_buffer; + u8 *guard = buf - buffer_offset(buf); + unsigned i; + + for (i = 0; guard < buf; i++, guard++) { + if (*guard != GUARD_BYTE) { + ERROR(tdev, "guard byte[%d] %d (not %d)\n", + i, *guard, GUARD_BYTE); + return -EINVAL; + } + } + return 0; +} + +static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) { unsigned i; u8 expected; u8 *buf = urb->transfer_buffer; unsigned len = urb->actual_length; + int ret = check_guard_bytes(tdev, urb); + if (ret) + return ret; + for (i = 0; i < len; i++, buf++) { switch (pattern) { /* all-zeroes has no synchronization issues */ @@ -274,8 +329,16 @@ static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) static void simple_free_urb(struct urb *urb) { - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); + unsigned offset = buffer_offset(urb->transfer_buffer); + + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + usb_free_coherent( + urb->dev, + urb->transfer_buffer_length + offset, + urb->transfer_buffer - offset, + urb->transfer_dma - offset); + else + kfree(urb->transfer_buffer - offset); usb_free_urb(urb); } @@ -1256,7 +1319,7 @@ done: * try whatever we're told to try. */ static int ctrl_out(struct usbtest_dev *dev, - unsigned count, unsigned length, unsigned vary) + unsigned count, unsigned length, unsigned vary, unsigned offset) { unsigned i, j, len; int retval; @@ -1267,10 +1330,11 @@ static int ctrl_out(struct usbtest_dev *dev, if (length < 1 || length > 0xffff || vary >= length) return -EINVAL; - buf = kmalloc(length, GFP_KERNEL); + buf = kmalloc(length + offset, GFP_KERNEL); if (!buf) return -ENOMEM; + buf += offset; udev = testdev_to_usbdev(dev); len = length; retval = 0; @@ -1337,7 +1401,7 @@ static int ctrl_out(struct usbtest_dev *dev, ERROR(dev, "ctrl_out %s failed, code %d, count %d\n", what, retval, i); - kfree(buf); + kfree(buf - offset); return retval; } @@ -1373,6 +1437,8 @@ static void iso_callback(struct urb *urb) ctx->errors += urb->number_of_packets; else if (urb->actual_length != urb->transfer_buffer_length) ctx->errors++; + else if (check_guard_bytes(ctx->dev, urb) != 0) + ctx->errors++; if (urb->status == 0 && ctx->count > (ctx->pending - 1) && !ctx->submit_error) { @@ -1408,7 +1474,8 @@ static struct urb *iso_alloc_urb( struct usb_device *udev, int pipe, struct usb_endpoint_descriptor *desc, - long bytes + long bytes, + unsigned offset ) { struct urb *urb; @@ -1428,13 +1495,24 @@ static struct urb *iso_alloc_urb( urb->number_of_packets = packets; urb->transfer_buffer_length = bytes; - urb->transfer_buffer = usb_alloc_coherent(udev, bytes, GFP_KERNEL, - &urb->transfer_dma); + urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset, + GFP_KERNEL, + &urb->transfer_dma); if (!urb->transfer_buffer) { usb_free_urb(urb); return NULL; } - memset(urb->transfer_buffer, 0, bytes); + if (offset) { + memset(urb->transfer_buffer, GUARD_BYTE, offset); + urb->transfer_buffer += offset; + urb->transfer_dma += offset; + } + /* For inbound transfers use guard byte so that test fails if + data not correctly copied */ + memset(urb->transfer_buffer, + usb_pipein(urb->pipe) ? GUARD_BYTE : 0, + bytes); + for (i = 0; i < packets; i++) { /* here, only the last packet will be short */ urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); @@ -1452,7 +1530,7 @@ static struct urb *iso_alloc_urb( static int test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, - int pipe, struct usb_endpoint_descriptor *desc) + int pipe, struct usb_endpoint_descriptor *desc, unsigned offset) { struct iso_context context; struct usb_device *udev; @@ -1480,7 +1558,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, for (i = 0; i < param->sglen; i++) { urbs[i] = iso_alloc_urb(udev, pipe, desc, - param->length); + param->length, offset); if (!urbs[i]) { status = -ENOMEM; goto fail; @@ -1542,6 +1620,26 @@ fail: return status; } +static int test_unaligned_bulk( + struct usbtest_dev *tdev, + int pipe, + unsigned length, + int iterations, + unsigned transfer_flags, + const char *label) +{ + int retval; + struct urb *urb = usbtest_alloc_urb( + testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1); + + if (!urb) + return -ENOMEM; + + retval = simple_io(tdev, urb, iterations, 0, 0, label); + simple_free_urb(urb); + return retval; +} + /*-------------------------------------------------------------------------*/ /* We only have this one interface to user space, through usbfs. @@ -1843,7 +1941,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) realworld ? 1 : 0, param->length, param->vary); retval = ctrl_out(dev, param->iterations, - param->length, param->vary); + param->length, param->vary, 0); break; /* iso write tests */ @@ -1856,7 +1954,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) param->sglen, param->length); /* FIRMWARE: iso sink */ retval = test_iso_queue(dev, param, - dev->out_iso_pipe, dev->iso_out); + dev->out_iso_pipe, dev->iso_out, 0); break; /* iso read tests */ @@ -1869,13 +1967,103 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) param->sglen, param->length); /* FIRMWARE: iso source */ retval = test_iso_queue(dev, param, - dev->in_iso_pipe, dev->iso_in); + 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 */ + case 17: + if (dev->out_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 17: write odd addr %d bytes %u times core map\n", + param->length, param->iterations); + + retval = test_unaligned_bulk( + dev, dev->out_pipe, + param->length, param->iterations, + 0, "test17"); + break; + + case 18: + if (dev->in_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 18: read odd addr %d bytes %u times core map\n", + param->length, param->iterations); + + retval = test_unaligned_bulk( + dev, dev->in_pipe, + param->length, param->iterations, + 0, "test18"); + break; + + /* Tests for bulk I/O using premapped coherent buffer and odd address */ + case 19: + if (dev->out_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 19: write odd addr %d bytes %u times premapped\n", + param->length, param->iterations); + + retval = test_unaligned_bulk( + dev, dev->out_pipe, + param->length, param->iterations, + URB_NO_TRANSFER_DMA_MAP, "test19"); + break; + + case 20: + if (dev->in_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 20: read odd addr %d bytes %u times premapped\n", + param->length, param->iterations); + + retval = test_unaligned_bulk( + dev, dev->in_pipe, + param->length, param->iterations, + URB_NO_TRANSFER_DMA_MAP, "test20"); + break; + + /* control write tests with unaligned buffer */ + case 21: + if (!dev->info->ctrl_out) + break; + dev_info(&intf->dev, + "TEST 21: %d ep0out odd addr, %d..%d vary %d\n", + param->iterations, + realworld ? 1 : 0, param->length, + param->vary); + retval = ctrl_out(dev, param->iterations, + param->length, param->vary, 1); + break; + + /* unaligned iso tests */ + case 22: + if (dev->out_iso_pipe == 0 || param->sglen == 0) + break; + dev_info(&intf->dev, + "TEST 22: write %d iso odd, %d entries of %d bytes\n", + param->iterations, + param->sglen, param->length); + retval = test_iso_queue(dev, param, + dev->out_iso_pipe, dev->iso_out, 1); + break; + + case 23: + if (dev->in_iso_pipe == 0 || param->sglen == 0) + break; + dev_info(&intf->dev, + "TEST 23: read %d iso odd, %d entries of %d bytes\n", + param->iterations, + param->sglen, param->length); + retval = test_iso_queue(dev, param, + dev->in_iso_pipe, dev->iso_in, 1); + break; + } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; -- cgit v1.1