Patchwork [U-Boot] usb: Add new command to set USB 2.0 port test modes

login
register
mail settings
Submitter Julius Werner
Date Feb. 20, 2013, 1:14 a.m.
Message ID <1361322897-16029-1-git-send-email-jwerner@chromium.org>
Download mbox | patch
Permalink /patch/222042/
State Rejected
Delegated to: Marek Vasut
Headers show

Comments

Julius Werner - Feb. 20, 2013, 1:14 a.m.
This patch adds a new 'usb test' command, that will set a port to a USB
2.0 test mode (see USB 2.0 spec 7.1.20). It supports all five test modes
on both downstream hub ports and ordinary device's upstream ports. In
addition, it supports EHCI root hub ports.

Signed-off-by: Julius Werner <jwerner@chromium.org>
---
 common/cmd_usb.c            | 121 +++++++++++++++++++++++++++++++++++---------
 drivers/usb/host/ehci-hcd.c |  30 +++++++----
 include/usb_defs.h          |  13 +++++
 3 files changed, 130 insertions(+), 34 deletions(-)
Julius Werner - March 1, 2013, 4:06 a.m.
Oops... I somehow pulled three lines of other local changes in there
that don't belong when I cherry-picked this into my upstream branch.
Sorry, will resubmit this once more.

On Tue, Feb 19, 2013 at 5:14 PM, Julius Werner <jwerner@chromium.org> wrote:
> This patch adds a new 'usb test' command, that will set a port to a USB
> 2.0 test mode (see USB 2.0 spec 7.1.20). It supports all five test modes
> on both downstream hub ports and ordinary device's upstream ports. In
> addition, it supports EHCI root hub ports.
>
> Signed-off-by: Julius Werner <jwerner@chromium.org>
> ---
>  common/cmd_usb.c            | 121 +++++++++++++++++++++++++++++++++++---------
>  drivers/usb/host/ehci-hcd.c |  30 +++++++----
>  include/usb_defs.h          |  13 +++++
>  3 files changed, 130 insertions(+), 34 deletions(-)
>
> diff --git a/common/cmd_usb.c b/common/cmd_usb.c
> index dacdc2d..adc5f02 100644
> --- a/common/cmd_usb.c
> +++ b/common/cmd_usb.c
> @@ -269,6 +269,22 @@ static void usb_display_config(struct usb_device *dev)
>         printf("\n");
>  }
>
> +static struct usb_device *usb_find_device(int devnum)
> +{
> +       struct usb_device *dev;
> +       int d;
> +
> +       for (d = 0; d < USB_MAX_DEVICE; d++) {
> +               dev = usb_get_dev_index(d);
> +               if (dev == NULL)
> +                       return NULL;
> +               if (dev->devnum == devnum)
> +                       return dev;
> +       }
> +
> +       return NULL;
> +}
> +
>  static inline char *portspeed(int speed)
>  {
>         if (speed == USB_SPEED_HIGH)
> @@ -348,6 +364,66 @@ static void usb_show_tree(struct usb_device *dev)
>         usb_show_tree_graph(dev, &preamble[0]);
>  }
>
> +static int usb_test(struct usb_device *dev, int port, char* arg)
> +{
> +       int mode;
> +
> +       if (port > dev->maxchild) {
> +               printf("Device is no hub or does not have %d ports.\n", port);
> +               return 1;
> +       }
> +
> +       switch (arg[0]) {
> +       case 'J':
> +       case 'j':
> +               printf("Setting Test_J mode");
> +               mode = USB_TEST_MODE_J;
> +               break;
> +       case 'K':
> +       case 'k':
> +               printf("Setting Test_K mode");
> +               mode = USB_TEST_MODE_K;
> +               break;
> +       case 'S':
> +       case 's':
> +               printf("Setting Test_SE0_NAK mode");
> +               mode = USB_TEST_MODE_SE0_NAK;
> +               break;
> +       case 'P':
> +       case 'p':
> +               printf("Setting Test_Packet mode");
> +               mode = USB_TEST_MODE_PACKET;
> +               break;
> +       case 'F':
> +       case 'f':
> +               printf("Setting Test_Force_Enable mode");
> +               mode = USB_TEST_MODE_FORCE_ENABLE;
> +               break;
> +       default:
> +               printf("Unrecognized test mode: %s\nAvailable modes: "
> +                      "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg);
> +               return 1;
> +       }
> +
> +       if (port)
> +               printf(" on downstream facing port %d...\n", port);
> +       else
> +               printf(" on upstream facing port...\n");
> +
> +       if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE,
> +                           port ? USB_RT_PORT : USB_RECIP_DEVICE,
> +                           port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST,
> +                           (mode << 8) | port,
> +                           NULL, 0, USB_CNTL_TIMEOUT) == -1) {
> +               printf("Error during SET_FEATURE.\n");
> +               return 1;
> +       } else {
> +               printf("Test mode successfully set. Use 'usb start' "
> +                      "to return to normal operation.\n");
> +               return 0;
> +       }
> +}
> +
>
>  /******************************************************************************
>   * usb boot command intepreter. Derived from diskboot
> @@ -441,17 +517,9 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>                         }
>                         return 0;
>                 } else {
> -                       int d;
> -
> -                       i = simple_strtoul(argv[2], NULL, 16);
> +                       i = simple_strtoul(argv[2], NULL, 10);
>                         printf("config for device %d\n", i);
> -                       for (d = 0; d < USB_MAX_DEVICE; d++) {
> -                               dev = usb_get_dev_index(d);
> -                               if (dev == NULL)
> -                                       break;
> -                               if (dev->devnum == i)
> -                                       break;
> -                       }
> +                       dev = usb_find_device(i);
>                         if (dev == NULL) {
>                                 printf("*** No device available ***\n");
>                                 return 0;
> @@ -462,6 +530,18 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>                 }
>                 return 0;
>         }
> +       if (strncmp(argv[1], "test", 4) == 0) {
> +               if (argc < 5)
> +                       return CMD_RET_USAGE;
> +               i = simple_strtoul(argv[2], NULL, 10);
> +               dev = usb_find_device(i);
> +               if (dev == NULL) {
> +                       printf("Device %d does not exist.\n", i);
> +                       return 1;
> +               }
> +               i = simple_strtoul(argv[3], NULL, 10);
> +               return usb_test(dev, i, argv[4]);
> +       }
>  #ifdef CONFIG_USB_STORAGE
>         if (strncmp(argv[1], "stor", 4) == 0)
>                 return usb_stor_info();
> @@ -571,7 +651,6 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>         return CMD_RET_USAGE;
>  }
>
> -#ifdef CONFIG_USB_STORAGE
>  U_BOOT_CMD(
>         usb,    5,      1,      do_usb,
>         "USB sub-system",
> @@ -580,30 +659,26 @@ U_BOOT_CMD(
>         "usb stop [f] - stop USB [f]=force stop\n"
>         "usb tree - show USB device tree\n"
>         "usb info [dev] - show available USB devices\n"
> +       "usb test [dev] [port] [mode] - set USB 2.0 test mode\n"
> +       "    (specify port 0 to indicate the device's upstream port)\n"
> +       "    Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n"
> +#ifdef CONFIG_USB_STORAGE
>         "usb storage - show details of USB storage devices\n"
>         "usb dev [dev] - show or set current USB storage device\n"
>         "usb part [dev] - print partition table of one or all USB storage"
> -       " devices\n"
> +       "    devices\n"
>         "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
>         "    to memory address `addr'\n"
>         "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
>         "    from memory address `addr'"
> +#endif /* CONFIG_USB_STORAGE */
>  );
>
>
> +#ifdef CONFIG_USB_STORAGE
>  U_BOOT_CMD(
>         usbboot,        3,      1,      do_usbboot,
>         "boot from USB device",
>         "loadAddr dev:part"
>  );
> -
> -#else
> -U_BOOT_CMD(
> -       usb,    5,      1,      do_usb,
> -       "USB sub-system",
> -       "start - start (scan) USB controller\n"
> -       "usb reset - reset (rescan) USB controller\n"
> -       "usb tree - show USB device tree\n"
> -       "usb info [dev] - show available USB devices"
> -);
> -#endif
> +#endif /* CONFIG_USB_STORAGE */
> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> index 7f98a63..32c7c1d 100644
> --- a/drivers/usb/host/ehci-hcd.c
> +++ b/drivers/usb/host/ehci-hcd.c
> @@ -602,15 +602,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
>         int len, srclen;
>         uint32_t reg;
>         uint32_t *status_reg;
> +       int port = le16_to_cpu(req->index) & 0xff;
>         struct ehci_ctrl *ctrl = dev->controller;
>
> -       if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
> -               printf("The request port(%d) is not configured\n",
> -                       le16_to_cpu(req->index) - 1);
> +       if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
> +               printf("The request port(%d) is not configured\n", port - 1);
>                 return -1;
>         }
> -       status_reg = (uint32_t *)&ctrl->hcor->or_portsc[
> -                                               le16_to_cpu(req->index) - 1];
> +       if (port && !(ctrl->port_enable_mask &
> +                       (1 << (port - 1)))) {
> +               debug("skip request on disabled port%d\n", port - 1);
> +               return 0;
> +       }
> +       status_reg = (uint32_t *)&hcor->or_portsc[port - 1];
>         srclen = 0;
>
>         debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
> @@ -727,7 +731,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
>                         tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
>                 if (reg & EHCI_PS_OCC)
>                         tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
> -               if (ctrl->portreset & (1 << le16_to_cpu(req->index)))
> +               if (ctrl->portreset & (1 << port))
>                         tmpbuf[2] |= USB_PORT_STAT_C_RESET;
>
>                 srcptr = tmpbuf;
> @@ -753,7 +757,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
>                             EHCI_PS_IS_LOWSPEED(reg)) {
>                                 /* Low speed device, give up ownership. */
>                                 debug("port %d low speed --> companion\n",
> -                                     req->index - 1);
> +                                     port - 1);
>                                 reg |= EHCI_PS_PO;
>                                 ehci_writel(status_reg, reg);
>                                 break;
> @@ -779,13 +783,17 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
>                                 ret = handshake(status_reg, EHCI_PS_PR, 0,
>                                                 2 * 1000);
>                                 if (!ret)
> -                                       ctrl->portreset |=
> -                                               1 << le16_to_cpu(req->index);
> +                                       ctrl->portreset |= 1 << port;
>                                 else
>                                         printf("port(%d) reset error\n",
> -                                       le16_to_cpu(req->index) - 1);
> +                                              port - 1);
>                         }
>                         break;
> +               case USB_PORT_FEAT_TEST:
> +                       reg &= ~(0xf << 16);
> +                       reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;
> +                       ehci_writel(status_reg, reg);
> +                       break;
>                 default:
>                         debug("unknown feature %x\n", le16_to_cpu(req->value));
>                         goto unknown;
> @@ -812,7 +820,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
>                         reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
>                         break;
>                 case USB_PORT_FEAT_C_RESET:
> -                       ctrl->portreset &= ~(1 << le16_to_cpu(req->index));
> +                       ctrl->portreset &= ~(1 << port);
>                         break;
>                 default:
>                         debug("unknown feature %x\n", le16_to_cpu(req->value));
> diff --git a/include/usb_defs.h b/include/usb_defs.h
> index 9502544..5c5478f 100644
> --- a/include/usb_defs.h
> +++ b/include/usb_defs.h
> @@ -150,6 +150,18 @@
>  #define USB_REQ_SET_IDLE            0x0A
>  #define USB_REQ_SET_PROTOCOL        0x0B
>
> +/* Device features */
> +#define USB_FEAT_HALT               0x00
> +#define USB_FEAT_WAKEUP             0x01
> +#define USB_FEAT_TEST               0x02
> +
> +/* Test modes */
> +#define USB_TEST_MODE_J             0x01
> +#define USB_TEST_MODE_K             0x02
> +#define USB_TEST_MODE_SE0_NAK       0x03
> +#define USB_TEST_MODE_PACKET        0x04
> +#define USB_TEST_MODE_FORCE_ENABLE  0x05
> +
>
>  /* "pipe" definitions */
>
> @@ -208,6 +220,7 @@
>  #define USB_PORT_FEAT_C_SUSPEND      18
>  #define USB_PORT_FEAT_C_OVER_CURRENT 19
>  #define USB_PORT_FEAT_C_RESET        20
> +#define USB_PORT_FEAT_TEST           21
>
>  /* wPortStatus bits */
>  #define USB_PORT_STAT_CONNECTION    0x0001
> --
> 1.8.1.3
>

Patch

diff --git a/common/cmd_usb.c b/common/cmd_usb.c
index dacdc2d..adc5f02 100644
--- a/common/cmd_usb.c
+++ b/common/cmd_usb.c
@@ -269,6 +269,22 @@  static void usb_display_config(struct usb_device *dev)
 	printf("\n");
 }
 
+static struct usb_device *usb_find_device(int devnum)
+{
+	struct usb_device *dev;
+	int d;
+
+	for (d = 0; d < USB_MAX_DEVICE; d++) {
+		dev = usb_get_dev_index(d);
+		if (dev == NULL)
+			return NULL;
+		if (dev->devnum == devnum)
+			return dev;
+	}
+
+	return NULL;
+}
+
 static inline char *portspeed(int speed)
 {
 	if (speed == USB_SPEED_HIGH)
@@ -348,6 +364,66 @@  static void usb_show_tree(struct usb_device *dev)
 	usb_show_tree_graph(dev, &preamble[0]);
 }
 
+static int usb_test(struct usb_device *dev, int port, char* arg)
+{
+	int mode;
+
+	if (port > dev->maxchild) {
+		printf("Device is no hub or does not have %d ports.\n", port);
+		return 1;
+	}
+
+	switch (arg[0]) {
+	case 'J':
+	case 'j':
+		printf("Setting Test_J mode");
+		mode = USB_TEST_MODE_J;
+		break;
+	case 'K':
+	case 'k':
+		printf("Setting Test_K mode");
+		mode = USB_TEST_MODE_K;
+		break;
+	case 'S':
+	case 's':
+		printf("Setting Test_SE0_NAK mode");
+		mode = USB_TEST_MODE_SE0_NAK;
+		break;
+	case 'P':
+	case 'p':
+		printf("Setting Test_Packet mode");
+		mode = USB_TEST_MODE_PACKET;
+		break;
+	case 'F':
+	case 'f':
+		printf("Setting Test_Force_Enable mode");
+		mode = USB_TEST_MODE_FORCE_ENABLE;
+		break;
+	default:
+		printf("Unrecognized test mode: %s\nAvailable modes: "
+		       "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg);
+		return 1;
+	}
+
+	if (port)
+		printf(" on downstream facing port %d...\n", port);
+	else
+		printf(" on upstream facing port...\n");
+
+	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE,
+			    port ? USB_RT_PORT : USB_RECIP_DEVICE,
+			    port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST,
+			    (mode << 8) | port,
+			    NULL, 0, USB_CNTL_TIMEOUT) == -1) {
+		printf("Error during SET_FEATURE.\n");
+		return 1;
+	} else {
+		printf("Test mode successfully set. Use 'usb start' "
+		       "to return to normal operation.\n");
+		return 0;
+	}
+}
+
 
 /******************************************************************************
  * usb boot command intepreter. Derived from diskboot
@@ -441,17 +517,9 @@  static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 			}
 			return 0;
 		} else {
-			int d;
-
-			i = simple_strtoul(argv[2], NULL, 16);
+			i = simple_strtoul(argv[2], NULL, 10);
 			printf("config for device %d\n", i);
-			for (d = 0; d < USB_MAX_DEVICE; d++) {
-				dev = usb_get_dev_index(d);
-				if (dev == NULL)
-					break;
-				if (dev->devnum == i)
-					break;
-			}
+			dev = usb_find_device(i);
 			if (dev == NULL) {
 				printf("*** No device available ***\n");
 				return 0;
@@ -462,6 +530,18 @@  static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		}
 		return 0;
 	}
+	if (strncmp(argv[1], "test", 4) == 0) {
+		if (argc < 5)
+			return CMD_RET_USAGE;
+		i = simple_strtoul(argv[2], NULL, 10);
+		dev = usb_find_device(i);
+		if (dev == NULL) {
+			printf("Device %d does not exist.\n", i);
+			return 1;
+		}
+		i = simple_strtoul(argv[3], NULL, 10);
+		return usb_test(dev, i, argv[4]);
+	}
 #ifdef CONFIG_USB_STORAGE
 	if (strncmp(argv[1], "stor", 4) == 0)
 		return usb_stor_info();
@@ -571,7 +651,6 @@  static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	return CMD_RET_USAGE;
 }
 
-#ifdef CONFIG_USB_STORAGE
 U_BOOT_CMD(
 	usb,	5,	1,	do_usb,
 	"USB sub-system",
@@ -580,30 +659,26 @@  U_BOOT_CMD(
 	"usb stop [f] - stop USB [f]=force stop\n"
 	"usb tree - show USB device tree\n"
 	"usb info [dev] - show available USB devices\n"
+	"usb test [dev] [port] [mode] - set USB 2.0 test mode\n"
+	"    (specify port 0 to indicate the device's upstream port)\n"
+	"    Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n"
+#ifdef CONFIG_USB_STORAGE
 	"usb storage - show details of USB storage devices\n"
 	"usb dev [dev] - show or set current USB storage device\n"
 	"usb part [dev] - print partition table of one or all USB storage"
-	" devices\n"
+	"    devices\n"
 	"usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
 	"    to memory address `addr'\n"
 	"usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
 	"    from memory address `addr'"
+#endif /* CONFIG_USB_STORAGE */
 );
 
 
+#ifdef CONFIG_USB_STORAGE
 U_BOOT_CMD(
 	usbboot,	3,	1,	do_usbboot,
 	"boot from USB device",
 	"loadAddr dev:part"
 );
-
-#else
-U_BOOT_CMD(
-	usb,	5,	1,	do_usb,
-	"USB sub-system",
-	"start - start (scan) USB controller\n"
-	"usb reset - reset (rescan) USB controller\n"
-	"usb tree - show USB device tree\n"
-	"usb info [dev] - show available USB devices"
-);
-#endif
+#endif /* CONFIG_USB_STORAGE */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7f98a63..32c7c1d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -602,15 +602,19 @@  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
 	int len, srclen;
 	uint32_t reg;
 	uint32_t *status_reg;
+	int port = le16_to_cpu(req->index) & 0xff;
 	struct ehci_ctrl *ctrl = dev->controller;
 
-	if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
-		printf("The request port(%d) is not configured\n",
-			le16_to_cpu(req->index) - 1);
+	if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
+		printf("The request port(%d) is not configured\n", port - 1);
 		return -1;
 	}
-	status_reg = (uint32_t *)&ctrl->hcor->or_portsc[
-						le16_to_cpu(req->index) - 1];
+	if (port && !(ctrl->port_enable_mask &
+			(1 << (port - 1)))) {
+		debug("skip request on disabled port%d\n", port - 1);
+		return 0;
+	}
+	status_reg = (uint32_t *)&hcor->or_portsc[port - 1];
 	srclen = 0;
 
 	debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
@@ -727,7 +731,7 @@  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
 			tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
 		if (reg & EHCI_PS_OCC)
 			tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
-		if (ctrl->portreset & (1 << le16_to_cpu(req->index)))
+		if (ctrl->portreset & (1 << port))
 			tmpbuf[2] |= USB_PORT_STAT_C_RESET;
 
 		srcptr = tmpbuf;
@@ -753,7 +757,7 @@  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
 			    EHCI_PS_IS_LOWSPEED(reg)) {
 				/* Low speed device, give up ownership. */
 				debug("port %d low speed --> companion\n",
-				      req->index - 1);
+				      port - 1);
 				reg |= EHCI_PS_PO;
 				ehci_writel(status_reg, reg);
 				break;
@@ -779,13 +783,17 @@  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
 				ret = handshake(status_reg, EHCI_PS_PR, 0,
 						2 * 1000);
 				if (!ret)
-					ctrl->portreset |=
-						1 << le16_to_cpu(req->index);
+					ctrl->portreset |= 1 << port;
 				else
 					printf("port(%d) reset error\n",
-					le16_to_cpu(req->index) - 1);
+					       port - 1);
 			}
 			break;
+		case USB_PORT_FEAT_TEST:
+			reg &= ~(0xf << 16);
+			reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;
+			ehci_writel(status_reg, reg);
+			break;
 		default:
 			debug("unknown feature %x\n", le16_to_cpu(req->value));
 			goto unknown;
@@ -812,7 +820,7 @@  ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
 			reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
 			break;
 		case USB_PORT_FEAT_C_RESET:
-			ctrl->portreset &= ~(1 << le16_to_cpu(req->index));
+			ctrl->portreset &= ~(1 << port);
 			break;
 		default:
 			debug("unknown feature %x\n", le16_to_cpu(req->value));
diff --git a/include/usb_defs.h b/include/usb_defs.h
index 9502544..5c5478f 100644
--- a/include/usb_defs.h
+++ b/include/usb_defs.h
@@ -150,6 +150,18 @@ 
 #define USB_REQ_SET_IDLE            0x0A
 #define USB_REQ_SET_PROTOCOL        0x0B
 
+/* Device features */
+#define USB_FEAT_HALT               0x00
+#define USB_FEAT_WAKEUP             0x01
+#define USB_FEAT_TEST               0x02
+
+/* Test modes */
+#define USB_TEST_MODE_J             0x01
+#define USB_TEST_MODE_K             0x02
+#define USB_TEST_MODE_SE0_NAK       0x03
+#define USB_TEST_MODE_PACKET        0x04
+#define USB_TEST_MODE_FORCE_ENABLE  0x05
+
 
 /* "pipe" definitions */
 
@@ -208,6 +220,7 @@ 
 #define USB_PORT_FEAT_C_SUSPEND      18
 #define USB_PORT_FEAT_C_OVER_CURRENT 19
 #define USB_PORT_FEAT_C_RESET        20
+#define USB_PORT_FEAT_TEST           21
 
 /* wPortStatus bits */
 #define USB_PORT_STAT_CONNECTION    0x0001