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 <mfuzzey@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
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 @@
 #define WARNING(tdev, fmt, args...) \
 	dev_warn(&(tdev)->intf->dev , fmt , ## args)
 
+#define GUARD_BYTE	0xA5
+
 /*-------------------------------------------------------------------------*/
 
 static int
@@ -186,11 +188,12 @@
 	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 @@
 	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 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 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 @@
  * 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 @@
 	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 @@
 		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 @@
 		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 @@
 	struct usb_device	*udev,
 	int			pipe,
 	struct usb_endpoint_descriptor	*desc,
-	long			bytes
+	long			bytes,
+	unsigned offset
 )
 {
 	struct urb		*urb;
@@ -1428,13 +1495,24 @@
 
 	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 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 @@
 
 	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 @@
 	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 @@
 				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 @@
 				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 @@
 				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;