Hi, I've found out why test13 is failing for me on i.MX21. I don't think it's related to my HCD but I'm not sure if it's an ARM or USB problem : When the problem occurs I noticed that both the setup phase buffer and the data phase buffer are in the same 32 byte block. As dma_get_cache_alignment() returns 32 on this platform I believe they share a cache line. So this means that when performing the GET_STATUS request the usb core is calling: dma_map_single(DMA_TO_DEVICE) for the 8 byte setup buffer dma_map_single(DMA_FROM_DEVICE) for the 2 byte data buffer and that when the problem occurs both buffers are in the same cache line.. This causes the data seen in main memory for the data buffer to be incorrect. Attached is a patch to demonstrate the problem. It adds a new usbtest case (#20) which uses the -s parameter to mean alignment. It then just does GET_STATUS requests in a loop (without halts or anything else). So ./testusb -c 1000 -s 32 -a -t 20 never fails whereas ./testusb -c 1000 -s 16 -a -t 20 fails within a few iterations I'm not sure how to fix this - normally the setup buffer and data buffers are obtained from separate kmallocs - I was just unlucky to get nearby addresses. Regards, Martin
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index b626283..29fc9aa 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -950,6 +950,38 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data) } EXPORT_SYMBOL_GPL(usb_get_status); + +int usb_get_status_dma_align_pb(struct usb_device *dev, int type, int target, void *data, int alignment) +{ + int ret; + struct usb_ctrlrequest *dr; + u16 *status; + int padding = max(0, (int)(alignment - sizeof(*status))); + + void *buf = kmalloc(sizeof(*status) + padding + sizeof(struct usb_ctrlrequest), GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + status = (u16*)buf; + dr = (buf + sizeof(*status) + padding); + + printk(KERN_DEBUG "setup %p data %p\n", dr, status); + + dr->bRequestType = USB_DIR_IN | type; + dr->bRequest = USB_REQ_GET_STATUS; + dr->wValue = cpu_to_le16(0); + dr->wIndex = cpu_to_le16(target); + dr->wLength = cpu_to_le16(sizeof(*status)); + + ret = usb_internal_control_msg(dev, usb_rcvctrlpipe(dev, 0), dr, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); + + *(u16 *)data = *status; + kfree(buf); + return ret; +} +EXPORT_SYMBOL_GPL(usb_get_status_dma_align_pb); + /** * usb_clear_halt - tells device to clear endpoint halt/stall condition * @dev: device whose endpoint is halted diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 5f1a19d..4525e91 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1540,6 +1540,7 @@ fail: * urbs and then call usbtest_disconnect(). To abort a test, you're best * off just killing the userspace task and waiting for it to exit. */ +extern int usb_get_status_dma_align_pb(struct usb_device *dev, int type, int target, void *data, int alignment); static int usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) @@ -1853,6 +1854,28 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) // FIXME scatterlist cancel (needs helper thread) + case 20: + if (dev->in_pipe == 0) + break; + retval = 0; + dev_info(&intf->dev, "TEST 20: GET_STATUS %d times with %d alignment\n", + param->iterations, param->length); + + for (i = param->iterations; retval >= 0 && i--; /* NOP */) { + u16 status; + int ep = usb_pipeendpoint (dev->in_pipe) | USB_DIR_IN; + retval = usb_get_status_dma_align_pb(udev, USB_RECIP_ENDPOINT, ep, &status, param->length); + + if (retval >= 0 && status != 0) { + ERROR(dev, "ep %02x bogus status: %04x != 0\n", ep, status); + retval = -EINVAL; + } + } + + if (retval < 0) + ERROR(dev, "failed %d iterations left %d\n", retval, i); + break; + } do_gettimeofday (¶m->duration); param->duration.tv_sec -= start.tv_sec;