Re: [Patch] Support for Embedded Host Hi-Speed Electrical Tests

"Craig W. Nadler" <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
     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

-------------------------------------------------------------------------
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/

_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
ehset_2.6.21.4.patch (text/x-patch, 8.2 KB)
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;
ehci_wIndex.patch (text/x-patch, 519 B)
--- 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;
port_test.c (text/x-csrc, 1.1 KB)
#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;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.