usbdebug: Refactor descriptor probing of dongle

Organized such that it is easy to support devices that do not
export special Debug Descriptor. Some of these can still work
in a fixed configuration and/or require additional initialisation
for UART clocks etc.

Change-Id: Id07fd6b69007332d67d9e9a456f58fdbca1999cd
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: http://review.coreboot.org/7209
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins)
diff --git a/src/drivers/usb/gadget.c b/src/drivers/usb/gadget.c
index d3b4963..5dd7993 100644
--- a/src/drivers/usb/gadget.c
+++ b/src/drivers/usb/gadget.c
@@ -17,11 +17,13 @@
 
 #include <stddef.h>
 #include <console/console.h>
+#include <string.h>
 
 #include "ehci_debug.h"
 #include "usb_ch9.h"
 #include "ehci.h"
 
+#define dprintk printk
 
 #define USB_HUB_PORT_CONNECTION		0
 #define USB_HUB_PORT_ENABLED		1
@@ -120,6 +122,108 @@
 	return -1;
 }
 
+static void ack_set_configuration(struct dbgp_pipe *pipe, u8 devnum, int timeout)
+{
+	int i = DBGP_SETUP_EP0;
+	while (++i < DBGP_MAX_ENDPOINTS) {
+		if (pipe[i].endpoint != 0) {
+			pipe[i].devnum = devnum;
+			pipe[i].pid = USB_PID_DATA0;
+			pipe[i].timeout = timeout;
+		}
+	}
+}
+
+static void activate_endpoints(struct dbgp_pipe *pipe)
+{
+	int i = DBGP_SETUP_EP0;
+	pipe[i].status |= DBGP_EP_ENABLED | DBGP_EP_VALID;
+	while (++i < DBGP_MAX_ENDPOINTS) {
+		if (pipe[i].endpoint != 0)
+			pipe[i].status |= DBGP_EP_ENABLED | DBGP_EP_VALID;
+	}
+}
+
+static int probe_for_debug_descriptor(struct ehci_dbg_port *ehci_debug, struct dbgp_pipe *pipe)
+{
+	struct usb_debug_descriptor dbgp_desc;
+	int configured = 0, ret;
+	u8 devnum = 0;
+
+	/* Find the debug device and make it device number 127 */
+debug_dev_retry:
+	memset(&dbgp_desc, 0, sizeof(dbgp_desc));
+	ret = dbgp_control_msg(ehci_debug, devnum,
+		USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+		USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0,
+		&dbgp_desc, sizeof(dbgp_desc));
+	if (ret == sizeof(dbgp_desc)) {
+		if (dbgp_desc.bLength == sizeof(dbgp_desc) && dbgp_desc.bDescriptorType==USB_DT_DEBUG)
+			goto debug_dev_found;
+		else
+			dprintk(BIOS_INFO, "Invalid debug device descriptor.\n");
+	}
+	if (devnum == 0) {
+		devnum = USB_DEBUG_DEVNUM;
+		goto debug_dev_retry;
+	} else {
+		dprintk(BIOS_INFO, "Could not find attached debug device.\n");
+		return -1;
+	}
+debug_dev_found:
+
+	/* Move the device to 127 if it isn't already there */
+	if (devnum != USB_DEBUG_DEVNUM) {
+		ret = dbgp_control_msg(ehci_debug, devnum,
+			USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+			USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0);
+		if (ret < 0) {
+			dprintk(BIOS_INFO, "Could not move attached device to %d.\n",
+				USB_DEBUG_DEVNUM);
+			return -2;
+		}
+		devnum = USB_DEBUG_DEVNUM;
+		dprintk(BIOS_INFO, "EHCI debug device renamed to 127.\n");
+	}
+
+	/* Enable the debug interface */
+	ret = dbgp_control_msg(ehci_debug, USB_DEBUG_DEVNUM,
+		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+		USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0);
+	if (ret < 0) {
+		dprintk(BIOS_INFO, "Could not enable EHCI debug device.\n");
+		return -3;
+	}
+	dprintk(BIOS_INFO, "EHCI debug interface enabled.\n");
+
+	pipe[DBGP_CONSOLE_EPOUT].endpoint = dbgp_desc.bDebugOutEndpoint;
+	pipe[DBGP_CONSOLE_EPIN].endpoint = dbgp_desc.bDebugInEndpoint;
+
+	ack_set_configuration(pipe, devnum, 1000);
+
+	/* Perform a small write. */
+	configured = 0;
+small_write:
+	ret = dbgp_bulk_write_x(&pipe[DBGP_CONSOLE_EPOUT], "USB\r\n",5);
+	if (ret < 0) {
+		dprintk(BIOS_INFO, "dbgp_bulk_write failed: %d\n", ret);
+		if (!configured) {
+			/* Send Set Configure request to device. This is required for FX2
+			   (CY7C68013) to transfer from USB state Addressed to Configured,
+			   only then endpoints other than 0 are enabled. */
+			if (dbgp_control_msg(ehci_debug, USB_DEBUG_DEVNUM,
+				USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+				USB_REQ_SET_CONFIGURATION, 1, 0, NULL, 0) >= 0) {
+				configured = 1;
+				goto small_write;
+			}
+		}
+		return -4;
+	}
+	dprintk(BIOS_INFO, "Test write done\n");
+	return 0;
+}
+
 
 int dbgp_probe_gadget(struct ehci_dbg_port *ehci_debug, struct dbgp_pipe *pipe)
 {
@@ -134,5 +238,12 @@
 		}
 	}
 
+	ret = probe_for_debug_descriptor(ehci_debug, pipe);
+	if (ret < 0) {
+		dprintk(BIOS_INFO, "Could not enable gadget using Debug Descriptor.\n");
+		return ret;
+	}
+
+	activate_endpoints(pipe);
 	return 0;
 }