I updated my EHSET patch for Linux 2.6.21.4 and to make use IOCTL
calls made from a user mode program. This patch adds support for the
SINGLE_STEP_SET_FEATURE test to the EHCI driver. The other EHSET tests
are either already in the EHCI driver in 2.6.21.4 or can be run from a
user mode program using usbfs.
To use the EHSET patch you will also need a small patch to fix a
problem in the EHCI driver with running the existing port test modes. I
also attached source for a simple utility to make the IOCTL calls to
trigger the port test modes in the EHCI driver. The test number used by
this patch for SINGLE_STEP_SET_FEATURE is 0.
Best Regards,
Craig Nadler
diff -uprN a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
--- a/drivers/usb/core/hub.h 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/core/hub.h 2007-06-11 02:50:33.000000000 -0400
@@ -56,6 +56,23 @@
#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
+
+/*
+ * This is used for the 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 USB_PORT_TEST_SINGLE_STEP_SET_FEATURE 0x00
+
+/*
* Hub Status and Hub Change results
* See USB 2.0 spec Table 11-19 and Table 11-20
*/
diff -uprN a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/host/ehci.h 2007-06-11 02:50:33.000000000 -0400
@@ -645,6 +645,24 @@ ehci_port_speed(struct ehci_hcd *ehci, u
#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);
+
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PPC_83xx
diff -uprN a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/host/ehci-hub.c 2007-06-11 02:50:33.000000000 -0400
@@ -430,6 +430,116 @@ ehci_hub_descriptor (
desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
}
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifdef CONFIG_USB_EHCI_EHSET
+
+static int
+single_step_set_feature( struct usb_hcd *hcd, u8 port)
+{
+ struct usb_bus *bus = hcd_to_bus(hcd);
+ struct usb_device *udev;
+ struct ehci_hcd *ehci = hcd_to_ehci (hcd);
+ struct list_head qtd_list;
+ struct list_head setup_list;
+ struct list_head data_list;
+ struct ehci_qtd *qtd;
+ struct urb urb;
+ struct usb_ctrlrequest setup_packet;
+ char data_buffer[USB_DT_DEVICE_SIZE];
+
+ ehci_info (ehci, "Testing SINGLE_STEP_SET_FEATURE\n");
+
+ if (bus == NULL) {
+ ehci_err (ehci, "EHSET: usb_bus pointer is NULL\n");
+ return -EPIPE;
+ }
+
+ udev = bus->root_hub;
+ if (udev == NULL) {
+ ehci_err (ehci, "EHSET: root_hub pointer is NULL\n");
+ return -EPIPE;
+ }
+
+ udev = udev->children[port-1];
+ if (udev == NULL) {
+ ehci_err (ehci, "EHSET: No test device found on port %d\n",
+ port);
+ return -EPIPE;
+ }
+
+ 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 = udev;
+ urb.pipe = usb_rcvctrlpipe(udev, 0);
+ urb.hcpriv = udev->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");
+ return -EBUSY;
+ }
+
+ if (!qh_urb_transaction (ehci, &urb, &qtd_list, GFP_ATOMIC)) {
+ ehci_err (ehci, "qh_urb_transaction Failed\n");
+ return -EBUSY;
+ }
+
+ 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, &udev->ep0, &urb, &setup_list, GFP_ATOMIC)) {
+ ehci_err (ehci, "Failed to queue up qtds\n");
+ return -EBUSY;
+ }
+
+ 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, &udev->ep0, &urb, &data_list, GFP_ATOMIC))
+ {
+ ehci_err (ehci, "Failed to queue up qtds\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_USB_EHCI_EHSET */
+
+
/*-------------------------------------------------------------------------*/
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
@@ -728,12 +838,34 @@ static int ehci_hub_control (
* about the EHCI-specific stuff.
*/
case USB_PORT_FEAT_TEST:
- if (!selector || selector > 5)
+ ehci_info(ehci, "USB_PORT_FEAT_TEST: running test %x "
+ "on port %d\n", selector, port_num);
+ switch (selector) {
+ case USB_PORT_TEST_J:
+ case USB_PORT_TEST_K:
+ case USB_PORT_TEST_SE0_NAK:
+ case USB_PORT_TEST_PACKET:
+ case USB_PORT_TEST_FORCE_ENABLE:
+ ehci_quiesce(ehci);
+ ehci_halt(ehci);
+ temp |= selector << 16;
+ ehci_writel(ehci, temp, status_reg);
+ break;
+
+#ifdef CONFIG_USB_EHCI_EHSET
+ case USB_PORT_TEST_SINGLE_STEP_SET_FEATURE:
+ spin_unlock_irqrestore (&ehci->lock, flags);
+ if (single_step_set_feature(hcd, port_num)) {
+ spin_lock_irqsave (&ehci->lock, flags);
+ goto error;
+ }
+ spin_lock_irqsave (&ehci->lock, flags);
+ break;
+#endif /* CONFIG_USB_EHCI_EHSET */
+
+ default:
goto error;
- ehci_quiesce(ehci);
- ehci_halt(ehci);
- temp |= selector << 16;
- ehci_writel(ehci, temp, status_reg);
+ }
break;
default:
diff -uprN a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/host/ehci-q.c 2007-06-11 02:50:33.000000000 -0400
@@ -260,6 +260,11 @@ __acquires(ehci->lock)
urb->actual_length, urb->transfer_buffer_length);
#endif
+#ifdef CONFIG_USB_EHCI_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);
diff -uprN a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/host/Kconfig 2007-06-11 02:50:33.000000000 -0400
@@ -38,6 +38,20 @@ config USB_EHCI_SPLIT_ISO
EHCI or USB 2.0 transaction translator implementations.
It should work for ISO-OUT transfers, like audio.
+config USB_EHCI_EHSET
+ bool "Embedded High-speed Host Electrical Test Support (EXPERIMENTAL)"
+ depends on USB_EHCI_HCD && EXPERIMENTAL
+ ---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 EHCI driver. This is
+ one of the tests performed during High-speed USB Host certification
+ testing.
+
+ If you are at all unsure then say N here.
+
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
diff -uprN a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h 2007-06-07 17:27:31.000000000 -0400
+++ b/include/linux/usb.h 2007-06-11 02:50:33.000000000 -0400
@@ -934,6 +934,7 @@ extern int usb_disabled(void);
#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;
--- a/drivers/usb/host/ehci-hub.c 2007-06-07 17:27:31.000000000 -0400
+++ b/drivers/usb/host/ehci-hub.c 2007-06-10 13:45:14.000000000 -0400
@@ -444,7 +444,8 @@ static int ehci_hub_control (
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
- u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1];
+ unsigned port_num = wIndex&0xFF;
+ u32 __iomem *status_reg = &ehci->regs->port_status[port_num - 1];
u32 temp, status;
unsigned long flags;
int retval = 0;
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_OTHER)
#define USB_PORT_FEAT_TEST 21
main(int argc,char*argv[])
{
int fd, ret;
static int port = 0;
static int test = 0;
struct usbdevfs_ctrltransfer ctrl_req = {0};
if (argc != 4) {
fprintf(stderr, "Usage: %s /proc/bus/usb/<BusNo>/<HubID>"
" [port] [test]\n", argv[0]);
exit(1);
}
if (argc > 2) {
port = atoi( argv[2] );
printf( "set port to %d\n", port );
}
if (argc > 3) {
test = atoi( argv[3] );
printf( "set test to %d\n", test );
}
ctrl_req.bRequestType = USB_RT_HUB;
ctrl_req.bRequest = USB_REQ_SET_FEATURE;
ctrl_req.wValue = USB_PORT_FEAT_TEST;
ctrl_req.wIndex = (test << 8)|port;
errno = 0;
fd = open(argv[1],O_RDWR);
if (fd < 0) {
perror("open failed:");
exit(errno);
}
errno = 0;
ret = ioctl(fd, USBDEVFS_CONTROL, &ctrl_req);
printf("IOCTL return status:%d\n",ret);
if (ret<0) {
perror("IOCTL failed:");
close(fd);
exit(3);
} else {
close(fd);
exit(0);
}
return 0;
}
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel