|
@@ -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(¶m->duration);
|
|
|
param->duration.tv_sec -= start.tv_sec;
|