USB: usbtest - Add tests to ensure HCDs can accept byte aligned buffers.
authorMartin Fuzzey <mfuzzey@gmail.com>
Sun, 16 Jan 2011 18:17:11 +0000 (19:17 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Sun, 23 Jan 2011 03:42:13 +0000 (19:42 -0800)
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 <mfuzzey@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/misc/usbtest.c

index a35b427c0bac59ce6965b37d28c6a8748ae03dd5..388cc128072af3f43da5309ab7caed765a6f9020 100644 (file)
@@ -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(&param->duration);
        param->duration.tv_sec -= start.tv_sec;