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)
#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 @@ static int halt_simple(struct usbtest_dev *dev)
* 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 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param,
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;
......
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