This patch adds two new tests: #17 and #18 which are similar to tests #1 and #2 except that instead of allocating the data buffers with usb_alloc_coherent() and setting URB_NO_TRANSFER_DMA_MAP they use kmalloc() plus a 0-3 byte offset and let the core do the DMA mapping. This mimics the behaviour of drivers such as usbnet which add an offset to the transfer_buffer to align the IP header and causes problems for HCDs that don't accept arbitary alignment for DMA buffers. Signed-off-by: Martin Fuzzey <mfuzzey@xxxxxxxxx> --- drivers/usb/misc/usbtest.c | 102 +++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 96 insertions(+), 6 deletions(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 16dffe9..5acf2ac 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -84,6 +84,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 @@ -184,10 +186,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 int transfer_flags, + unsigned int offset ) { struct urb *urb; @@ -199,11 +203,23 @@ 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, + GFP_KERNEL, &urb->transfer_dma); + else { + /* To test unaligned transfers add an offset and fill the + unused memory with a guard value */ + urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL); + if (urb->transfer_buffer) { + memset(urb->transfer_buffer, GUARD_BYTE, offset); + urb->transfer_buffer += offset; + } + } + if (!urb->transfer_buffer) { usb_free_urb (urb); urb = NULL; @@ -212,6 +228,15 @@ static struct urb *simple_alloc_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 = 0; static unsigned mod_pattern; module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); @@ -236,13 +261,27 @@ static inline void simple_fill_buf (struct urb *urb) } } +static inline void *containing_buffer(void *buf) +{ + return (void *)((unsigned)buf & ~(ARCH_KMALLOC_MINALIGN - 1)); +} + static inline int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) { unsigned i; u8 expected; u8 *buf = urb->transfer_buffer; + u8 *guard = containing_buffer(buf); unsigned len = urb->actual_length; + 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; + } + } + for (i = 0; i < len; i++, buf++) { switch (pattern) { /* all-zeroes has no synchronization issues */ @@ -272,8 +311,11 @@ 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, + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + usb_free_coherent(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); + else + kfree(containing_buffer(urb->transfer_buffer)); usb_free_urb (urb); } @@ -1560,6 +1602,7 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) struct usb_sg_request req; struct timeval start; unsigned i; + unsigned offset; // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. @@ -1858,6 +1901,53 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) // FIXME scatterlist cancel (needs helper thread) + /* Tests for bulk I/O using DMA mapping by core with various offsets + simulates use by usbnet drivers which add offset to align + the IP header + */ + case 17: + if (dev->out_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 17: write %d bytes %u times core dmamap\n", + param->length, param->iterations); + for (offset = 0; offset < 4; offset++) { + dev_info(&intf->dev, "offset=%d\n", offset); + urb = usbtest_alloc_urb(udev, dev->out_pipe, + param->length, 0, offset); + if (!urb) { + retval = -ENOMEM; + break; + } + retval = simple_io(dev, urb, param->iterations, + 0, 0, "test17"); + simple_free_urb(urb); + if (retval) + break; + } + break; + + case 18: + if (dev->in_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 18: read %d bytes %u times core dmamap\n", + param->length, param->iterations); + for (offset = 0; offset < 4; offset++) { + dev_info(&intf->dev, "offset=%d\n", offset); + urb = usbtest_alloc_urb(udev, dev->in_pipe, + param->length, 0, offset); + if (!urb) { + retval = -ENOMEM; + break; + } + retval = simple_io(dev, urb, param->iterations, + 0, 0, "test18"); + simple_free_urb(urb); + if (retval) + break; + } + break; } do_gettimeofday (¶m->duration); param->duration.tv_sec -= start.tv_sec; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html