Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 084fb206 authored by Martin Fuzzey's avatar Martin Fuzzey Committed by Greg Kroah-Hartman
Browse files

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: default avatarMartin Fuzzey <mfuzzey@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 50a6cb93
Loading
Loading
Loading
Loading
+212 −24
Original line number Diff line number Diff line
@@ -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_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;