Greg KH wrote: >On Thu, Mar 23, 2006 at 11:17:02PM -0500, Craig W. Nadler wrote: > > >> About a year ago a new test method was developed for running the USB >>Hi-Speed Host Electrical tests during compliance testing. The new method >>was designed for embedded devices with OTG or Host ports. Prior to this >>the test method required that Windows test software be run on the test >>target. The new method allows the firmware in the test target to set it >>up for different test cases based on the Vendor and Product ID of the >>attached test device. >> Attached is a patch that shows how I think this support could be >>added to the USB core directory and the EHCI driver. The changes to the >>USB core would check newly attached devices to see if they have the >>Vendor ID set aside for this test method. If so and it's running in >>Hi-Speed mode then the HCD entry point for sending Root Hub device >>requests is called to cause the HC Driver to run a test based on the >>Product ID. This would provide a frame work for adding support for this >>test method to other Hi-Speed Host Controller Drivers. >> The patch compiles cleanly but has not yet been tested at all. >>Before I put more work into it I'd like to know if anyone else is >>interested in it. Also if anyone has any comments about how I coded it >>please let me know. >> >> > >Hm, I don't know the ehci stuff, so I can't comment on that, but as far >as the idea goes, I really like it. I get a few questions a year about >using Linux to run USB compliance tests, especially from embedded people >who do not run any other operating system on their machines. > >So anything you can do to help this happen would be greatly appreciated. > >thanks, > >greg k-h > > > > Here's the latest patch for Embedded High-speed Host Electrical Test support. I ran all the test modes on an EHCI controller hooked up to a USB analyzer and they all seem to be working correctly.
Please note that the USB Host Controller driver must also support this option for the test support to work. This patch adds support for this to the USB Host Stack Core and the EHCI driver. If you are using another high-speed host controller you will need to add support for this to the HC driver for this option to work on that HC. Best Regards, Craig Nadler
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/core/hcd.h 2006-03-31 01:36:12.000000000 -0500 @@ -87,6 +87,7 @@ unsigned uses_new_polling:1; unsigned poll_rh:1; /* poll for rh status? */ unsigned poll_pending:1; /* status has changed? */ + unsigned EHSET_in_progress:1; int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/core/hub.c 2006-03-31 01:36:12.000000000 -0500 @@ -76,6 +76,16 @@ "first one fails"); +#ifdef CONFIG_USB_EHSET +/* Semaphore used for Hi-Speed Host Electrical tests */ +static DECLARE_MUTEX(ehset_sem); +static struct workqueue_struct *EHSET_workqueue = NULL; +#endif /* CONFIG_USB_EHSET */ + +static int hub_suspend(struct usb_interface *, pm_message_t); +static int hub_resume(struct usb_interface *); + + #ifdef DEBUG static inline char *portspeed (int portstatus) { @@ -1264,6 +1274,92 @@ #endif +#ifdef CONFIG_USB_EHSET +static void EHSET_tests(void *data) +{ + struct usb_device *udev = (struct usb_device *)data; + struct usb_hcd *hcd = udev->bus->hcpriv; + int status; + u8 port = udev->portnum; + + dev_info(&udev->dev, "running EHSET Test %x\n", + udev->descriptor.idProduct); + + switch (udev->descriptor.idProduct) { + case EHSET_TEST_SE0_NAK: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + (USB_PORT_TEST_SE0_NAK<<8)|port, + NULL, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + /* This test runs until the host is powered off */ + break; + case EHSET_TEST_J: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + (USB_PORT_TEST_J<<8)|port, + NULL, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + /* This test runs until the host is powered off */ + break; + case EHSET_TEST_K: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + (USB_PORT_TEST_K<<8)|port, + NULL, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + /* This test runs until the host is powered off */ + break; + case EHSET_TEST_PACKET: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + (USB_PORT_TEST_PACKET<<8)|port, + NULL, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + down(&ehset_sem); + hcd->EHSET_in_progress = 0; + up(&ehset_sem); + break; + /* Note the FORCE ENABLE test is no longer used in the EHSET spec. */ + case EHSET_TEST_FORCE_ENABLE: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + (USB_PORT_TEST_FORCE_ENABLE<<8)|port, + NULL, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + down(&ehset_sem); + hcd->EHSET_in_progress = 0; + up(&ehset_sem); + break; + case EHSET_HS_HOST_PORT_SUSPEND_RESUME: + case EHSET_SINGLE_STEP_GET_DEV_DESC: + case EHSET_SINGLE_STEP_SET_FEATURE: + status = hcd->driver->hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_TEST, + ((udev->descriptor.idProduct&0xFF)<<8)| + port, (char *)udev, 0); + if (status) + dev_err(&udev->dev, "SetPortFeature Failed\n"); + down(&ehset_sem); + hcd->EHSET_in_progress = 0; + up(&ehset_sem); + break; + default: + dev_err(&udev->dev, "EHSET: Unsupported test mode %x\n", + udev->descriptor.idProduct); + down(&ehset_sem); + hcd->EHSET_in_progress = 0; + up(&ehset_sem); + } +} +#endif /* CONFIG_USB_EHSET */ + + #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" #endif @@ -1406,9 +1502,52 @@ /* USB device state == configured ... usable */ usb_notify_add_device(udev); - usb_unlock_device(udev); +#ifdef CONFIG_USB_EHSET + if (udev->speed == USB_SPEED_HIGH && + udev->parent == udev->bus->root_hub && + udev->descriptor.idVendor == 0x1A0A) + { + static struct work_struct EHSET_work; + struct usb_hcd *hcd = udev->bus->hcpriv; + + dev_info(&udev->dev, "Found EHSET Test Device (test=%x)\n", + udev->descriptor.idProduct); + + if (!EHSET_workqueue) { + EHSET_workqueue = create_singlethread_workqueue( + "USB EHSET"); + if (!EHSET_workqueue) { + dev_err(&udev->dev, "EHSET: Failed to create " + "a workqueue\n"); + } + } + + if (EHSET_workqueue) { + if (down_trylock(&ehset_sem) == 0) { + if (!hcd->EHSET_in_progress) { + hcd->EHSET_in_progress = 1; + up(&ehset_sem); + INIT_WORK(&EHSET_work, EHSET_tests, + udev); + queue_work(EHSET_workqueue, + &EHSET_work); + } else { + up(&ehset_sem); + dev_err(&udev->dev, "EHSET: test " + "already in progress" + "\n"); + } + } else { + dev_err(&udev->dev, "EHSET: Failed to get " + "lock\n"); + } + } + } + else +#endif /* CONFIG_USB_EHSET */ + return 0; fail: @@ -2928,6 +3067,12 @@ * individual hub resources. -greg */ usb_deregister(&hub_driver); + +#ifdef CONFIG_USB_EHSET + if (EHSET_workqueue) + destroy_workqueue(EHSET_workqueue); +#endif /* CONFIG_USB_EHSET */ + } /* usb_hub_cleanup() */ static int config_descriptors_changed(struct usb_device *udev) diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/core/hub.h 2006-03-31 01:36:12.000000000 -0500 @@ -55,6 +55,35 @@ #define USB_PORT_FEAT_TEST 21 #define USB_PORT_FEAT_INDICATOR 22 + +/* + * Hub Port Test Mode Selector Codes + * See USB 2.0 spec Table 11-24 + */ + +#define USB_PORT_TEST_J 0x01 +#define USB_PORT_TEST_K 0x02 +#define USB_PORT_TEST_SE0_NAK 0x03 +#define USB_PORT_TEST_PACKET 0x04 +#define USB_PORT_TEST_FORCE_ENABLE 0x05 + + +/* + * Product IDs used to trigger USB Hi-Speed Host Electrical Tests + * on the root hub. See USB 2.0 spec 7.1.20 and the + * Embedded High-speed Host Electrical Test Procedure. + */ +#define EHSET_TEST_SE0_NAK 0x0101 +#define EHSET_TEST_J 0x0102 +#define EHSET_TEST_K 0x0103 +#define EHSET_TEST_PACKET 0x0104 +/* Note that the FORCE ENABLE test is no longer used in the EHSET spec. */ +#define EHSET_TEST_FORCE_ENABLE 0x0105 +#define EHSET_HS_HOST_PORT_SUSPEND_RESUME 0x0106 +#define EHSET_SINGLE_STEP_GET_DEV_DESC 0x0107 +#define EHSET_SINGLE_STEP_SET_FEATURE 0x0108 + + /* * Hub Status and Hub Change results * See USB 2.0 spec Table 11-19 and Table 11-20 diff -Nru a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig --- a/drivers/usb/core/Kconfig 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/core/Kconfig 2006-03-31 01:36:33.000000000 -0500 @@ -100,3 +100,22 @@ convenient for many stages of product development. +config USB_EHSET + bool "Embedded High-speed Host Electrical Test Support" + depends on USB + default n + help + This option is only used if you are developing firmware for + an embedded device with a Hi-speed USB Host or OTG port. + + If you say Y here, software support for the Embedded + High-speed Host Electrical Tests will be added to the USB + Host stack. This is one of the tests performed during + High-speed USB Host certification testing. + + Please note that the USB Host Controller Driver must also + support this option. For an example of how to add support + for this to a USB Host Controller Driver see the EHCI driver. + + If you are at all unsure then say N here. + diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/host/ehci.h 2006-03-31 01:36:12.000000000 -0500 @@ -245,7 +245,11 @@ #define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ #define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ -/* 19:16 for port testing */ +#define PORT_TEST_J (1<<16) +#define PORT_TEST_K (2<<16) +#define PORT_TEST_SE0_NAK (3<<16) +#define PORT_TEST_PACKET (4<<16) +#define PORT_TEST_FORCE_ENABLE (5<<16) #define PORT_LED_OFF (0<<14) #define PORT_LED_AMBER (1<<14) #define PORT_LED_GREEN (2<<14) @@ -636,6 +640,24 @@ #define ehci_port_speed(ehci, portsc) (1<<USB_PORT_FEAT_HIGHSPEED) #endif + +static struct list_head * qh_urb_transaction ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *head, + gfp_t flags); + +static int submit_async ( + struct ehci_hcd *ehci, + struct usb_host_endpoint *ep, + struct urb *urb, + struct list_head *qtd_list, + gfp_t mem_flags); + +static inline void ehci_qtd_free ( + struct ehci_hcd *ehci, + struct ehci_qtd *qtd); + /*-------------------------------------------------------------------------*/ #ifndef DEBUG diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c --- a/drivers/usb/host/ehci-hub.c 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/host/ehci-hub.c 2006-03-31 01:41:19.000000000 -0500 @@ -504,10 +504,10 @@ } break; case SetPortFeature: - if (!wIndex || wIndex > ports) + if (!wIndex || (wIndex&0xFF) > ports) goto error; wIndex--; - temp = readl (&ehci->regs->port_status [wIndex]); + temp = readl (&ehci->regs->port_status [wIndex&0xFF]); if (temp & PORT_OWNER) break; @@ -520,12 +520,12 @@ if (device_may_wakeup(&hcd->self.root_hub->dev)) temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, - &ehci->regs->port_status [wIndex]); + &ehci->regs->port_status [wIndex&0xFF]); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, - &ehci->regs->port_status [wIndex]); + &ehci->regs->port_status [wIndex&0xFF]); break; case USB_PORT_FEAT_RESET: if (temp & PORT_RESUME) @@ -550,18 +550,283 @@ * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ - ehci->reset_done [wIndex] = jiffies + ehci->reset_done [wIndex&0xFF] = jiffies + msecs_to_jiffies (50); } writel (temp, &ehci->regs->port_status [wIndex]); break; +#ifdef CONFIG_USB_EHSET + case USB_PORT_FEAT_TEST: + ehci_info (ehci, "running EHCI test %x on port %x\n", + ((wIndex>>8)&0xFF), wIndex&0xFF); + switch ((wIndex>>8)&0xFF) { + case USB_PORT_TEST_J: + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing J State\n"); + ehci_bus_suspend (hcd); + spin_lock_irqsave (&ehci->lock, flags); + writel (temp|PORT_TEST_J, + &ehci->regs->port_status + [wIndex&0xFF]); + break; + case USB_PORT_TEST_K: + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing K State\n"); + ehci_bus_suspend (hcd); + spin_lock_irqsave (&ehci->lock, flags); + writel (temp|PORT_TEST_K, + &ehci->regs->port_status + [wIndex&0xFF]); + break; + case USB_PORT_TEST_SE0_NAK: + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing SE0_NAK\n"); + ehci_bus_suspend (hcd); + spin_lock_irqsave (&ehci->lock, flags); + writel (temp|PORT_TEST_SE0_NAK, + &ehci->regs->port_status + [wIndex&0xFF]); + break; + case USB_PORT_TEST_PACKET: + ehci_info (ehci, "Sending Test Packets\n"); + writel (temp|PORT_TEST_PACKET, + &ehci->regs->port_status + [wIndex&0xFF]); + break; + case USB_PORT_TEST_FORCE_ENABLE: + ehci_info (ehci, "Testing FORCE_ENABLE\n"); + writel (temp|PORT_TEST_FORCE_ENABLE, + &ehci->regs->port_status + [wIndex&0xFF]); + break; + case (EHSET_HS_HOST_PORT_SUSPEND_RESUME&0xFF): + { + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing SUSPEND RESUME\n"); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(15000)); + ehci_info (ehci, "Suspend Root Hub\n"); + ehci_bus_suspend (hcd); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(15000)); + ehci_info (ehci, "Resume Root Hub\n"); + ehci_bus_resume (hcd); + spin_lock_irqsave (&ehci->lock, flags); + break; + } + case (EHSET_SINGLE_STEP_GET_DEV_DESC&0xFF): + { + struct list_head qtd_list; + struct list_head test_list; + struct usb_device *dev; + struct ehci_qtd *qtd; + struct urb urb; + struct usb_ctrlrequest setup_packet; + char data_buffer[USB_DT_DEVICE_SIZE]; + + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing SINGLE_STEP_GET_DEV" + "_DESC\n"); + if (!buf) { + ehci_err (ehci, "No usb_device pointer" + " found\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(15000)); + + dev = (struct usb_device *)buf; + setup_packet.bRequestType = USB_DIR_IN; + setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_packet.wValue = (USB_DT_DEVICE << 8); + setup_packet.wIndex = 0; + setup_packet.wLength = USB_DT_DEVICE_SIZE; + + INIT_LIST_HEAD (&qtd_list); + INIT_LIST_HEAD (&test_list); + urb.transfer_buffer_length = + USB_DT_DEVICE_SIZE; + urb.dev = dev; + urb.pipe = usb_rcvctrlpipe(dev, 0); + urb.hcpriv = dev->ep0.hcpriv; + urb.setup_packet = (char *)&setup_packet; + urb.transfer_buffer = data_buffer; + urb.transfer_flags = URB_HCD_DRIVER_TEST; + spin_lock_init(&urb.lock); + urb.setup_dma = dma_map_single ( + hcd->self.controller, + urb.setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + urb.transfer_dma = dma_map_single ( + hcd->self.controller, + urb.transfer_buffer, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (!urb.setup_dma || !urb.transfer_dma) { + ehci_err (ehci, "dma_map_single Failed" + "\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + + if (!qh_urb_transaction (ehci, &urb, &qtd_list, + GFP_ATOMIC)) + { + ehci_err (ehci, "qh_urb_transaction " + "Failed\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + list_add (&qtd->qtd_list, &test_list); + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + list_add_tail (&qtd->qtd_list, &test_list); + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + ehci_qtd_free (ehci, qtd); + + ehci_info (ehci, "Sending SETUP&DATA PHASE\n"); + if (submit_async (ehci, &dev->ep0, &urb, + &test_list, GFP_ATOMIC)) + { + ehci_err (ehci, "Failed to queue up " + "qtds\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + spin_lock_irqsave (&ehci->lock, flags); + break; + } + case (EHSET_SINGLE_STEP_SET_FEATURE&0xFF): + { + struct list_head qtd_list; + struct list_head setup_list; + struct list_head data_list; + struct usb_device *dev; + struct ehci_qtd *qtd; + struct urb urb; + struct usb_ctrlrequest setup_packet; + char data_buffer[USB_DT_DEVICE_SIZE]; + + spin_unlock_irqrestore (&ehci->lock, flags); + ehci_info (ehci, "Testing SINGLE_STEP_SET_" + "FEATURE\n"); + if (!buf) { + ehci_err (ehci, "No usb_device pointer" + " found\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + dev = (struct usb_device *)buf; + setup_packet.bRequestType = USB_DIR_IN; + setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; + setup_packet.wValue = (USB_DT_DEVICE << 8); + setup_packet.wIndex = 0; + setup_packet.wLength = USB_DT_DEVICE_SIZE; + + INIT_LIST_HEAD (&qtd_list); + INIT_LIST_HEAD (&setup_list); + INIT_LIST_HEAD (&data_list); + urb.transfer_buffer_length = + USB_DT_DEVICE_SIZE; + urb.dev = dev; + urb.pipe = usb_rcvctrlpipe(dev, 0); + urb.hcpriv = dev->ep0.hcpriv; + urb.setup_packet = (char *)&setup_packet; + urb.transfer_buffer = data_buffer; + urb.transfer_flags = URB_HCD_DRIVER_TEST; + spin_lock_init(&urb.lock); + urb.setup_dma = dma_map_single ( + hcd->self.controller, + urb.setup_packet, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + urb.transfer_dma = dma_map_single ( + hcd->self.controller, + urb.transfer_buffer, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (!urb.setup_dma || !urb.transfer_dma) { + ehci_err (ehci, "dma_map_single Failed" + "\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + + if (!qh_urb_transaction (ehci, &urb, &qtd_list, + GFP_ATOMIC)) + { + ehci_err (ehci, "qh_urb_transaction " + "Failed\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + list_add (&qtd->qtd_list, &setup_list); + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + list_add (&qtd->qtd_list, &data_list); + qtd = container_of (qtd_list.next, + struct ehci_qtd, qtd_list); + list_del_init (&qtd->qtd_list); + ehci_qtd_free (ehci, qtd); + + ehci_info (ehci, "Sending SETUP PHASE\n"); + if (submit_async (ehci, &dev->ep0, &urb, + &setup_list, GFP_ATOMIC)) + { + ehci_err (ehci, "Failed to queue up " + "qtds\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(15000)); + urb.status = 0; + urb.actual_length = 0; + + ehci_info (ehci, "Sending DATA PHASE\n"); + if (submit_async (ehci, &dev->ep0, &urb, + &data_list, GFP_ATOMIC)) + { + ehci_err (ehci, "Failed to queue up " + "qtds\n"); + spin_lock_irqsave (&ehci->lock, flags); + goto error; + } + spin_lock_irqsave (&ehci->lock, flags); + break; + } + default: + ehci_err (ehci, "EHSET: Unknown test %x\n", + (wIndex>>8)&0xFF); + goto error; + } + break; +#endif /* CONFIG_USB_EHSET */ default: + ehci_err (ehci, "SetPortFeature: Unknown Feature %x\n", + wValue); goto error; } readl (&ehci->regs->command); /* unblock posted writes */ break; default: + ehci_err (ehci, "Unknown Device Request %x\n", typeReq); error: /* "stall" on error */ retval = -EPIPE; diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c 2006-03-20 00:53:29.000000000 -0500 +++ b/drivers/usb/host/ehci-q.c 2006-03-31 01:36:12.000000000 -0500 @@ -260,6 +260,11 @@ urb->actual_length, urb->transfer_buffer_length); #endif +#ifdef CONFIG_USB_EHSET + if (likely (urb->transfer_flags == URB_HCD_DRIVER_TEST)) + return; +#endif + /* complete() can reenter this HCD */ spin_unlock (&ehci->lock); usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb, regs); diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h 2006-03-20 00:53:29.000000000 -0500 +++ b/include/linux/usb.h 2006-03-31 01:36:12.000000000 -0500 @@ -655,6 +655,7 @@ #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ #define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt * needed */ +#define URB_HCD_DRIVER_TEST 0xFFFF /* Do NOT hand back or free this URB. */ struct usb_iso_packet_descriptor { unsigned int offset;