This patch adds USB capabilities to libpayload. It requires some
memalign implementation (eg. the one I sent yesterday).
Features:
 - UHCI controller driver
 - UHCI root hub driver
 - USB MSC (Mass Storage Class) driver
 - skeleton of a USB HID driver
   (requires better interrupt transfer handling, which is TODO)
 - skeleton of a USB hub driver
   (needs several blank spots filled in, eg. power management.
    Again: TODO)

OHCI and EHCI are not supported, though OHCI support should be rather
easy as the stack provides reasonable abstractions (or so I hope). EHCI
will probably be more complicated.

Isochronous transfers (eg. webcams, audio stuff, ...) are not supported.
They can be, but I doubt we'll have a reason for that in the boot
environment.

The MSC driver was tested against a couple of USB flash drives, and
should be reasonably tolerant by now. But I probably underestimate
the amount of bugs present in USB flash drives, so feedback is welcome.

Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de>
Acked-by: Jordan Crouse <jordan.crouse@amd.com>


git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3560 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c
new file mode 100644
index 0000000..fc4de87
--- /dev/null
+++ b/payloads/libpayload/drivers/usb/usb.c
@@ -0,0 +1,350 @@
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include "usb.h"
+
+hci_t *usb_hcs = 0;
+
+hci_t *
+new_controller ()
+{
+	hci_t *controller = malloc (sizeof (hci_t));
+
+	/* atomic */
+	controller->next = usb_hcs;
+	usb_hcs = controller;
+	/* atomic end */
+
+	return controller;
+}
+
+void
+detach_controller (hci_t *controller)
+{
+	if (controller == 0)
+		return;
+	if (usb_hcs == controller) {
+		usb_hcs = controller->next;
+	} else {
+		hci_t *it = usb_hcs;
+		while (it != 0) {
+			if (it->next == controller) {
+				it->next = controller->next;
+				return;
+			}
+		}
+	}
+}
+
+/**
+ * Polls all hubs on all USB controllers, to find out about device changes
+ */
+void
+usb_poll ()
+{
+	if (usb_hcs == 0)
+		return;
+	hci_t *controller = usb_hcs;
+	while (controller != 0) {
+		int i;
+		for (i = 0; i < 128; i++) {
+			if (controller->devices[i].address != -1) {
+				controller->devices[i].poll (&controller->
+							     devices[i]);
+			}
+		}
+		controller = controller->next;
+	}
+}
+
+void
+init_device_entry (hci_t *controller, int i)
+{
+	controller->devices[i].controller = controller;
+	controller->devices[i].address = -1;
+	controller->devices[i].hub = -1;
+	controller->devices[i].port = -1;
+	controller->devices[i].init = usb_nop_init;
+	controller->devices[i].init (&controller->devices[i]);
+}
+
+void
+set_feature (usbdev_t *dev, int endp, int feature, int rtype)
+{
+	dev_req_t dr;
+
+	dr.bmRequestType = rtype;
+	dr.data_dir = host_to_device;
+	dr.bRequest = SET_FEATURE;
+	dr.wValue = feature;
+	dr.wIndex = endp;
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+void
+get_status (usbdev_t *dev, int intf, int rtype, int len, void *data)
+{
+	dev_req_t dr;
+
+	dr.bmRequestType = rtype;
+	dr.data_dir = device_to_host;
+	dr.bRequest = GET_STATUS;
+	dr.wValue = 0;
+	dr.wIndex = intf;
+	dr.wLength = len;
+	dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
+}
+
+u8 *
+get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
+		int descIdx, int langID)
+{
+	u8 buf[8];
+	u8 *result;
+	dev_req_t dr;
+	int size;
+
+	dr.bmRequestType = bmRequestType;
+	dr.data_dir = device_to_host;	// always like this for descriptors
+	dr.bRequest = GET_DESCRIPTOR;
+	dr.wValue = (descType << 8) | descIdx;
+	dr.wIndex = langID;
+	dr.wLength = 8;
+	if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
+		printf ("getting descriptor size (type %x) failed\n",
+			descType);
+	}
+
+	if (descType == 1) {
+		device_descriptor_t *dd = (device_descriptor_t *) buf;
+		printf ("maxPacketSize0: %x\n", dd->bMaxPacketSize0);
+		if (dd->bMaxPacketSize0 != 0)
+			dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
+	}
+
+	/* special case for configuration descriptors: they carry all their
+	   subsequent descriptors with them, and keep the entire size at a
+	   different location */
+	size = buf[0];
+	if (buf[1] == 2) {
+		int realsize = ((unsigned short *) (buf + 2))[0];
+		size = realsize;
+	}
+	result = malloc (size);
+	memset (result, 0, size);
+	dr.wLength = size;
+	if (dev->controller->
+	    control (dev, IN, sizeof (dr), &dr, size, result)) {
+		printf ("getting descriptor (type %x, size %x) failed\n",
+			descType, size);
+	}
+
+	return result;
+}
+
+void
+set_configuration (usbdev_t *dev)
+{
+	dev_req_t dr;
+
+	dr.bmRequestType = 0;
+	dr.bRequest = SET_CONFIGURATION;
+	dr.wValue = dev->configuration[5];
+	dr.wIndex = 0;
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+int
+clear_stall (endpoint_t *ep)
+{
+	usbdev_t *dev = ep->dev;
+	int endp = ep->endpoint;
+	dev_req_t dr;
+
+	dr.bmRequestType = 0;
+	if (endp != 0) {
+		dr.req_recp = endp_recp;
+	}
+	dr.bRequest = CLEAR_FEATURE;
+	dr.wValue = ENDPOINT_HALT;
+	dr.wIndex = endp;
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+	return 0;
+}
+
+/* returns free address or -1 */
+static int
+get_free_address (hci_t *controller)
+{
+	int i;
+	for (i = 1; i < 128; i++) {
+		if (controller->devices[i].address != i)
+			return i;
+	}
+	printf ("no free address found\n");
+	return -1;		// no free address
+}
+
+int
+set_address (hci_t *controller, int lowspeed)
+{
+	int adr = get_free_address (controller);	// address to set
+	dev_req_t dr;
+	configuration_descriptor_t *cd;
+	device_descriptor_t *dd;
+
+	memset (&dr, 0, sizeof (dr));
+	dr.data_dir = host_to_device;
+	dr.req_type = standard_type;
+	dr.req_recp = dev_recp;
+	dr.bRequest = SET_ADDRESS;
+	dr.wValue = adr;
+	dr.wIndex = 0;
+	dr.wLength = 0;
+
+	usbdev_t *dev = &controller->devices[adr];
+	// dummy values for registering the address
+	dev->address = 0;
+	dev->lowspeed = lowspeed;
+	dev->endpoints[0].dev = dev;
+	dev->endpoints[0].endpoint = 0;
+	dev->endpoints[0].maxpacketsize = 8;
+	dev->endpoints[0].toggle = 0;
+	dev->endpoints[0].direction = SETUP;
+	mdelay (50);
+	if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) {
+		printf ("set_address failed\n");
+		return -1;
+	}
+	mdelay (50);
+	dev->address = adr;
+	dev->descriptor =
+		get_descriptor (dev,
+				gen_bmRequestType (device_to_host,
+						   standard_type, dev_recp),
+				1, 0, 0);
+	dd = (device_descriptor_t *) dev->descriptor;
+	printf ("device version: %x.%x\n", dd->bcdUSB >> 8,
+		dd->bcdUSB & 0xff);
+	printf ("device has %x configurations\n", dd->bNumConfigurations);
+	if (dd->bNumConfigurations == 0) {
+		/* device isn't usable */
+		printf ("no usable configuration!\n");
+		dev->address = 0;
+		return -1;
+	}
+	dev->configuration =
+		get_descriptor (dev,
+				gen_bmRequestType (device_to_host,
+						   standard_type, dev_recp),
+				2, 0, 0);
+	cd = (configuration_descriptor_t *) dev->configuration;
+	set_configuration (dev);
+	interface_descriptor_t *interface =
+		(interface_descriptor_t *) (((char *) cd) + cd->bLength);
+	{
+		int i;
+		int num = cd->bNumInterfaces;
+		interface_descriptor_t *current = interface;
+		printf ("device has %x interfaces\n", num);
+		num = (num > 5) ? 5 : num;
+		for (i = 0; i < num; i++) {
+			int j;
+			printf (" #%x has %x endpoints, interface %x:%x, protocol %x\n", current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol);
+			endpoint_descriptor_t *endp =
+				(endpoint_descriptor_t *) (((char *) current)
+							   +
+							   current->bLength);
+			if (interface->bInterfaceClass == 0x3)
+				endp = (endpoint_descriptor_t *) (((char *) endp) + ((char *) endp)[0]);	// ignore HID descriptor
+			memset (dev->endpoints, 0, sizeof (dev->endpoints));
+			dev->num_endp = 1;	// 0 always exists
+			dev->endpoints[0].dev = dev;
+			dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
+			dev->endpoints[0].direction = SETUP;
+			dev->endpoints[0].type = CONTROL;
+			for (j = 1; j <= current->bNumEndpoints; j++) {
+				static const char *transfertypes[4] =
+					{ "control", "isochronous", "bulk",
+					"interrupt"
+				};
+				printf ("   #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", endp->wMaxPacketSize, transfertypes[endp->bmAttributes]);
+				endpoint_t *ep =
+					&dev->endpoints[dev->num_endp++];
+				ep->dev = dev;
+				ep->endpoint = endp->bEndpointAddress;
+				ep->toggle = 0;
+				ep->maxpacketsize = endp->wMaxPacketSize;
+				ep->direction =
+					((endp->bEndpointAddress & 0x80) ==
+					 0) ? OUT : IN;
+				ep->type = endp->bmAttributes;
+				endp = (endpoint_descriptor_t
+					*) (((char *) endp) + endp->bLength);
+			}
+			current = (interface_descriptor_t *) endp;
+		}
+	}
+	int class = dd->bDeviceClass;
+	if (class == 0)
+		class = interface->bInterfaceClass;
+
+	enum { hid_device = 0x3, msc_device = 0x8, hub_device = 0x9 };
+
+	printf ("device of class %x found\n", class);
+	if (class == hub_device) {
+		printf ("hub found\n");
+#ifdef CONFIG_USB_HUB
+		controller->devices[adr].init = usb_hub_init;
+#else
+		printf ("support not compiled in\n");
+#endif
+	}
+	if (class == hid_device) {
+		printf ("HID found\n");
+#ifdef CONFIG_USB_HID
+		controller->devices[adr].init = usb_hid_init;
+#else
+		printf ("support not compiled in\n");
+#endif
+	}
+	if (class == msc_device) {
+		printf ("MSC found\n");
+#ifdef CONFIG_USB_MSC
+		controller->devices[adr].init = usb_msc_init;
+#else
+		printf ("support not compiled in\n");
+#endif
+	}
+	return adr;
+}