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

Reply via email to