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

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
...@@ -83,6 +83,8 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test) ...@@ -83,6 +83,8 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test)
#define WARNING(tdev, fmt, args...) \ #define WARNING(tdev, fmt, args...) \
dev_warn(&(tdev)->intf->dev , fmt , ## args) dev_warn(&(tdev)->intf->dev , fmt , ## args)
#define GUARD_BYTE 0xA5
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static int static int
...@@ -186,11 +188,12 @@ static void simple_callback(struct urb *urb) ...@@ -186,11 +188,12 @@ static void simple_callback(struct urb *urb)
complete(urb->context); complete(urb->context);
} }
static struct urb *simple_alloc_urb( static struct urb *usbtest_alloc_urb(
struct usb_device *udev, struct usb_device *udev,
int pipe, int pipe,
unsigned long bytes unsigned long bytes,
) unsigned transfer_flags,
unsigned offset)
{ {
struct urb *urb; struct urb *urb;
...@@ -201,19 +204,46 @@ static struct urb *simple_alloc_urb( ...@@ -201,19 +204,46 @@ static struct urb *simple_alloc_urb(
urb->interval = (udev->speed == USB_SPEED_HIGH) urb->interval = (udev->speed == USB_SPEED_HIGH)
? (INTERRUPT_RATE << 3) ? (INTERRUPT_RATE << 3)
: INTERRUPT_RATE; : INTERRUPT_RATE;
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->transfer_flags = transfer_flags;
if (usb_pipein(pipe)) if (usb_pipein(pipe))
urb->transfer_flags |= URB_SHORT_NOT_OK; 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) { if (!urb->transfer_buffer) {
usb_free_urb(urb); usb_free_urb(urb);
urb = NULL; return NULL;
} else }
memset(urb->transfer_buffer, 0, bytes);
/* 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; 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 pattern;
static unsigned mod_pattern; static unsigned mod_pattern;
module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
...@@ -238,13 +268,38 @@ static inline void simple_fill_buf(struct urb *urb) ...@@ -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; unsigned i;
u8 expected; u8 expected;
u8 *buf = urb->transfer_buffer; u8 *buf = urb->transfer_buffer;
unsigned len = urb->actual_length; unsigned len = urb->actual_length;
int ret = check_guard_bytes(tdev, urb);
if (ret)
return ret;
for (i = 0; i < len; i++, buf++) { for (i = 0; i < len; i++, buf++) {
switch (pattern) { switch (pattern) {
/* all-zeroes has no synchronization issues */ /* all-zeroes has no synchronization issues */
...@@ -274,8 +329,16 @@ static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) ...@@ -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) static void simple_free_urb(struct urb *urb)
{ {
usb_free_coherent(urb->dev, urb->transfer_buffer_length, unsigned offset = buffer_offset(urb->transfer_buffer);
urb->transfer_buffer, urb->transfer_dma);
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); usb_free_urb(urb);
} }
...@@ -1256,7 +1319,7 @@ static int halt_simple(struct usbtest_dev *dev) ...@@ -1256,7 +1319,7 @@ static int halt_simple(struct usbtest_dev *dev)
* try whatever we're told to try. * try whatever we're told to try.
*/ */
static int ctrl_out(struct usbtest_dev *dev, 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; unsigned i, j, len;
int retval; int retval;
...@@ -1267,10 +1330,11 @@ static int ctrl_out(struct usbtest_dev *dev, ...@@ -1267,10 +1330,11 @@ static int ctrl_out(struct usbtest_dev *dev,
if (length < 1 || length > 0xffff || vary >= length) if (length < 1 || length > 0xffff || vary >= length)
return -EINVAL; return -EINVAL;
buf = kmalloc(length, GFP_KERNEL); buf = kmalloc(length + offset, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
buf += offset;
udev = testdev_to_usbdev(dev); udev = testdev_to_usbdev(dev);
len = length; len = length;
retval = 0; retval = 0;
...@@ -1337,7 +1401,7 @@ static int ctrl_out(struct usbtest_dev *dev, ...@@ -1337,7 +1401,7 @@ static int ctrl_out(struct usbtest_dev *dev,
ERROR(dev, "ctrl_out %s failed, code %d, count %d\n", ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",
what, retval, i); what, retval, i);
kfree(buf); kfree(buf - offset);
return retval; return retval;
} }
...@@ -1373,6 +1437,8 @@ static void iso_callback(struct urb *urb) ...@@ -1373,6 +1437,8 @@ static void iso_callback(struct urb *urb)
ctx->errors += urb->number_of_packets; ctx->errors += urb->number_of_packets;
else if (urb->actual_length != urb->transfer_buffer_length) else if (urb->actual_length != urb->transfer_buffer_length)
ctx->errors++; ctx->errors++;
else if (check_guard_bytes(ctx->dev, urb) != 0)
ctx->errors++;
if (urb->status == 0 && ctx->count > (ctx->pending - 1) if (urb->status == 0 && ctx->count > (ctx->pending - 1)
&& !ctx->submit_error) { && !ctx->submit_error) {
...@@ -1408,7 +1474,8 @@ static struct urb *iso_alloc_urb( ...@@ -1408,7 +1474,8 @@ static struct urb *iso_alloc_urb(
struct usb_device *udev, struct usb_device *udev,
int pipe, int pipe,
struct usb_endpoint_descriptor *desc, struct usb_endpoint_descriptor *desc,
long bytes long bytes,
unsigned offset
) )
{ {
struct urb *urb; struct urb *urb;
...@@ -1428,13 +1495,24 @@ static struct urb *iso_alloc_urb( ...@@ -1428,13 +1495,24 @@ static struct urb *iso_alloc_urb(
urb->number_of_packets = packets; urb->number_of_packets = packets;
urb->transfer_buffer_length = bytes; 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); &urb->transfer_dma);
if (!urb->transfer_buffer) { if (!urb->transfer_buffer) {
usb_free_urb(urb); usb_free_urb(urb);
return NULL; 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++) { for (i = 0; i < packets; i++) {
/* here, only the last packet will be short */ /* here, only the last packet will be short */
urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp);
...@@ -1452,7 +1530,7 @@ static struct urb *iso_alloc_urb( ...@@ -1452,7 +1530,7 @@ static struct urb *iso_alloc_urb(
static int static int
test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, 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 iso_context context;
struct usb_device *udev; struct usb_device *udev;
...@@ -1480,7 +1558,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, ...@@ -1480,7 +1558,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
for (i = 0; i < param->sglen; i++) { for (i = 0; i < param->sglen; i++) {
urbs[i] = iso_alloc_urb(udev, pipe, desc, urbs[i] = iso_alloc_urb(udev, pipe, desc,
param->length); param->length, offset);
if (!urbs[i]) { if (!urbs[i]) {
status = -ENOMEM; status = -ENOMEM;
goto fail; goto fail;
...@@ -1542,6 +1620,26 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, ...@@ -1542,6 +1620,26 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
return status; 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. /* 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) ...@@ -1843,7 +1941,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
realworld ? 1 : 0, param->length, realworld ? 1 : 0, param->length,
param->vary); param->vary);
retval = ctrl_out(dev, param->iterations, retval = ctrl_out(dev, param->iterations,
param->length, param->vary); param->length, param->vary, 0);
break; break;
/* iso write tests */ /* iso write tests */
...@@ -1856,7 +1954,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) ...@@ -1856,7 +1954,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->sglen, param->length); param->sglen, param->length);
/* FIRMWARE: iso sink */ /* FIRMWARE: iso sink */
retval = test_iso_queue(dev, param, retval = test_iso_queue(dev, param,
dev->out_iso_pipe, dev->iso_out); dev->out_iso_pipe, dev->iso_out, 0);
break; break;
/* iso read tests */ /* iso read tests */
...@@ -1869,13 +1967,103 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) ...@@ -1869,13 +1967,103 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
param->sglen, param->length); param->sglen, param->length);
/* FIRMWARE: iso source */ /* FIRMWARE: iso source */
retval = test_iso_queue(dev, param, retval = test_iso_queue(dev, param,
dev->in_iso_pipe, dev->iso_in); dev->in_iso_pipe, dev->iso_in, 0);
break; break;
/* FIXME unlink from queue (ring with N urbs) */ /* FIXME unlink from queue (ring with N urbs) */
/* FIXME scatterlist cancel (needs helper thread) */ /* 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); do_gettimeofday(&param->duration);
param->duration.tv_sec -= start.tv_sec; param->duration.tv_sec -= start.tv_sec;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment