[OpenBIOS] [PATCH] Add USB OHCI + HID driver

BALATON Zoltan balaton at eik.bme.hu
Sun Jun 1 23:55:19 CEST 2014


This driver is ported from CoreBoot's libpayload and fixed to work on
big endian CPUs (tested on PPC with QEMU). It is enough to support a
USB keyboard on an Apple Keylargo OHCI compliant USB host and makes
OpenBIOS usable on qemu-system-ppc64 with mac99 machine type as well
as allows to emulate PowerMac3,1 better.

Signed-off-by: BALATON Zoltan <balaton at eik.bme.hu>
---
 openbios-devel/config/examples/ppc_config.xml |   3 +
 openbios-devel/drivers/Kconfig                |  20 +
 openbios-devel/drivers/build.xml              |   4 +
 openbios-devel/drivers/pci.c                  |  18 +
 openbios-devel/drivers/pci.h                  |   1 +
 openbios-devel/drivers/pci_database.c         |  18 +-
 openbios-devel/drivers/pci_database.h         |   1 +
 openbios-devel/drivers/usb.c                  | 587 ++++++++++++++++
 openbios-devel/drivers/usb.h                  | 357 ++++++++++
 openbios-devel/drivers/usbhid.c               | 579 ++++++++++++++++
 openbios-devel/drivers/usbohci.c              | 920 ++++++++++++++++++++++++++
 openbios-devel/drivers/usbohci.h              |  45 ++
 openbios-devel/drivers/usbohci_private.h      | 270 ++++++++
 openbios-devel/drivers/usbohci_rh.c           | 212 ++++++
 openbios-devel/include/drivers/pci.h          |   1 +
 openbios-devel/include/drivers/usb.h          |   8 +
 16 files changed, 3043 insertions(+), 1 deletion(-)
 create mode 100644 openbios-devel/drivers/usb.c
 create mode 100644 openbios-devel/drivers/usb.h
 create mode 100644 openbios-devel/drivers/usbhid.c
 create mode 100644 openbios-devel/drivers/usbohci.c
 create mode 100644 openbios-devel/drivers/usbohci.h
 create mode 100644 openbios-devel/drivers/usbohci_private.h
 create mode 100644 openbios-devel/drivers/usbohci_rh.c
 create mode 100644 openbios-devel/include/drivers/usb.h

diff --git a/openbios-devel/config/examples/ppc_config.xml b/openbios-devel/config/examples/ppc_config.xml
index 621b65d..4c14eb6 100644
--- a/openbios-devel/config/examples/ppc_config.xml
+++ b/openbios-devel/config/examples/ppc_config.xml
@@ -80,3 +80,6 @@
   <option name="CONFIG_DRIVER_ESCC" type="boolean" value="true"/>
   <option name="CONFIG_DRIVER_FW_CFG" type="boolean" value="true"/>
   <option name="CONFIG_FW_CFG_ADDR" type="integer" value="0xf0000510"/>
+  <option name="CONFIG_DRIVER_USB" type="boolean" value="true"/>
+  <option name="CONFIG_DEBUG_USB" type="boolean" value="false"/>
+  <option name="CONFIG_USB_HID" type="boolean" value="true"/>
diff --git a/openbios-devel/drivers/Kconfig b/openbios-devel/drivers/Kconfig
index 7be334b..ce7e7ac 100644
--- a/openbios-devel/drivers/Kconfig
+++ b/openbios-devel/drivers/Kconfig
@@ -36,4 +36,24 @@ config DEBUG_IDE
 	help
 	  Debug IDE driver 
 	
+config DRIVER_USB
+	bool "USB Support"
+	default n
+	help
+	  If you want to be able to use USB devices, enable this option.
+
+config DEBUG_USB
+	depends DRIVER_USB
+	bool "Debug USB driver"
+	default n
+	help
+	  Debug USB driver
+	  
+config USB_HID
+	depends DRIVER_USB
+	bool "USB driver for HID devices"
+	default n
+	help
+	  If you want to be able to use USB keyboard, enable this option.
+	  
 endmenu
diff --git a/openbios-devel/drivers/build.xml b/openbios-devel/drivers/build.xml
index edec6b5..bd1abd3 100644
--- a/openbios-devel/drivers/build.xml
+++ b/openbios-devel/drivers/build.xml
@@ -22,6 +22,10 @@
   <object source="pc_serial.c" condition="DRIVER_PC_SERIAL"/>
   <object source="escc.c" condition="DRIVER_ESCC"/>
   <object source="fw_cfg.c" condition="DRIVER_FW_CFG"/>
+  <object source="usb.c" condition="DRIVER_USB"/>
+  <object source="usbhid.c" condition="USB_HID"/>
+  <object source="usbohci.c" condition="DRIVER_USB"/>
+  <object source="usbohci_rh.c" condition="DRIVER_USB"/>
  </library>
 
  <dictionary name="openbios" target="forth">
diff --git a/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c
index ca92d63..ca4de87 100644
--- a/openbios-devel/drivers/pci.c
+++ b/openbios-devel/drivers/pci.c
@@ -34,6 +34,9 @@
 #include "cuda.h"
 #include "macio.h"
 #endif
+#ifdef CONFIG_DRIVER_USB
+#include "drivers/usb.h"
+#endif
 
 #if defined (CONFIG_DEBUG_PCI)
 # define PCI_DPRINTF(format, ...) printk(format, ## __VA_ARGS__)
@@ -883,6 +886,21 @@ int i82378_config_cb(const pci_config_t *config)
     return 0;
 }
 
+int usb_ohci_config_cb(const pci_config_t *config)
+{
+#ifdef CONFIG_DRIVER_USB
+    pci_addr addr = 0x80000000u | config->dev;
+    uint16_t cmd;
+
+    cmd = pci_config_read16(addr, PCI_COMMAND);
+    cmd |= PCI_COMMAND_BUS_MASTER;
+    pci_config_write16(addr, PCI_COMMAND, cmd);
+
+    ob_usb_ohci_init(config->path, config->assigned[0] & ~0x0000000F);
+#endif
+    return 0;
+}
+
 static void ob_pci_add_properties(phandle_t phandle,
                                   pci_addr addr, const pci_dev_t *pci_dev,
                                   const pci_config_t *config, int num_bars)
diff --git a/openbios-devel/drivers/pci.h b/openbios-devel/drivers/pci.h
index ab8f184..84a2b2c 100644
--- a/openbios-devel/drivers/pci.h
+++ b/openbios-devel/drivers/pci.h
@@ -7,6 +7,7 @@
 #define PCI_COMMAND		0x04
 #define  PCI_COMMAND_IO		0x01
 #define  PCI_COMMAND_MEMORY	0x02
+#define  PCI_COMMAND_BUS_MASTER	0x04
 
 #define PCI_STATUS              0x06    /* 16 bits */
 #define  PCI_STATUS_CAP_LIST    0x10    /* Support Capability List */
diff --git a/openbios-devel/drivers/pci_database.c b/openbios-devel/drivers/pci_database.c
index 0bd854a..3b670d3 100644
--- a/openbios-devel/drivers/pci_database.c
+++ b/openbios-devel/drivers/pci_database.c
@@ -845,6 +845,22 @@ static const pci_subclass_t cpu_subclass[] = {
     },
 };
 
+static const pci_dev_t usb_devices[] = {
+    {
+        PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KEYL_USB,
+        "usb", "usb", NULL,
+	"pci106b,3f\0pciclass,0c0310\0",
+        1, 0, 0,
+        NULL, NULL,
+    },
+    {
+        0xFFFF, 0xFFFF,
+        NULL, NULL, NULL, NULL,
+        -1, -1, -1,
+        NULL, NULL,
+    },
+};
+
 static const pci_iface_t usb_iface[] = {
     {
         0x00, "UHCI USB controller", NULL,
@@ -852,7 +868,7 @@ static const pci_iface_t usb_iface[] = {
     },
     {
         0x10, "OHCI USB controller", NULL,
-        NULL, NULL, NULL,
+        usb_devices, &usb_ohci_config_cb, NULL,
     },
     {
         0x20, "EHCI USB controller", NULL,
diff --git a/openbios-devel/drivers/pci_database.h b/openbios-devel/drivers/pci_database.h
index 16b385b..7f053d8 100644
--- a/openbios-devel/drivers/pci_database.h
+++ b/openbios-devel/drivers/pci_database.h
@@ -38,6 +38,7 @@ extern int sabre_config_cb(const pci_config_t *config);
 extern int bridge_config_cb(const pci_config_t *config);
 extern int ebus_config_cb(const pci_config_t *config);
 extern int i82378_config_cb(const pci_config_t *config);
+extern int usb_ohci_config_cb(const pci_config_t *config);
 
 static inline int pci_compat_len(const pci_dev_t *dev)
 {
diff --git a/openbios-devel/drivers/usb.c b/openbios-devel/drivers/usb.c
new file mode 100644
index 0000000..c6e3717
--- /dev/null
+++ b/openbios-devel/drivers/usb.c
@@ -0,0 +1,587 @@
+/*
+ * Driver for USB ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2008-2010 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 "drivers/usb.h"
+#include "usb.h"
+#include "timer.h"
+#include "libc/byteorder.h"
+
+hci_t *usb_hcs = 0;
+
+static void usb_nop_init (usbdev_t *dev);
+
+static void
+usb_nop_destroy (usbdev_t *dev)
+{
+	if (dev->descriptor != 0)
+		free (dev->descriptor);
+	usb_nop_init (dev);
+	dev->address = -1;
+	dev->hub = -1;
+	dev->port = -1;
+}
+
+static void
+usb_nop_poll (usbdev_t *dev)
+{
+	return;
+}
+
+static void
+usb_nop_init (usbdev_t *dev)
+{
+	dev->descriptor = 0;
+	dev->destroy = usb_nop_destroy;
+	dev->poll = usb_nop_poll;
+}
+
+hci_t *
+new_controller (void)
+{
+	hci_t *controller = malloc (sizeof (hci_t));
+
+	if (controller) {
+		/* atomic */
+		controller->next = usb_hcs;
+		usb_hcs = controller;
+		/* atomic end */
+	}
+
+	return controller;
+}
+
+void
+detach_controller (hci_t *controller)
+{
+	if (controller == NULL)
+		return;
+	if (usb_hcs == controller) {
+		usb_hcs = controller->next;
+	} else {
+		hci_t *it = usb_hcs;
+		while (it != NULL) {
+			if (it->next == controller) {
+				it->next = controller->next;
+				return;
+			}
+			it = it->next;
+		}
+	}
+}
+
+/**
+ * Shut down all controllers
+ */
+int
+usb_exit (void)
+{
+	while (usb_hcs != NULL) {
+		usb_hcs->shutdown(usb_hcs);
+	}
+	return 0;
+}
+
+/**
+ * Polls all hubs on all USB controllers, to find out about device changes
+ */
+void
+usb_poll (void)
+{
+	if (usb_hcs == 0)
+		return;
+	hci_t *controller = usb_hcs;
+	while (controller != NULL) {
+		int i;
+		for (i = 0; i < 128; i++) {
+			if (controller->devices[i] != 0) {
+				controller->devices[i]->poll (controller->devices[i]);
+			}
+		}
+		controller = controller->next;
+	}
+}
+
+void
+init_device_entry (hci_t *controller, int i)
+{
+	if (controller->devices[i] != 0)
+		usb_debug("warning: device %d reassigned?\n", i);
+	controller->devices[i] = malloc(sizeof(usbdev_t));
+	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 = __cpu_to_le16(feature);
+	dr.wIndex = __cpu_to_le16(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 = __cpu_to_le16(intf);
+	dr.wLength = __cpu_to_le16(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 = __cpu_to_le16((descType << 8) | descIdx);
+	dr.wIndex = __cpu_to_le16(langID);
+	dr.wLength = __cpu_to_le16(8);
+	if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
+		usb_debug ("getting descriptor size (type %x) failed\n",
+			descType);
+	}
+
+	if (descType == 1) {
+		device_descriptor_t *dd = (device_descriptor_t *) buf;
+		usb_debug ("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 = __le16_to_cpu(((unsigned short *) (buf + 2))[0]);
+		size = realsize;
+	}
+	result = malloc (size);
+	memset (result, 0, size);
+	dr.wLength = __cpu_to_le16(size);
+	if (dev->controller->
+	    control (dev, IN, sizeof (dr), &dr, size, result)) {
+		usb_debug ("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 = __cpu_to_le16(dev->configuration[5]);
+	dr.wIndex = 0;
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+int
+clear_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 = CLEAR_FEATURE;
+	dr.wValue = __cpu_to_le16(feature);
+	dr.wIndex = __cpu_to_le16(endp);
+	dr.wLength = 0;
+	return 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;
+	int rtype = gen_bmRequestType (host_to_device, standard_type,
+					endp ? endp_recp : dev_recp);
+
+	int ret = clear_feature (dev, endp, ENDPOINT_HALT, rtype);
+	ep->toggle = 0;
+	return ret;
+}
+
+/* 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] == 0)
+			return i;
+	}
+	usb_debug ("no free address found\n");
+	return -1;		// no free address
+}
+
+int
+generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+{
+	int adr = get_free_address (controller);	// address to set
+	dev_req_t dr;
+
+	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 = __cpu_to_le16(adr);
+	dr.wIndex = 0;
+	dr.wLength = 0;
+
+	init_device_entry(controller, adr);
+	usbdev_t *dev = controller->devices[adr];
+	// dummy values for registering the address
+	dev->address = 0;
+	dev->hub = hubaddr;
+	dev->port = hubport;
+	dev->speed = speed;
+	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)) {
+		return -1;
+	}
+	mdelay (50);
+
+	return adr;
+}
+
+/* Normalize bInterval to log2 of microframes */
+static int
+usb_decode_interval(const int speed, const endpoint_type type, const unsigned char bInterval)
+{
+#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1)
+	switch (speed) {
+	case LOW_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return LOG2(bInterval) + 3;
+		default:
+			return 0;
+		}
+	case FULL_SPEED:
+		switch (type) {
+		case ISOCHRONOUS:
+			return (bInterval - 1) + 3;
+		case INTERRUPT:
+			return LOG2(bInterval) + 3;
+		default:
+			return 0;
+		}
+	case HIGH_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return bInterval - 1;
+		default:
+			return LOG2(bInterval);
+		}
+	case SUPER_SPEED:
+		switch (type) {
+		case ISOCHRONOUS: case INTERRUPT:
+			return bInterval - 1;
+		default:
+			return 0;
+		}
+	default:
+		return 0;
+	}
+#undef LOG2
+}
+
+static int
+set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+{
+	int adr = controller->set_address(controller, speed, hubport, hubaddr);
+	if (adr < 0 || !controller->devices[adr]) {
+		usb_debug ("set_address failed\n");
+		return -1;
+	}
+	configuration_descriptor_t *cd;
+	device_descriptor_t *dd;
+
+	usbdev_t *dev = controller->devices[adr];
+	dev->address = adr;
+	dev->hub = hubaddr;
+	dev->port = hubport;
+	dev->speed = speed;
+	dev->descriptor = get_descriptor (dev, gen_bmRequestType
+		(device_to_host, standard_type, dev_recp), 1, 0, 0);
+	dd = (device_descriptor_t *) dev->descriptor;
+
+	usb_debug ("* found device (0x%04x:0x%04x, USB %x.%x)",
+		 __le16_to_cpu(dd->idVendor), __le16_to_cpu(dd->idProduct),
+		 __le16_to_cpu(dd->bcdUSB) >> 8, __le16_to_cpu(dd->bcdUSB) & 0xff);
+	dev->quirks = USB_QUIRK_NONE;
+
+	usb_debug ("\ndevice has %x configurations\n", dd->bNumConfigurations);
+	if (dd->bNumConfigurations == 0) {
+		/* device isn't usable */
+		usb_debug ("... 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;
+	interface_descriptor_t *interface =
+		(interface_descriptor_t *) (((char *) cd) + cd->bLength);
+	{
+		int i;
+		int num = cd->bNumInterfaces;
+		interface_descriptor_t *current = interface;
+		usb_debug ("device has %x interfaces\n", num);
+		if (num > 1) {
+			usb_debug ("\nNOTICE: This driver defaults to using the first interface.\n"
+				   "This might be the wrong choice and lead to limited functionality\n"
+				   "of the device.\n");
+			 /* we limit to the first interface, as there was no need to
+			 * implement something else for the time being. If you need
+			 * it, see the SetInterface and GetInterface functions in
+			 * the USB specification, and adapt appropriately.
+			 */
+			num = (num > 1) ? 1 : num;
+		}
+		for (i = 0; i < num; i++) {
+			int j;
+			usb_debug (" #%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);
+			/* Skip any non-endpoint descriptor */
+			if (endp->bDescriptorType != 0x05)
+				endp = (endpoint_descriptor_t *)(((char *)endp) + ((char *)endp)[0]);
+
+			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;
+			dev->endpoints[0].interval = usb_decode_interval(dev->speed, CONTROL, endp->bInterval);
+			for (j = 1; j <= current->bNumEndpoints; j++) {
+#ifdef CONFIG_DEBUG_USB
+				static const char *transfertypes[4] = {
+					"control", "isochronous", "bulk", "interrupt"
+				};
+				usb_debug ("   #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", __le16_to_cpu(endp->wMaxPacketSize), transfertypes[endp->bmAttributes]);
+#endif
+				endpoint_t *ep =
+					&dev->endpoints[dev->num_endp++];
+				ep->dev = dev;
+				ep->endpoint = endp->bEndpointAddress;
+				ep->toggle = 0;
+				ep->maxpacketsize = __le16_to_cpu(endp->wMaxPacketSize);
+				ep->direction =
+					((endp->bEndpointAddress & 0x80) ==
+					 0) ? OUT : IN;
+				ep->type = endp->bmAttributes;
+				ep->interval = usb_decode_interval(dev->speed, ep->type, endp->bInterval);
+				endp = (endpoint_descriptor_t
+					*) (((char *) endp) + endp->bLength);
+			}
+			current = (interface_descriptor_t *) endp;
+		}
+	}
+
+	if (controller->finish_device_config &&
+			controller->finish_device_config(dev))
+		return adr; /* Device isn't configured correctly,
+			       only control transfers may work. */
+
+	set_configuration(dev);
+
+	int class = dd->bDeviceClass;
+	if (class == 0)
+		class = interface->bInterfaceClass;
+
+	usb_debug(", class: ");
+	switch (class) {
+	case audio_device:
+		usb_debug("audio\n");
+		break;
+	case comm_device:
+		usb_debug("communication\n");
+		break;
+	case hid_device:
+		usb_debug ("HID\n");
+#ifdef CONFIG_USB_HID
+		controller->devices[adr]->init = usb_hid_init;
+		return adr;
+#else
+		usb_debug ("NOTICE: USB HID support not compiled in\n");
+#endif
+		break;
+	case physical_device:
+		usb_debug("physical\n");
+		break;
+	case imaging_device:
+		usb_debug("camera\n");
+		break;
+	case printer_device:
+		usb_debug("printer\n");
+		break;
+	case msc_device:
+		usb_debug ("MSC\n");
+#ifdef CONFIG_USB_MSC
+		controller->devices[adr]->init = usb_msc_init;
+		return adr;
+#else
+		usb_debug ("NOTICE: USB MSC support not compiled in\n");
+#endif
+		break;
+	case hub_device:
+		usb_debug ("hub\n");
+#ifdef CONFIG_USB_HUB
+		controller->devices[adr]->init = usb_hub_init;
+		return adr;
+#else
+		usb_debug ("NOTICE: USB hub support not compiled in.\n");
+#endif
+		break;
+	case cdc_device:
+		usb_debug("CDC\n");
+		break;
+	case ccid_device:
+		usb_debug("smartcard / CCID\n");
+		break;
+	case security_device:
+		usb_debug("content security\n");
+		break;
+	case video_device:
+		usb_debug("video\n");
+		break;
+	case healthcare_device:
+		usb_debug("healthcare\n");
+		break;
+	case diagnostic_device:
+		usb_debug("diagnostic\n");
+		break;
+	case wireless_device:
+		usb_debug("wireless\n");
+		break;
+	default:
+		usb_debug("unsupported class %x\n", class);
+		break;
+	}
+	controller->devices[adr]->init = usb_generic_init;
+	return adr;
+}
+
+/*
+ * Should be called by the hub drivers whenever a physical detach occurs
+ * and can be called by usb class drivers if they are unsatisfied with a
+ * malfunctioning device.
+ */
+void
+usb_detach_device(hci_t *controller, int devno)
+{
+	/* check if device exists, as we may have
+	   been called yet by the usb class driver */
+	if (controller->devices[devno]) {
+		controller->devices[devno]->destroy (controller->devices[devno]);
+		free(controller->devices[devno]);
+		controller->devices[devno] = NULL;
+		if (controller->destroy_device)
+			controller->destroy_device(controller, devno);
+	}
+}
+
+int
+usb_attach_device(hci_t *controller, int hubaddress, int port, int speed)
+{
+#ifdef CONFIG_USB_DEBUG
+	static const char* speeds[] = { "full", "low", "high" };
+	usb_debug ("%sspeed device\n", (speed <= 2) ? speeds[speed] : "invalid value - no");
+#endif
+	int newdev = set_address (controller, speed, port, hubaddress);
+	if (newdev == -1)
+		return -1;
+	usbdev_t *newdev_t = controller->devices[newdev];
+	// determine responsible driver - current done in set_address
+	newdev_t->init (newdev_t);
+	/* init() may have called usb_detach_device() yet, so check */
+	return controller->devices[newdev] ? newdev : -1;
+}
+
+static void
+usb_generic_destroy (usbdev_t *dev)
+{
+	if (usb_generic_remove)
+		usb_generic_remove(dev);
+}
+
+void
+usb_generic_init (usbdev_t *dev)
+{
+	dev->data = NULL;
+	dev->destroy = usb_generic_destroy;
+
+	if (usb_generic_create)
+		usb_generic_create(dev);
+}
diff --git a/openbios-devel/drivers/usb.h b/openbios-devel/drivers/usb.h
new file mode 100644
index 0000000..2e23a13
--- /dev/null
+++ b/openbios-devel/drivers/usb.h
@@ -0,0 +1,357 @@
+/*
+ * Driver for USB ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was 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.
+ */
+
+#ifndef __USB_H
+#define __USB_H
+#include <drivers/pci.h>
+
+typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir;
+typedef enum { standard_type = 0, class_type = 1, vendor_type =
+		2, reserved_type = 3
+} dev_req_type;
+typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3
+} dev_req_recp;
+
+typedef enum {
+	GET_STATUS = 0,
+	CLEAR_FEATURE = 1,
+	SET_FEATURE = 3,
+	SET_ADDRESS = 5,
+	GET_DESCRIPTOR = 6,
+	SET_DESCRIPTOR = 7,
+	GET_CONFIGURATION = 8,
+	SET_CONFIGURATION = 9,
+	GET_INTERFACE = 10,
+	SET_INTERFACE = 11,
+	SYNCH_FRAME = 12
+} bRequest_Codes;
+
+typedef enum {
+	ENDPOINT_HALT = 0,
+	DEVICE_REMOTE_WAKEUP = 1,
+	TEST_MODE = 2
+} feature_selectors;
+
+enum {
+	audio_device      = 0x01,
+	comm_device       = 0x02,
+	hid_device        = 0x03,
+	physical_device   = 0x05,
+	imaging_device    = 0x06,
+	printer_device    = 0x07,
+	msc_device        = 0x08,
+	hub_device        = 0x09,
+	cdc_device        = 0x0a,
+	ccid_device       = 0x0b,
+	security_device   = 0x0d,
+	video_device      = 0x0e,
+	healthcare_device = 0x0f,
+	diagnostic_device = 0xdc,
+	wireless_device   = 0xe0,
+	misc_device       = 0xef,
+};
+
+enum { hid_subclass_none = 0, hid_subclass_boot = 1 };
+
+enum {
+	hid_boot_proto_none = 0,
+	hid_boot_proto_keyboard = 1,
+	hid_boot_proto_mouse = 2
+};
+
+typedef struct {
+	union {
+		struct {
+#ifdef CONFIG_BIG_ENDIAN
+			dev_req_dir data_dir:1;
+			dev_req_type req_type:2;
+			dev_req_recp req_recp:5;
+#else
+			dev_req_recp req_recp:5;
+			dev_req_type req_type:2;
+			dev_req_dir data_dir:1;
+#endif
+		} __attribute__ ((packed));
+		unsigned char bmRequestType;
+	} __attribute__ ((packed));
+	unsigned char bRequest;
+	unsigned short wValue;
+	unsigned short wIndex;
+	unsigned short wLength;
+} __attribute__ ((packed)) dev_req_t;
+
+struct usbdev_hc;
+typedef struct usbdev_hc hci_t;
+
+struct usbdev;
+typedef struct usbdev usbdev_t;
+
+typedef enum { SETUP, IN, OUT } direction_t;
+typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3
+} endpoint_type;
+
+typedef struct {
+	usbdev_t *dev;
+	int endpoint;
+	direction_t direction;
+	int toggle;
+	int maxpacketsize;
+	endpoint_type type;
+	int interval; /* expressed as binary logarithm of the number
+			 of microframes (i.e. t = 125us * 2^interval) */
+} endpoint_t;
+
+enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 };
+
+struct usbdev {
+	hci_t *controller;
+	endpoint_t endpoints[32];
+	int num_endp;
+	int address;		// usb address
+	int hub;		// hub, device is attached to
+	int port;		// port where device is attached
+	int speed;		// 1: lowspeed, 0: fullspeed, 2: highspeed
+	u32 quirks;		// quirks field. got to love usb
+	void *data;
+	u8 *descriptor;
+	u8 *configuration;
+	void (*init) (usbdev_t *dev);
+	void (*destroy) (usbdev_t *dev);
+	void (*poll) (usbdev_t *dev);
+};
+
+typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type;
+
+struct usbdev_hc {
+	hci_t *next;
+	u32 reg_base;
+	hc_type type;
+	usbdev_t *devices[128];	// dev 0 is root hub, 127 is last addressable
+
+	/* start():     Resume operation. */
+	void (*start) (hci_t *controller);
+	/* stop():      Stop operation but keep controller initialized. */
+	void (*stop) (hci_t *controller);
+	/* reset():     Perform a controller reset. The controller needs to
+	                be (re)initialized afterwards to work (again). */
+	void (*reset) (hci_t *controller);
+	/* init():      Initialize a (previously reset) controller
+	                to a working state. */
+	void (*init) (hci_t *controller);
+	/* shutdown():  Stop operation, detach host controller and shutdown
+	                this driver instance. After calling shutdown() any
+			other usage of this hci_t* is invalid. */
+	void (*shutdown) (hci_t *controller);
+
+	int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize);
+	int (*control) (usbdev_t *dev, direction_t pid, int dr_length,
+			void *devreq, int data_length, u8 *data);
+	void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
+	void (*destroy_intr_queue) (endpoint_t *ep, void *queue);
+	u8* (*poll_intr_queue) (void *queue);
+	void *instance;
+
+	/* set_address():		Tell the usb device its address and
+					return it. xHCI controllers want to
+					do this by themself. Also, the usbdev
+					structure has to be allocated and
+					initialized. */
+	int (*set_address) (hci_t *controller, int speed, int hubport, int hubaddr);
+	/* finish_device_config():	Another hook for xHCI,
+					returns 0 on success. */
+	int (*finish_device_config) (usbdev_t *dev);
+	/* destroy_device():		Finally, destroy all structures that
+					were allocated during set_address()
+					and finish_device_config(). */
+	void (*destroy_device) (hci_t *controller, int devaddr);
+};
+
+typedef struct {
+	unsigned char bDescLength;
+	unsigned char bDescriptorType;
+	unsigned char bNbrPorts;
+	union {
+		struct {
+#ifdef CONFIG_BIG_ENDIAN
+			unsigned long:8;
+			unsigned long arePortIndicatorsSupported:1;
+			unsigned long ttThinkTime:2;
+			unsigned long overcurrentProtectionMode:2;
+			unsigned long isCompoundDevice:1;
+			unsigned long logicalPowerSwitchingMode:2;
+#else
+			unsigned long logicalPowerSwitchingMode:2;
+			unsigned long isCompoundDevice:1;
+			unsigned long overcurrentProtectionMode:2;
+			unsigned long ttThinkTime:2;
+			unsigned long arePortIndicatorsSupported:1;
+			unsigned long:8;
+#endif
+		} __attribute__ ((packed));
+		unsigned short wHubCharacteristics;
+	} __attribute__ ((packed));
+	unsigned char bPowerOn2PwrGood;
+	unsigned char bHubContrCurrent;
+	char DeviceRemovable[];
+} __attribute__ ((packed)) hub_descriptor_t;
+
+typedef struct {
+	unsigned char bLength;
+	unsigned char bDescriptorType;
+	unsigned short bcdUSB;
+	unsigned char bDeviceClass;
+	unsigned char bDeviceSubClass;
+	unsigned char bDeviceProtocol;
+	unsigned char bMaxPacketSize0;
+	unsigned short idVendor;
+	unsigned short idProduct;
+	unsigned short bcdDevice;
+	unsigned char iManufacturer;
+	unsigned char iProduct;
+	unsigned char iSerialNumber;
+	unsigned char bNumConfigurations;
+} __attribute__ ((packed)) device_descriptor_t;
+
+typedef struct {
+	unsigned char bLength;
+	unsigned char bDescriptorType;
+	unsigned short wTotalLength;
+	unsigned char bNumInterfaces;
+	unsigned char bConfigurationValue;
+	unsigned char iConfiguration;
+	unsigned char bmAttributes;
+	unsigned char bMaxPower;
+} __attribute__ ((packed)) configuration_descriptor_t;
+
+typedef struct {
+	unsigned char bLength;
+	unsigned char bDescriptorType;
+	unsigned char bInterfaceNumber;
+	unsigned char bAlternateSetting;
+	unsigned char bNumEndpoints;
+	unsigned char bInterfaceClass;
+	unsigned char bInterfaceSubClass;
+	unsigned char bInterfaceProtocol;
+	unsigned char iInterface;
+} __attribute__ ((packed)) interface_descriptor_t;
+
+typedef struct {
+	unsigned char bLength;
+	unsigned char bDescriptorType;
+	unsigned char bEndpointAddress;
+	unsigned char bmAttributes;
+	unsigned short wMaxPacketSize;
+	unsigned char bInterval;
+} __attribute__ ((packed)) endpoint_descriptor_t;
+
+typedef struct {
+	unsigned char bLength;
+	unsigned char bDescriptorType;
+	unsigned short bcdHID;
+	unsigned char bCountryCode;
+	unsigned char bNumDescriptors;
+	unsigned char bReportDescriptorType;
+	unsigned short wReportDescriptorLength;
+} __attribute__ ((packed)) hid_descriptor_t;
+
+hci_t *new_controller (void);
+void detach_controller (hci_t *controller);
+void usb_poll (void);
+void init_device_entry (hci_t *controller, int num);
+
+void set_feature (usbdev_t *dev, int endp, int feature, int rtype);
+void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data);
+void set_configuration (usbdev_t *dev);
+int clear_feature (usbdev_t *dev, int endp, int feature, int rtype);
+int clear_stall (endpoint_t *ep);
+
+void usb_hub_init (usbdev_t *dev);
+void usb_hid_init (usbdev_t *dev);
+void usb_msc_init (usbdev_t *dev);
+void usb_generic_init (usbdev_t *dev);
+
+u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType,
+		    int descType, int descIdx, int langID);
+
+static inline unsigned char
+gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp)
+{
+	return (dir << 7) | (type << 5) | recp;
+}
+
+/* default "set address" handler */
+int generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr);
+
+void usb_detach_device(hci_t *controller, int devno);
+int usb_attach_device(hci_t *controller, int hubaddress, int port, int speed);
+
+u32 usb_quirk_check(u16 vendor, u16 device);
+int usb_interface_check(u16 vendor, u16 device);
+
+#define USB_QUIRK_MSC_FORCE_PROTO_SCSI		(1 <<  0)
+#define USB_QUIRK_MSC_FORCE_PROTO_ATAPI		(1 <<  1)
+#define USB_QUIRK_MSC_FORCE_PROTO_UFI		(1 <<  2)
+#define USB_QUIRK_MSC_FORCE_PROTO_RBC		(1 <<  3)
+#define USB_QUIRK_MSC_FORCE_TRANS_BBB		(1 <<  4)
+#define USB_QUIRK_MSC_FORCE_TRANS_CBI		(1 <<  5)
+#define USB_QUIRK_MSC_FORCE_TRANS_CBI_I		(1 <<  6)
+#define USB_QUIRK_MSC_NO_TEST_UNIT_READY	(1 <<  7)
+#define USB_QUIRK_MSC_SHORT_INQUIRY		(1 <<  8)
+#define USB_QUIRK_TEST				(1 << 31)
+#define USB_QUIRK_NONE				 0
+
+#ifdef CONFIG_DEBUG_USB
+#define usb_debug(fmt, args...)  do { printk(fmt , ##args); } while (0)
+#else
+#define usb_debug(fmt, args...)
+#endif
+
+/**
+ * To be implemented by libpayload-client. It's called by the USB stack
+ * when a new USB device is found which isn't claimed by a built in driver,
+ * so the client has the chance to know about it.
+ *
+ * @param dev descriptor for the USB device
+ */
+void __attribute__((weak)) usb_generic_create (usbdev_t *dev);
+
+/**
+ * To be implemented by libpayload-client. It's called by the USB stack
+ * when it finds out that a USB device is removed which wasn't claimed by a
+ * built in driver.
+ *
+ * @param dev descriptor for the USB device
+ */
+void __attribute__((weak)) usb_generic_remove (usbdev_t *dev);
+
+#endif
diff --git a/openbios-devel/drivers/usbhid.c b/openbios-devel/drivers/usbhid.c
new file mode 100644
index 0000000..a423278
--- /dev/null
+++ b/openbios-devel/drivers/usbhid.c
@@ -0,0 +1,579 @@
+/*
+ * Driver for HID devices ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2008-2010 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 "libopenbios/bindings.h"
+#include <libc/string.h>
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "drivers/usb.h"
+#include "usb.h"
+
+DECLARE_UNNAMED_NODE(usb_kbd, INSTALL_OPEN, sizeof(int));
+
+static void
+keyboard_open(int *idx)
+{
+	RET(-1);
+}
+
+static void
+keyboard_close(int *idx)
+{
+}
+
+static void keyboard_read(void);
+
+NODE_METHODS( usb_kbd ) = {
+	{ "open",		keyboard_open		},
+	{ "close",		keyboard_close		},
+	{ "read",               keyboard_read		},
+};
+
+#ifdef CONFIG_USB_DEBUG
+static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
+#endif
+typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto;
+enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
+		0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
+};
+
+typedef union {
+	struct {
+		u8 modifiers;
+		u8 repeats;
+		u8 keys[6];
+	};
+	u8 buffer[8];
+} usb_hid_keyboard_event_t;
+
+typedef struct {
+	void* queue;
+	hid_descriptor_t *descriptor;
+
+	usb_hid_keyboard_event_t previous;
+	int lastkeypress;
+	int repeat_delay;
+} usbhid_inst_t;
+
+#define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)
+
+static void
+usb_hid_destroy (usbdev_t *dev)
+{
+	if (HID_INST(dev)->queue) {
+		int i;
+		for (i = 0; i <= dev->num_endp; i++) {
+			if (dev->endpoints[i].endpoint == 0)
+				continue;
+			if (dev->endpoints[i].type != INTERRUPT)
+				continue;
+			if (dev->endpoints[i].direction != IN)
+				continue;
+			break;
+		}
+		dev->controller->destroy_intr_queue(
+				&dev->endpoints[i], HID_INST(dev)->queue);
+		HID_INST(dev)->queue = NULL;
+	}
+	free (dev->data);
+}
+
+/* keybuffer is global to all USB keyboards */
+static int keycount;
+#define KEYBOARD_BUFFER_SIZE 16
+static short keybuffer[KEYBOARD_BUFFER_SIZE];
+
+const char *countries[36][2] = {
+	{ "unknown", "us" },
+	{ "Arabic", "ae" },
+	{ "Belgian", "be" },
+	{ "Canadian-Bilingual", "ca" },
+	{ "Canadian-French", "ca" },
+	{ "Czech Republic", "cz" },
+	{ "Danish", "dk" },
+	{ "Finnish", "fi" },
+	{ "French", "fr" },
+	{ "German", "de" },
+	{ "Greek", "gr" },
+	{ "Hebrew", "il" },
+	{ "Hungary", "hu" },
+	{ "International (ISO)", "iso" },
+	{ "Italian", "it" },
+	{ "Japan (Katakana)", "jp" },
+	{ "Korean", "us" },
+	{ "Latin American", "us" },
+	{ "Netherlands/Dutch", "nl" },
+	{ "Norwegian", "no" },
+	{ "Persian (Farsi)", "ir" },
+	{ "Poland", "pl" },
+	{ "Portuguese", "pt" },
+	{ "Russia", "ru" },
+	{ "Slovakia", "sl" },
+	{ "Spanish", "es" },
+	{ "Swedish", "se" },
+	{ "Swiss/French", "ch" },
+	{ "Swiss/German", "ch" },
+	{ "Switzerland", "ch" },
+	{ "Taiwan", "tw" },
+	{ "Turkish-Q", "tr" },
+	{ "UK", "uk" },
+	{ "US", "us" },
+	{ "Yugoslavia", "yu" },
+	{ "Turkish-F", "tr" },
+	/* 36 - 255: Reserved */
+};
+
+
+
+struct layout_maps {
+	const char *country;
+	const short map[4][0x80];
+};
+
+static const struct layout_maps *map;
+
+#define KEY_BREAK     0x101  /* Not on PC KBD */
+#define KEY_DOWN      0x102  /* Down arrow key */
+#define KEY_UP        0x103  /* Up arrow key */
+#define KEY_LEFT      0x104  /* Left arrow key */
+#define KEY_RIGHT     0x105  /* Right arrow key */
+#define KEY_HOME      0x106  /* home key */
+#define KEY_BACKSPACE 0x107  /* not on pc */
+#define KEY_F0        0x108  /* function keys; 64 reserved */
+#define KEY_F(n)      (KEY_F0 + (n))
+
+#define KEY_DC        0x14a  /* delete character */
+#define KEY_IC        0x14b  /* insert char or enter ins mode */
+
+#define KEY_NPAGE     0x152  /* next page */
+#define KEY_PPAGE     0x153  /* previous page */
+
+#define KEY_ENTER     0x157  /* enter or send (unreliable) */
+
+#define KEY_PRINT     0x15a  /* print/copy */
+
+#define KEY_END       0x166  /* end key */
+
+static const struct layout_maps keyboard_layouts[] = {
+// #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
+{ .country = "us", .map = {
+	{ /* No modifier */
+	-1, -1, -1, -1, 'a', 'b', 'c', 'd',
+	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+	/* 0x10 */
+	'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+	'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
+	/* 0x20 */
+	'3', '4', '5', '6', '7', '8', '9', '0',
+	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
+	/* 0x30 */
+	']', '\\', -1, ';', '\'', '`', ',', '.',
+	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
+	/* 0x40 */
+	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
+	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
+	/* 50 */
+	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
+	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
+	/* 60 */
+	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	/* 70 */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	 },
+	{ /* Shift modifier */
+	-1, -1, -1, -1, 'A', 'B', 'C', 'D',
+	'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+	/* 0x10 */
+	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+	'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
+	/* 0x20 */
+	'#', '$', '%', '^', '&', '*', '(', ')',
+	'\n', '\e', '\b', '\t', ' ', '_', '+', '{',
+	/* 0x30 */
+	'}', '|', -1, ':', '"', '~', '<', '>',
+	'?', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
+	/* 0x40 */
+	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
+	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
+	/* 50 */
+	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
+	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
+	/* 60 */
+	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	/* 70 */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	 },
+	{ /* Alt */
+	-1, -1, -1, -1, 'a', 'b', 'c', 'd',
+	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+	/* 0x10 */
+	'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+	'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
+	/* 0x20 */
+	'3', '4', '5', '6', '7', '8', '9', '0',
+	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
+	/* 0x30 */
+	']', '\\', -1, ';', '\'', '`', ',', '.',
+	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
+	/* 0x40 */
+	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
+	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
+	/* 50 */
+	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
+	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
+	/* 60 */
+	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	/* 70 */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	 },
+	{ /* Shift+Alt modifier */
+	-1, -1, -1, -1, 'A', 'B', 'C', 'D',
+	'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+	/* 0x10 */
+	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+	'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
+	/* 0x20 */
+	'#', '$', '%', '^', '&', '*', '(', ')',
+	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
+	/* 0x30 */
+	']', '\\', -1, ':', '\'', '`', ',', '.',
+	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
+	/* 0x40 */
+	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
+	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
+	/* 50 */
+	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
+	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
+	/* 60 */
+	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	/* 70 */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	 }
+}},
+//#endif
+};
+
+#define MOD_SHIFT    (1 << 0)
+#define MOD_ALT      (1 << 1)
+#define MOD_CTRL     (1 << 2)
+
+static void usb_hid_keyboard_queue(int ch) {
+	/* ignore key presses if buffer full */
+	if (keycount < KEYBOARD_BUFFER_SIZE)
+		keybuffer[keycount++] = ch;
+}
+
+#define KEYBOARD_REPEAT_MS	30
+#define INITIAL_REPEAT_DELAY	10
+#define REPEAT_DELAY		 2
+
+static void
+usb_hid_process_keyboard_event(usbhid_inst_t *const inst,
+		const usb_hid_keyboard_event_t *const current)
+{
+	const usb_hid_keyboard_event_t *const previous = &inst->previous;
+
+	int i, keypress = 0, modifiers = 0;
+
+	if (current->modifiers & 0x01) /* Left-Ctrl */   modifiers |= MOD_CTRL;
+	if (current->modifiers & 0x02) /* Left-Shift */  modifiers |= MOD_SHIFT;
+	if (current->modifiers & 0x04) /* Left-Alt */    modifiers |= MOD_ALT;
+	if (current->modifiers & 0x08) /* Left-GUI */    ;
+	if (current->modifiers & 0x10) /* Right-Ctrl */  modifiers |= MOD_CTRL;
+	if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT;
+	if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT;
+	if (current->modifiers & 0x80) /* Right-GUI */   ;
+
+	/* Did the event change at all? */
+	if (inst->lastkeypress &&
+			!memcmp(current, previous, sizeof(*current))) {
+		/* No. Then it's a key repeat event. */
+		if (inst->repeat_delay) {
+			inst->repeat_delay--;
+		} else {
+			usb_hid_keyboard_queue(inst->lastkeypress);
+			inst->repeat_delay = REPEAT_DELAY;
+		}
+
+		return;
+	}
+
+	inst->lastkeypress = 0;
+
+	for (i=0; i<6; i++) {
+		int j;
+		int skip = 0;
+		// No more keys? skip
+		if (current->keys[i] == 0)
+			return;
+
+		for (j=0; j<6; j++) {
+			if (current->keys[i] == previous->keys[j]) {
+				skip = 1;
+				break;
+			}
+		}
+		if (skip)
+			continue;
+
+
+		/* Mask off MOD_CTRL */
+		keypress = map->map[modifiers & 0x03][current->keys[i]];
+
+		if (modifiers & MOD_CTRL) {
+			switch (keypress) {
+			case 'a' ... 'z':
+				keypress &= 0x1f;
+				break;
+			default:
+				continue;
+			}
+		}
+
+		if (keypress == -1) {
+			/* Debug: Print unknown keys */
+			usb_debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
+				current->modifiers, current->repeats,
+			current->keys[0], current->keys[1],
+			current->keys[2], current->keys[3],
+			current->keys[4], current->keys[5], i);
+
+			/* Unknown key? Try next one in the queue */
+			continue;
+		}
+
+		usb_hid_keyboard_queue(keypress);
+
+		/* Remember for authentic key repeat */
+		inst->lastkeypress = keypress;
+		inst->repeat_delay = INITIAL_REPEAT_DELAY;
+	}
+}
+
+static void
+usb_hid_poll (usbdev_t *dev)
+{
+	usb_hid_keyboard_event_t current;
+	const u8 *buf;
+
+	while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
+		memcpy(&current.buffer, buf, 8);
+		usb_hid_process_keyboard_event(HID_INST(dev), &current);
+		HID_INST(dev)->previous = current;
+	}
+}
+
+static void
+usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
+{
+	dev_req_t dr;
+	dr.data_dir = host_to_device;
+	dr.req_type = class_type;
+	dr.req_recp = iface_recp;
+	dr.bRequest = SET_IDLE;
+	dr.wValue = __cpu_to_le16((duration >> 2) << 8);
+	dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
+}
+
+static void
+usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto)
+{
+	dev_req_t dr;
+	dr.data_dir = host_to_device;
+	dr.req_type = class_type;
+	dr.req_recp = iface_recp;
+	dr.bRequest = SET_PROTOCOL;
+	dr.wValue = __cpu_to_le16(proto);
+	dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
+	dr.wLength = 0;
+	dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
+}
+
+static int usb_hid_set_layout (const char *country)
+{
+	/* FIXME should be per keyboard */
+	int i;
+
+	for (i=0; i<sizeof(keyboard_layouts)/sizeof(keyboard_layouts[0]); i++) {
+		if (strncmp(keyboard_layouts[i].country, country,
+					strlen(keyboard_layouts[i].country)))
+			continue;
+
+		/* Found, changing keyboard layout */
+		map = &keyboard_layouts[i];
+		usb_debug("  Keyboard layout '%s'\n", map->country);
+		return 0;
+	}
+
+	usb_debug("  Keyboard layout '%s' not found, using '%s'\n",
+			country, map->country);
+
+	/* Nothing found, not changed */
+	return -1;
+}
+
+void
+usb_hid_init (usbdev_t *dev)
+{
+	configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
+	interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength);
+
+	if (interface->bInterfaceSubClass == hid_subclass_boot) {
+		u8 countrycode = 0;
+		usb_debug ("  supports boot interface..\n");
+		usb_debug ("  it's a %s\n",
+			boot_protos[interface->bInterfaceProtocol]);
+		switch (interface->bInterfaceProtocol) {
+		case hid_boot_proto_keyboard:
+			dev->data = malloc (sizeof (usbhid_inst_t));
+			if (!dev->data) {
+				printk("Not enough memory for USB HID device.\n");
+				return;
+                        }
+			memset(&HID_INST(dev)->previous, 0x00,
+			       sizeof(HID_INST(dev)->previous));
+			usb_debug ("  configuring...\n");
+			usb_hid_set_protocol(dev, interface, hid_proto_boot);
+			usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS);
+			usb_debug ("  activating...\n");
+#if 0
+			HID_INST (dev)->descriptor =
+				(hid_descriptor_t *)
+					get_descriptor(dev, gen_bmRequestType
+					(device_to_host, standard_type, iface_recp),
+					0x21, 0, 0);
+			countrycode = HID_INST(dev)->descriptor->bCountryCode;
+#endif
+			/* 35 countries defined: */
+			if (countrycode > 35)
+				countrycode = 0;
+			usb_debug ("  Keyboard has %s layout (country code %02x)\n",
+					countries[countrycode][0], countrycode);
+
+			/* Set keyboard layout accordingly */
+			usb_hid_set_layout(countries[countrycode][1]);
+
+			// only add here, because we only support boot-keyboard HID devices
+			dev->destroy = usb_hid_destroy;
+			dev->poll = usb_hid_poll;
+			int i;
+			for (i = 0; i <= dev->num_endp; i++) {
+				if (dev->endpoints[i].endpoint == 0)
+					continue;
+				if (dev->endpoints[i].type != INTERRUPT)
+					continue;
+				if (dev->endpoints[i].direction != IN)
+					continue;
+				break;
+			}
+			usb_debug ("  found endpoint %x for interrupt-in\n", i);
+			/* 20 buffers of 8 bytes, for every 10 msecs */
+			HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10);
+			keycount = 0;
+			usb_debug ("  configuration done.\n");
+			break;
+		default:
+			usb_debug("NOTICE: HID interface protocol %d%s not supported.\n",
+				 interface->bInterfaceProtocol,
+				 (interface->bInterfaceProtocol == hid_boot_proto_mouse ?
+				 " (USB mouse)" : ""));
+			break;
+		}
+	}
+}
+
+static int usbhid_havechar (void)
+{
+	return (keycount != 0);
+}
+
+static int usbhid_getchar (void)
+{
+	short ret;
+
+	if (keycount == 0)
+		return 0;
+	ret = keybuffer[0];
+	memmove(keybuffer, keybuffer + 1, --keycount);
+
+	return (int)ret;
+}
+
+/* ( addr len -- actual ) */
+static void keyboard_read(void)
+{
+	char *addr;
+	int len, key, i;
+
+	usb_poll();
+	len=POP();
+	addr=(char *)cell2pointer(POP());
+
+	for (i = 0; i < len; i++) {
+	    if (!usbhid_havechar())
+			break;
+		key = usbhid_getchar();
+		*addr++ = (char)key;
+	}
+	PUSH(i);
+}
+
+void ob_usb_hid_add_keyboard(const char *path)
+{
+	char name[128];
+	phandle_t aliases;
+
+	snprintf(name, sizeof(name), "%s/keyboard", path);
+	usb_debug("Found keyboard at %s\n", name);
+	REGISTER_NAMED_NODE(usb_kbd, name);
+
+	push_str(name);
+	fword("find-device");
+
+	push_str("keyboard");
+	fword("device-type");
+
+	aliases = find_dev("/aliases");
+	set_property(aliases, "adb-keyboard", name, strlen(name) + 1);
+}
diff --git a/openbios-devel/drivers/usbohci.c b/openbios-devel/drivers/usbohci.c
new file mode 100644
index 0000000..58c1423
--- /dev/null
+++ b/openbios-devel/drivers/usbohci.c
@@ -0,0 +1,920 @@
+/*
+ * Driver for USB OHCI ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * 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.
+ */
+
+//#define USB_DEBUG_ED
+
+#include "config.h"
+#include <asm/io.h>
+#include <libopenbios/ofmem.h>
+#include "timer.h"
+#include <drivers/usb.h>
+#include "usbohci_private.h"
+#include "usbohci.h"
+
+static void ohci_start (hci_t *controller);
+static void ohci_stop (hci_t *controller);
+static void ohci_reset (hci_t *controller);
+static void ohci_shutdown (hci_t *controller);
+static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
+static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
+			 int dalen, u8 *data);
+static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
+static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue);
+static u8* ohci_poll_intr_queue (void *queue);
+static void ohci_process_done_queue(ohci_t *ohci, int spew_debug);
+
+#ifdef USB_DEBUG_ED
+static void
+dump_td (td_t *cur)
+{
+	usb_debug("+---------------------------------------------------+\n");
+	if (((__le32_to_cpu(cur->config) & (3UL << 19)) >> 19) == 0)
+		usb_debug("|..[SETUP]..........................................|\n");
+	else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 2)
+		usb_debug("|..[IN].............................................|\n");
+	else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 1)
+		usb_debug("|..[OUT]............................................|\n");
+	else
+		usb_debug("|..[]...............................................|\n");
+	usb_debug("|:|============ OHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(cur));
+	usb_debug("|:| ERRORS = [%ld] | CONFIG = [0x%08x] |        |:|\n",
+		  3 - ((__le32_to_cpu(cur->config) & (3UL << 26)) >> 26), __le32_to_cpu(cur->config));
+	usb_debug("|:+-----------------------------------------------+:|\n");
+	usb_debug("|:|   C   | Condition Code               |   [%02ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (0xFUL << 28)) >> 28);
+	usb_debug("|:|   O   | Direction/PID                |    [%ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (3UL << 19)) >> 19);
+	usb_debug("|:|   N   | Buffer Rounding              |    [%ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (1UL << 18)) >> 18);
+	usb_debug("|:|   F   | Delay Intterrupt             |    [%ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (7UL << 21)) >> 21);
+	usb_debug("|:|   I   | Data Toggle                  |    [%ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (3UL << 24)) >> 24);
+	usb_debug("|:|   G   | Error Count                  |    [%ld] |:|\n",
+		 (__le32_to_cpu(cur->config) & (3UL << 26)) >> 26);
+	usb_debug("|:+-----------------------------------------------+:|\n");
+	usb_debug("|:| Current Buffer Pointer         [0x%08x]   |:|\n", __le32_to_cpu(cur->current_buffer_pointer));
+	usb_debug("|:+-----------------------------------------------+:|\n");
+	usb_debug("|:| Next TD                        [0x%08x]   |:|\n", __le32_to_cpu(cur->next_td));
+	usb_debug("|:+-----------------------------------------------+:|\n");
+	usb_debug("|:| Current Buffer End             [0x%08x]   |:|\n", __le32_to_cpu(cur->buffer_end));
+	usb_debug("|:|-----------------------------------------------|:|\n");
+	usb_debug("|...................................................|\n");
+	usb_debug("+---------------------------------------------------+\n");
+}
+
+static void
+dump_ed (ed_t *cur)
+{
+	td_t *tmp_td = NULL;
+	usb_debug("+===================================================+\n");
+	usb_debug("| ############# OHCI ED at [0x%08lx] ########### |\n", virt_to_phys(cur));
+	usb_debug("+---------------------------------------------------+\n");
+	usb_debug("| Next Endpoint Descriptor       [0x%08lx]       |\n", __le32_to_cpu(cur->next_ed) & ~0xFUL);
+	usb_debug("+---------------------------------------------------+\n");
+	usb_debug("|        |               @ 0x%08x :             |\n", __le32_to_cpu(cur->config));
+	usb_debug("|   C    | Maximum Packet Length           | [%04ld] |\n",
+		 ((__le32_to_cpu(cur->config) & (0x3fffUL << 16)) >> 16));
+	usb_debug("|   O    | Function Address                | [%04d] |\n",
+		 __le32_to_cpu(cur->config) & 0x7F);
+	usb_debug("|   N    | Endpoint Number                 |   [%02ld] |\n",
+		 (__le32_to_cpu(cur->config) & (0xFUL << 7)) >> 7);
+	usb_debug("|   F    | Endpoint Direction              |    [%ld] |\n",
+		 ((__le32_to_cpu(cur->config) & (3UL << 11)) >> 11));
+	usb_debug("|   I    | Endpoint Speed                  |    [%ld] |\n",
+		 ((__le32_to_cpu(cur->config) & (1UL << 13)) >> 13));
+	usb_debug("|   G    | Skip                            |    [%ld] |\n",
+		 ((__le32_to_cpu(cur->config) & (1UL << 14)) >> 14));
+	usb_debug("|        | Format                          |    [%ld] |\n",
+		 ((__le32_to_cpu(cur->config) & (1UL << 15)) >> 15));
+	usb_debug("+---------------------------------------------------+\n");
+	usb_debug("| TD Queue Tail Pointer          [0x%08lx]       |\n",
+		 __le32_to_cpu(cur->tail_pointer) & ~0xFUL);
+	usb_debug("+---------------------------------------------------+\n");
+	usb_debug("| TD Queue Head Pointer          [0x%08lx]       |\n",
+		 __le32_to_cpu(cur->head_pointer) & ~0xFUL);
+	usb_debug("| CarryToggleBit    [%d]          Halted   [%d]       |\n",
+		 (u16)(__le32_to_cpu(cur->head_pointer) & 0x2UL)>>1, (u16)(__le32_to_cpu(cur->head_pointer) & 0x1UL));
+
+	tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(cur->head_pointer) & ~0xFUL));
+	if ((__le32_to_cpu(cur->head_pointer) & ~0xFUL) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) {
+		usb_debug("|:::::::::::::::::: OHCI TD CHAIN ::::::::::::::::::|\n");
+		while (virt_to_phys(tmp_td) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL))
+		{
+			dump_td(tmp_td);
+			tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(tmp_td->next_td) & ~0xFUL));
+		}
+		usb_debug("|:::::::::::::::: EOF OHCI TD CHAIN ::::::::::::::::|\n");
+		usb_debug("+---------------------------------------------------+\n");
+	} else {
+		usb_debug("+---------------------------------------------------+\n");
+	}
+}
+#endif
+
+static void
+ohci_reset (hci_t *controller)
+{
+	if (controller == NULL)
+		return;
+
+	OHCI_INST(controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset);
+	mdelay(2); /* wait 2ms */
+	OHCI_INST(controller)->opreg->HcControl = 0;
+	mdelay(10); /* wait 10ms */
+}
+
+static void
+ohci_reinit (hci_t *controller)
+{
+}
+
+hci_t *
+ohci_init (void *bar)
+{
+	int i;
+
+	hci_t *controller = new_controller ();
+
+	if (!controller) {
+		printk("Could not create USB controller instance.\n");
+		return NULL;
+        }
+
+	controller->instance = malloc (sizeof (ohci_t));
+	if(!controller->instance) {
+		printk("Not enough memory creating USB controller instance.\n");
+                return NULL;
+        }
+
+	controller->type = OHCI;
+
+	controller->start = ohci_start;
+	controller->stop = ohci_stop;
+	controller->reset = ohci_reset;
+	controller->init = ohci_reinit;
+	controller->shutdown = ohci_shutdown;
+	controller->bulk = ohci_bulk;
+	controller->control = ohci_control;
+	controller->set_address = generic_set_address;
+	controller->finish_device_config = NULL;
+	controller->destroy_device = NULL;
+	controller->create_intr_queue = ohci_create_intr_queue;
+	controller->destroy_intr_queue = ohci_destroy_intr_queue;
+	controller->poll_intr_queue = ohci_poll_intr_queue;
+	for (i = 0; i < 128; i++) {
+		controller->devices[i] = 0;
+	}
+	init_device_entry (controller, 0);
+	OHCI_INST (controller)->roothub = controller->devices[0];
+
+	controller->reg_base = (u32)(unsigned long)bar;
+	OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base);
+	usb_debug("OHCI Version %x.%x\n",
+		  (READ_OPREG(OHCI_INST(controller), HcRevision) >> 4) & 0xf,
+		  READ_OPREG(OHCI_INST(controller), HcRevision) & 0xf);
+
+	if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) == USBReset) {
+		/* cold boot */
+		OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~RemoteWakeupConnected);
+		OHCI_INST (controller)->opreg->HcFmInterval = 
+			__cpu_to_le32((11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket));
+		/* TODO: right value for PowerOnToPowerGoodTime ? */
+		OHCI_INST (controller)->opreg->HcRhDescriptorA =
+			__cpu_to_le32(NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime));
+		OHCI_INST (controller)->opreg->HcRhDescriptorB = __cpu_to_le32(0 * DeviceRemovable);
+		udelay(100); /* TODO: reset asserting according to USB spec */
+	} else if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) != USBOperational) {
+		OHCI_INST (controller)->opreg->HcControl =
+			__cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) & ~HostControllerFunctionalStateMask)
+			| USBResume);
+		udelay(100); /* TODO: resume time according to USB spec */
+	}
+	int interval = OHCI_INST (controller)->opreg->HcFmInterval;
+
+	OHCI_INST (controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset);
+	udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */
+	OHCI_INST (controller)->opreg->HcFmInterval = interval;
+	ofmem_posix_memalign((void **)&(OHCI_INST (controller)->hcca), 256, 256);
+	memset((void*)OHCI_INST (controller)->hcca, 0, 256);
+
+	usb_debug("HCCA addr %p\n", OHCI_INST(controller)->hcca);
+	/* Initialize interrupt table. */
+	u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable;
+	ed_t *const periodic_ed;
+        ofmem_posix_memalign((void **)&periodic_ed, sizeof(ed_t), sizeof(ed_t));
+	memset((void *)periodic_ed, 0, sizeof(*periodic_ed));
+	for (i = 0; i < 32; ++i)
+		intr_table[i] = __cpu_to_le32(virt_to_phys(periodic_ed));
+	OHCI_INST (controller)->periodic_ed = periodic_ed;
+
+	OHCI_INST (controller)->opreg->HcHCCA = __cpu_to_le32(virt_to_phys(OHCI_INST(controller)->hcca));
+	/* Make sure periodic schedule is enabled. */
+	OHCI_INST (controller)->opreg->HcControl |= __cpu_to_le32(PeriodicListEnable);
+	OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~IsochronousEnable); // unused by this driver
+	// disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs
+	OHCI_INST (controller)->opreg->HcInterruptEnable = __cpu_to_le32(1<<31);
+	OHCI_INST (controller)->opreg->HcInterruptDisable = __cpu_to_le32(~(1<<31));
+	OHCI_INST (controller)->opreg->HcInterruptStatus = __cpu_to_le32(~0);
+	OHCI_INST (controller)->opreg->HcPeriodicStart =
+		__cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcFmInterval) & FrameIntervalMask) / 10 * 9);
+	OHCI_INST (controller)->opreg->HcControl = __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl)
+								& ~HostControllerFunctionalStateMask) | USBOperational);
+
+	mdelay(100);
+
+	controller->devices[0]->controller = controller;
+	controller->devices[0]->init = ohci_rh_init;
+	controller->devices[0]->init (controller->devices[0]);
+	return controller;
+}
+
+#ifdef CONFIG_USB_PCI
+hci_t *
+ohci_pci_init (pcidev_t addr)
+{
+	u32 reg_base;
+
+	/* regarding OHCI spec, Appendix A, BAR_OHCI register description, Table A-4
+	 * BASE ADDRESS only [31-12] bits. All other usually 0, but not all.
+	 * OHCI mandates MMIO, so bit 0 is clear */
+	reg_base = pci_read_config32 (addr, 0x10) & 0xfffff000;
+
+	return ohci_init((void *)(unsigned long)reg_base);
+}
+#endif
+
+static void
+ohci_shutdown (hci_t *controller)
+{
+	if (controller == 0)
+		return;
+	detach_controller (controller);
+	ohci_stop(controller);
+	OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)->
+						  roothub);
+	free ((void *)OHCI_INST (controller)->periodic_ed);
+	free (OHCI_INST (controller));
+	free (controller);
+}
+
+static void
+ohci_start (hci_t *controller)
+{
+// TODO: turn on all operation of OHCI, but assume that it's initialized.
+}
+
+static void
+ohci_stop (hci_t *controller)
+{
+// TODO: turn off all operation of OHCI
+}
+
+static int
+wait_for_ed(usbdev_t *dev, ed_t *head, int pages)
+{
+	usb_debug("Waiting for %d pages on dev %p with head %p\n", pages, dev, head);
+	/* wait for results */
+	/* TOTEST: how long to wait?
+	 *         give 2s per TD (2 pages) plus another 2s for now
+	 */
+	int timeout = pages*1000 + 2000;
+	while (((__le32_to_cpu(head->head_pointer) & ~3) != __le32_to_cpu(head->tail_pointer)) &&
+		!(__le32_to_cpu(head->head_pointer) & 1) &&
+		((__le32_to_cpu((((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3)))->config)
+		& TD_CC_MASK) >= TD_CC_NOACCESS) && timeout--) {
+		/* don't log every ms */
+		if (!(timeout % 100))
+		usb_debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n",
+			  READ_OPREG(OHCI_INST(dev->controller), HcInterruptStatus),
+			  READ_OPREG(OHCI_INST(dev->controller), HcControl),
+			  READ_OPREG(OHCI_INST(dev->controller), HcCommandStatus),
+			__le32_to_cpu(head->head_pointer),
+			__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->next_td),
+			__le32_to_cpu(head->tail_pointer),
+			(__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->config) & TD_CC_MASK) >> TD_CC_SHIFT);
+		mdelay(1);
+	}
+	if (timeout < 0)
+		usb_debug("Error: ohci: endpoint "
+			"descriptor processing timed out.\n");
+	/* Clear the done queue. */
+	ohci_process_done_queue(OHCI_INST(dev->controller), 1);
+
+	if (__le32_to_cpu(head->head_pointer) & 1) {
+		usb_debug("HALTED!\n");
+		return 1;
+	}
+	return 0;
+}
+
+static void
+ohci_free_ed (ed_t *const head)
+{
+	/* In case the transfer canceled, we have to free unprocessed TDs. */
+	while ((__le32_to_cpu(head->head_pointer) & ~0x3) != __le32_to_cpu(head->tail_pointer)) {
+		/* Save current TD pointer. */
+		td_t *const cur_td =
+			(td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3);
+		/* Advance head pointer. */
+		head->head_pointer = cur_td->next_td;
+		/* Free current TD. */
+		free((void *)cur_td);
+	}
+
+	/* Always free the dummy TD */
+	if ((__le32_to_cpu(head->head_pointer) & ~0x3) == __le32_to_cpu(head->tail_pointer))
+		free(phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3));
+	/* and the ED. */
+	free((void *)head);
+}
+
+static int
+ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
+	      unsigned char *data)
+{
+	td_t *cur;
+
+	// pages are specified as 4K in OHCI, so don't use getpagesize()
+	int first_page = (unsigned long)data / 4096;
+	int last_page = (unsigned long)(data+dalen-1)/4096;
+	if (last_page < first_page) last_page = first_page;
+	int pages = (dalen==0)?0:(last_page - first_page + 1);
+
+	/* First TD. */
+	td_t *const first_td;
+        ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t));
+	memset((void *)first_td, 0, sizeof(*first_td));
+	cur = first_td;
+
+	cur->config = __cpu_to_le32(TD_DIRECTION_SETUP |
+		TD_DELAY_INTERRUPT_NOINTR |
+		TD_TOGGLE_FROM_TD |
+		TD_TOGGLE_DATA0 |
+		TD_CC_NOACCESS);
+	cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(devreq));
+	cur->buffer_end = __cpu_to_le32(virt_to_phys((char *)devreq + drlen - 1));
+
+	while (pages > 0) {
+		/* One more TD. */
+		td_t *const next;
+		ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t));
+		memset((void *)next, 0, sizeof(*next));
+		/* Linked to the previous. */
+		cur->next_td = __cpu_to_le32(virt_to_phys(next));
+		/* Advance to the new TD. */
+		cur = next;
+
+		cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+			TD_DELAY_INTERRUPT_NOINTR |
+			TD_TOGGLE_FROM_ED |
+			TD_CC_NOACCESS);
+		cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data));
+		pages--;
+		int consumed = (4096 - ((unsigned long)data % 4096));
+		if (consumed >= dalen) {
+			// end of data is within same page
+			cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1));
+			dalen = 0;
+			/* assert(pages == 0); */
+		} else {
+			dalen -= consumed;
+			data += consumed;
+			pages--;
+			int second_page_size = dalen;
+			if (dalen > 4096) {
+				second_page_size = 4096;
+			}
+			cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1));
+			dalen -= second_page_size;
+			data += second_page_size;
+		}
+	}
+
+	/* One more TD. */
+	td_t *const next_td;
+	ofmem_posix_memalign((void **)&next_td, sizeof(td_t), sizeof(td_t));
+	memset((void *)next_td, 0, sizeof(*next_td));
+	/* Linked to the previous. */
+	cur->next_td = __cpu_to_le32(virt_to_phys(next_td));
+	/* Advance to the new TD. */
+	cur = next_td;
+	cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_OUT : TD_DIRECTION_IN) |
+		TD_DELAY_INTERRUPT_ZERO | /* Write done head after this TD. */
+		TD_TOGGLE_FROM_TD |
+		TD_TOGGLE_DATA1 |
+		TD_CC_NOACCESS);
+	cur->current_buffer_pointer = 0;
+	cur->buffer_end = 0;
+
+	/* Final dummy TD. */
+	td_t *const final_td;
+	ofmem_posix_memalign((void **)&final_td, sizeof(td_t), sizeof(td_t));
+	memset((void *)final_td, 0, sizeof(*final_td));
+	/* Linked to the previous. */
+	cur->next_td = __cpu_to_le32(virt_to_phys(final_td));
+
+	/* Data structures */
+	ed_t *head;
+	ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t));
+	memset((void*)head, 0, sizeof(*head));
+	head->config = __cpu_to_le32((dev->address << ED_FUNC_SHIFT) |
+		(0 << ED_EP_SHIFT) |
+		(OHCI_FROM_TD << ED_DIR_SHIFT) |
+		(dev->speed?ED_LOWSPEED:0) |
+		(dev->endpoints[0].maxpacketsize << ED_MPS_SHIFT));
+	head->tail_pointer = __cpu_to_le32(virt_to_phys(final_td));
+	head->head_pointer = __cpu_to_le32(virt_to_phys(first_td));
+
+	usb_debug("ohci_control(): doing transfer with %x. first_td at %x\n",
+		__le32_to_cpu(head->config) & ED_FUNC_MASK, __le32_to_cpu(head->head_pointer));
+#ifdef USB_DEBUG_ED
+	dump_ed(head);
+#endif
+
+	/* activate schedule */
+	OHCI_INST(dev->controller)->opreg->HcControlHeadED = __cpu_to_le32(virt_to_phys(head));
+	OHCI_INST(dev->controller)->opreg->HcControl |= __cpu_to_le32(ControlListEnable);
+	OHCI_INST(dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(ControlListFilled);
+
+	int failure = wait_for_ed(dev, head,
+			(dalen==0)?0:(last_page - first_page + 1));
+	/* Wait some frames before and one after disabling list access. */
+	mdelay(4);
+	OHCI_INST(dev->controller)->opreg->HcControl &= __cpu_to_le32(~ControlListEnable);
+	mdelay(1);
+
+	/* free memory */
+	ohci_free_ed(head);
+
+	return failure;
+}
+
+/* finalize == 1: if data is of packet aligned size, add a zero length packet */
+static int
+ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
+{
+	int i;
+	usb_debug("bulk: %x bytes from %p, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
+
+	td_t *cur, *next;
+
+	// pages are specified as 4K in OHCI, so don't use getpagesize()
+	int first_page = (unsigned long)data / 4096;
+	int last_page = (unsigned long)(data+dalen-1)/4096;
+	if (last_page < first_page) last_page = first_page;
+	int pages = (dalen==0)?0:(last_page - first_page + 1);
+	int td_count = (pages+1)/2;
+
+	if (finalize && ((dalen % ep->maxpacketsize) == 0)) {
+		td_count++;
+	}
+
+	/* First TD. */
+	td_t *const first_td;
+	ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t));
+	memset((void *)first_td, 0, sizeof(*first_td));
+	cur = next = first_td;
+
+	for (i = 0; i < td_count; ++i) {
+		/* Advance to next TD. */
+		cur = next;
+		cur->config = __cpu_to_le32((ep->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+                        TD_DELAY_INTERRUPT_NOINTR |
+                        TD_TOGGLE_FROM_ED |
+                        TD_CC_NOACCESS);
+		cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data));
+		pages--;
+		if (dalen == 0) {
+			/* magic TD for empty packet transfer */
+			cur->current_buffer_pointer = 0;
+			cur->buffer_end = 0;
+			/* assert((pages == 0) && finalize); */
+		}
+		int consumed = (4096 - ((unsigned long)data % 4096));
+		if (consumed >= dalen) {
+			// end of data is within same page
+			cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1));
+			dalen = 0;
+			/* assert(pages == finalize); */
+		} else {
+			dalen -= consumed;
+			data += consumed;
+			pages--;
+			int second_page_size = dalen;
+			if (dalen > 4096) {
+				second_page_size = 4096;
+			}
+			cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1));
+			dalen -= second_page_size;
+			data += second_page_size;
+		}
+		/* One more TD. */
+		ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t));
+		memset((void *)next, 0, sizeof(*next));
+		/* Linked to the previous. */
+		cur->next_td = __cpu_to_le32(virt_to_phys(next));
+	}
+
+	/* Write done head after last TD. */
+	cur->config &= __cpu_to_le32(~TD_DELAY_INTERRUPT_MASK);
+	/* Advance to final, dummy TD. */
+	cur = next;
+
+	/* Data structures */
+	ed_t *head;
+	ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t));
+	memset((void*)head, 0, sizeof(*head));
+	head->config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) |
+		((ep->endpoint & 0xf) << ED_EP_SHIFT) |
+		(((ep->direction==IN)?OHCI_IN:OHCI_OUT) << ED_DIR_SHIFT) |
+		(ep->dev->speed?ED_LOWSPEED:0) |
+		(ep->maxpacketsize << ED_MPS_SHIFT));
+	head->tail_pointer = __cpu_to_le32(virt_to_phys(cur));
+	head->head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle?ED_TOGGLE:0));
+
+	usb_debug("doing bulk transfer with %x(%x). first_td at %lx, last %lx\n",
+		__le32_to_cpu(head->config) & ED_FUNC_MASK,
+		(__le32_to_cpu(head->config) & ED_EP_MASK) >> ED_EP_SHIFT,
+		virt_to_phys(first_td), virt_to_phys(cur));
+
+	/* activate schedule */
+	OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = __cpu_to_le32(virt_to_phys(head));
+	OHCI_INST(ep->dev->controller)->opreg->HcControl |= __cpu_to_le32(BulkListEnable);
+	OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(BulkListFilled);
+
+	int failure = wait_for_ed(ep->dev, head,
+			(dalen==0)?0:(last_page - first_page + 1));
+	/* Wait some frames before and one after disabling list access. */
+	mdelay(4);
+	OHCI_INST(ep->dev->controller)->opreg->HcControl &= __cpu_to_le32(~BulkListEnable);
+	mdelay(1);
+
+	ep->toggle = __le32_to_cpu(head->head_pointer) & ED_TOGGLE;
+
+	/* free memory */
+	ohci_free_ed(head);
+
+	if (failure) {
+		/* try cleanup */
+		clear_stall(ep);
+	}
+
+	return failure;
+}
+
+
+struct _intr_queue;
+
+struct _intrq_td {
+	volatile td_t		td;
+	u8			*data;
+	struct _intrq_td	*next;
+	struct _intr_queue	*intrq;
+};
+
+struct _intr_queue {
+	volatile ed_t		ed;
+	struct _intrq_td	*head;
+	struct _intrq_td	*tail;
+	u8			*data;
+	int			reqsize;
+	endpoint_t		*endp;
+	unsigned int		remaining_tds;
+	int			destroy;
+};
+
+typedef struct _intrq_td intrq_td_t;
+typedef struct _intr_queue intr_queue_t;
+
+#define INTRQ_TD_FROM_TD(x) ((intrq_td_t *)x)
+
+static void
+ohci_fill_intrq_td(intrq_td_t *const td, intr_queue_t *const intrq,
+		   u8 *const data)
+{
+	memset(td, 0, sizeof(*td));
+	td->td.config = __cpu_to_le32(TD_QUEUETYPE_INTR |
+		(intrq->endp->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) |
+		TD_DELAY_INTERRUPT_ZERO |
+		TD_TOGGLE_FROM_ED |
+		TD_CC_NOACCESS);
+	td->td.current_buffer_pointer = __cpu_to_le32(virt_to_phys(data));
+	td->td.buffer_end = __cpu_to_le32(virt_to_phys(data) + intrq->reqsize - 1);
+	td->intrq = intrq;
+	td->data = data;
+}
+
+/* create and hook-up an intr queue into device schedule */
+static void *
+ohci_create_intr_queue(endpoint_t *const ep, const int reqsize,
+		       const int reqcount, const int reqtiming)
+{
+	int i;
+	intrq_td_t *first_td = NULL, *last_td = NULL;
+
+	if (reqsize > 4096)
+		return NULL;
+
+	intr_queue_t *const intrq;
+	ofmem_posix_memalign((void **)&intrq, sizeof(intrq->ed), sizeof(*intrq));
+	memset(intrq, 0, sizeof(*intrq));
+	intrq->data = (u8 *)malloc(reqcount * reqsize);
+	intrq->reqsize = reqsize;
+	intrq->endp = ep;
+
+	/* Create #reqcount TDs. */
+	u8 *cur_data = intrq->data;
+	for (i = 0; i < reqcount; ++i) {
+		intrq_td_t *const td;
+		ofmem_posix_memalign((void **)&td, sizeof(td->td), sizeof(*td));
+		++intrq->remaining_tds;
+		ohci_fill_intrq_td(td, intrq, cur_data);
+		cur_data += reqsize;
+		if (!first_td)
+			first_td = td;
+		else
+			last_td->td.next_td = __cpu_to_le32(virt_to_phys(&td->td));
+		last_td = td;
+	}
+
+	/* Create last, dummy TD. */
+	intrq_td_t *dummy_td;
+	ofmem_posix_memalign((void **)&dummy_td, sizeof(dummy_td->td), sizeof(*dummy_td));
+	memset(dummy_td, 0, sizeof(*dummy_td));
+	dummy_td->intrq = intrq;
+	if (last_td)
+		last_td->td.next_td = __cpu_to_le32(virt_to_phys(&dummy_td->td));
+	last_td = dummy_td;
+
+	/* Initialize ED. */
+	intrq->ed.config =  __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) |
+		((ep->endpoint & 0xf) << ED_EP_SHIFT) |
+		(((ep->direction == IN) ? OHCI_IN : OHCI_OUT) << ED_DIR_SHIFT) |
+		(ep->dev->speed ? ED_LOWSPEED : 0) |
+		(ep->maxpacketsize << ED_MPS_SHIFT));
+	intrq->ed.tail_pointer = __cpu_to_le32(virt_to_phys(last_td));
+	intrq->ed.head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle ? ED_TOGGLE : 0));
+
+#ifdef USB_DEBUG_ED
+	dump_ed(&intrq->ed);
+#endif
+	/* Insert ED into periodic table. */
+	int nothing_placed	= 1;
+	ohci_t *const ohci	= OHCI_INST(ep->dev->controller);
+	u32 *const intr_table	= ohci->hcca->HccaInterruptTable;
+	const u32 dummy_ptr	= __cpu_to_le32(virt_to_phys(ohci->periodic_ed));
+	for (i = 0; i < 32; i += reqtiming) {
+		/* Advance to the next free position. */
+		while ((i < 32) && (intr_table[i] != dummy_ptr)) ++i;
+		if (i < 32) {
+			usb_debug("Placed endpoint %lx to %d\n", virt_to_phys(&intrq->ed), i);
+			intr_table[i] = __cpu_to_le32(virt_to_phys(&intrq->ed));
+			nothing_placed = 0;
+		}
+	}
+	if (nothing_placed) {
+		usb_debug("Error: Failed to place ohci interrupt endpoint "
+			"descriptor into periodic table: no space left\n");
+		ohci_destroy_intr_queue(ep, intrq);
+		return NULL;
+	}
+
+	return intrq;
+}
+
+/* remove queue from device schedule, dropping all data that came in */
+static void
+ohci_destroy_intr_queue(endpoint_t *const ep, void *const q_)
+{
+	intr_queue_t *const intrq = (intr_queue_t *)q_;
+
+	int i;
+
+	/* Remove interrupt queue from periodic table. */
+	ohci_t *const ohci	= OHCI_INST(ep->dev->controller);
+	u32 *const intr_table	= ohci->hcca->HccaInterruptTable;
+	for (i=0; i < 32; ++i) {
+		if (intr_table[i] == __cpu_to_le32(virt_to_phys(intrq)))
+			intr_table[i] = __cpu_to_le32(virt_to_phys(ohci->periodic_ed));
+	}
+	/* Wait for frame to finish. */
+	mdelay(1);
+
+	/* Free unprocessed TDs. */
+	while ((__le32_to_cpu(intrq->ed.head_pointer) & ~0x3) != __le32_to_cpu(intrq->ed.tail_pointer)) {
+		td_t *const cur_td = (td_t *)phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3);
+		intrq->ed.head_pointer = cur_td->next_td;
+		free(INTRQ_TD_FROM_TD(cur_td));
+		--intrq->remaining_tds;
+	}
+	/* Free final, dummy TD. */
+	free(phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3));
+	/* Free data buffer. */
+	free(intrq->data);
+
+	/* Free TDs already fetched from the done queue. */
+	ohci_process_done_queue(ohci, 1);
+	while (intrq->head) {
+		intrq_td_t *const cur_td = (intrq_td_t *const )__le32_to_cpu(intrq->head);
+		intrq->head = intrq->head->next;
+		free(cur_td);
+		--intrq->remaining_tds;
+	}
+
+	/* Mark interrupt queue to be destroyed.
+	   ohci_process_done_queue() will free the remaining TDs
+	   and finish the interrupt queue off once all TDs are gone. */
+	intrq->destroy = 1;
+
+	/* Save data toggle. */
+	ep->toggle = __le32_to_cpu(intrq->ed.head_pointer) & ED_TOGGLE;
+}
+
+/* read one intr-packet from queue, if available. extend the queue for new input.
+   return NULL if nothing new available.
+   Recommended use: while (data=poll_intr_queue(q)) process(data);
+ */
+static u8 *
+ohci_poll_intr_queue(void *const q_)
+{
+	intr_queue_t *const intrq = (intr_queue_t *)q_;
+
+	u8 *data = NULL;
+
+	/* Process done queue first, then check if we have work to do. */
+	ohci_process_done_queue(OHCI_INST(intrq->endp->dev->controller), 0);
+
+	if (intrq->head) {
+		/* Save pointer to processed TD and advance. */
+		intrq_td_t *const cur_td = intrq->head;
+		intrq->head = cur_td->next;
+
+		/* Return data buffer of this TD. */
+		data = cur_td->data;
+
+		/* Requeue this TD (i.e. copy to dummy and requeue as dummy). */
+		intrq_td_t *const dummy_td =
+			INTRQ_TD_FROM_TD(phys_to_virt(__le32_to_cpu(intrq->ed.tail_pointer)));
+		ohci_fill_intrq_td(dummy_td, intrq, data);
+		/* Reset all but intrq pointer (i.e. init as dummy). */
+		memset(cur_td, 0, sizeof(*cur_td));
+		cur_td->intrq = intrq;
+		/* Insert into interrupt queue as dummy. */
+		dummy_td->td.next_td = __le32_to_cpu(virt_to_phys(&cur_td->td));
+		intrq->ed.tail_pointer = __le32_to_cpu(virt_to_phys(&cur_td->td));
+	}
+
+	return data;
+}
+
+static void
+ohci_process_done_queue(ohci_t *const ohci, const int spew_debug)
+{
+	int i, j;
+
+	/* Temporary queue of interrupt queue TDs (to reverse order). */
+	intrq_td_t *temp_tdq = NULL;
+
+	/* Check if done head has been written. */
+	if (!(READ_OPREG(ohci, HcInterruptStatus) & WritebackDoneHead))
+		return;
+	/* Fetch current done head.
+	   Lsb is only interesting for hw interrupts. */
+	u32 phys_done_queue = __le32_to_cpu(ohci->hcca->HccaDoneHead) & ~1;
+	/* Tell host controller, he may overwrite the done head pointer. */
+	ohci->opreg->HcInterruptStatus = __cpu_to_le32(WritebackDoneHead);
+
+	i = 0;
+	/* Process done queue (it's in reversed order). */
+	while (phys_done_queue) {
+		td_t *const done_td = (td_t *)phys_to_virt(phys_done_queue);
+
+		/* Advance pointer to next TD. */
+		phys_done_queue = __le32_to_cpu(done_td->next_td);
+
+		switch (__le32_to_cpu(done_td->config) & TD_QUEUETYPE_MASK) {
+		case TD_QUEUETYPE_ASYNC:
+			/* Free processed async TDs. */
+                  free((void *)done_td);
+			break;
+		case TD_QUEUETYPE_INTR: {
+			intrq_td_t *const td = INTRQ_TD_FROM_TD(done_td);
+			intr_queue_t *const intrq = td->intrq;
+			/* Check if the corresponding interrupt
+			   queue is still beeing processed. */
+			if (intrq->destroy) {
+				/* Free this TD, and */
+				free(td);
+				--intrq->remaining_tds;
+				/* the interrupt queue if it has no more TDs. */
+				if (!intrq->remaining_tds)
+					free(intrq);
+				usb_debug("Freed TD from orphaned interrupt "
+					  "queue, %d TDs remain.\n",
+					  intrq->remaining_tds);
+			} else {
+				/* Save done TD to be processed. */
+				td->next = temp_tdq;
+				temp_tdq = td;
+			}
+			break;
+		}
+		default:
+			break;
+		}
+		++i;
+	}
+	if (spew_debug)
+		usb_debug("Processed %d done TDs.\n", i);
+
+	j = 0;
+	/* Process interrupt queue TDs in right order. */
+	while (temp_tdq) {
+		/* Save pointer of current TD and advance. */
+		intrq_td_t *const cur_td = temp_tdq;
+		temp_tdq = temp_tdq->next;
+
+		/* The interrupt queue for the current TD. */
+		intr_queue_t *const intrq = cur_td->intrq;
+		/* Append to interrupt queue. */
+		if (!intrq->head) {
+			/* First element. */
+			intrq->head = intrq->tail = cur_td;
+		} else {
+			/* Insert at tail. */
+			intrq->tail->next = cur_td;
+			intrq->tail = cur_td;
+		}
+		/* It's always the last element. */
+		cur_td->next = NULL;
+		++j;
+	}
+	if (spew_debug)
+		usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j);
+}
+
+int ob_usb_ohci_init (const char *path, phys_addr_t addr)
+{
+	hci_t *ctrl;
+	int i;
+
+	usb_debug("ohci_init: %s addr = %x\n", path, addr);
+	ctrl = ohci_init((void *)addr);
+	if (!ctrl)
+		return 0;
+
+	/* Init ports */
+	usb_poll();
+
+	/* Look for a keyboard */
+	for (i = 0; i < 128; i++) {
+		if (ctrl->devices[i] && ctrl->devices[i]->configuration) {
+			configuration_descriptor_t *cd;
+			interface_descriptor_t *intf;
+
+			cd = (configuration_descriptor_t *)ctrl->devices[i]->configuration;
+			intf = (interface_descriptor_t *)(ctrl->devices[i]->configuration + cd->bLength);
+			usb_debug("Device at port %d is class %d\n", i, intf->bInterfaceClass);
+			if (intf->bInterfaceClass == hid_device &&
+			    intf->bInterfaceSubClass == hid_subclass_boot &&
+			    intf->bInterfaceProtocol == hid_boot_proto_keyboard ) {
+				break;
+			}
+		}
+	}
+	if ( i < 128 )
+		ob_usb_hid_add_keyboard(path);
+
+	return 1;
+}
diff --git a/openbios-devel/drivers/usbohci.h b/openbios-devel/drivers/usbohci.h
new file mode 100644
index 0000000..6903328
--- /dev/null
+++ b/openbios-devel/drivers/usbohci.h
@@ -0,0 +1,45 @@
+/*
+ * Driver for USB OHCI ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * 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.
+ */
+
+#ifndef __OHCI_H
+#define __OHCI_H
+
+#include "config.h"
+#include "usbohci_private.h"
+
+hci_t *ohci_pci_init (u32 addr);
+hci_t *ohci_init (void *bar);
+
+void ohci_rh_init (usbdev_t *dev);
+
+#endif
diff --git a/openbios-devel/drivers/usbohci_private.h b/openbios-devel/drivers/usbohci_private.h
new file mode 100644
index 0000000..b3a723e
--- /dev/null
+++ b/openbios-devel/drivers/usbohci_private.h
@@ -0,0 +1,270 @@
+/*
+ * Driver for USB OHCI ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * 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.
+ */
+
+#ifndef __OHCI_PRIVATE_H
+#define __OHCI_PRIVATE_H
+
+#include "libc/byteorder.h"
+#include "usb.h"
+
+#define READ_OPREG(ohci, field) (__le32_to_cpu((ohci)->opreg->field))
+#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit))
+
+	// FIXME: fake
+	typedef enum { CMD} reg;
+
+	enum {
+		NumberDownstreamPorts = 1<<0,
+		PowerSwitchingMode = 1<<8,
+		NoPowerSwitching = 1<<9,
+		DeviceType = 1<<10,
+		OverCurrentProtectionMode = 1<<11,
+		NoOverCurrentProtection = 1<<12,
+		PowerOnToPowerGoodTime = 1<<24
+	} HcRhDescriptorAReg;
+
+	enum {
+		NumberDownstreamPortsMask = MASK(0, 8),
+		PowerOnToPowerGoodTimeMask = MASK(24, 8)
+	} HcRhDescriptorAMask;
+
+	enum {
+		DeviceRemovable = 1<<0,
+		PortPowerControlMask = 1<<16
+	} HcRhDescriptorBReg;
+
+	enum {
+		CurrentConnectStatus		= 1<<0,
+		PortEnableStatus		= 1<<1,
+		PortSuspendStatus		= 1<<2,
+		PortOverCurrentIndicator	= 1<<3,
+		PortResetStatus			= 1<<4,
+		PortPowerStatus			= 1<<8,
+		LowSpeedDeviceAttached		= 1<<9,
+		ConnectStatusChange		= 1<<16,
+		PortEnableStatusChange		= 1<<17,
+		PortSuspendStatusChange		= 1<<18,
+		PortOverCurrentIndicatorChange	= 1<<19,
+		PortResetStatusChange		= 1<<20
+	} HcRhPortStatusRead;
+	enum {
+		ClearPortEnable			= 1<<0,
+		SetPortEnable			= 1<<1,
+		SetPortSuspend			= 1<<2,
+		ClearSuspendStatus		= 1<<3,
+		SetPortReset			= 1<<4,
+		SetPortPower			= 1<<8,
+		ClearPortPower			= 1<<9,
+	} HcRhPortStatusSet;
+
+	enum {
+		LocalPowerStatus = 1<<0,
+		OverCurrentIndicator = 1<<1,
+		DeviceRemoteWakeupEnable = 1<<15,
+		LocalPowerStatusChange = 1<<16,
+		OverCurrentIndicatorChange = 1<<17,
+		ClearRemoteWakeupEnable = 1<<31
+	} HcRhStatusReg;
+
+	enum {
+		FrameInterval = 1<<0,
+		FSLargestDataPacket = 1<<16,
+		FrameIntervalToggle = 1<<31
+	} HcFmIntervalOffset;
+	enum {
+		FrameIntervalMask = MASK(0, 14),
+		FSLargestDataPacketMask = MASK(16, 15),
+		FrameIntervalToggleMask = MASK(31, 1)
+	} HcFmIntervalMask;
+
+	enum {
+		ControlBulkServiceRatio = 1<<0,
+		PeriodicListEnable = 1<<2,
+		IsochronousEnable = 1<<3,
+		ControlListEnable = 1<<4,
+		BulkListEnable = 1<<5,
+		HostControllerFunctionalState = 1<<6,
+		InterruptRouting = 1<<8,
+		RemoteWakeupConnected = 1<<9,
+		RemoteWakeupEnable = 1<<10
+	} HcControlReg;
+
+	enum {
+		ControlBulkServiceRatioMask = MASK(0, 2),
+		HostControllerFunctionalStateMask = MASK(6, 2)
+	} HcControlMask;
+
+	enum {
+		USBReset = 0*HostControllerFunctionalState,
+		USBResume = 1*HostControllerFunctionalState,
+		USBOperational = 2*HostControllerFunctionalState,
+		USBSuspend = 3*HostControllerFunctionalState
+	};
+
+	enum {
+		HostControllerReset = 1<<0,
+		ControlListFilled = 1<<1,
+		BulkListFilled = 1<<2,
+		OwnershipChangeRequest = 1<<3,
+		SchedulingOverrunCount = 1<<16
+	} HcCommandStatusReg;
+
+	enum {
+		SchedulingOverrunCountMask = MASK(16, 2)
+	} HcCommandStatusMask;
+
+	enum {
+		FrameRemaining = 1<<0,
+		FrameRemainingToggle = 1<<31
+	} HcFmRemainingReg;
+
+	enum {
+		SchedulingOverrung = 1<<0,
+		WritebackDoneHead = 1<<1,
+		StartofFrame = 1<<2,
+		ResumeDetected = 1<<3,
+		UnrecoverableError = 1<<4,
+		FrameNumberOverflow = 1<<5,
+		RootHubStatusChange = 1<<6,
+		OwnershipChange = 1<<30
+	} HcInterruptStatusReg;
+
+     typedef struct {
+	// Control and Status Partition
+	volatile u32 HcRevision;
+	volatile u32 HcControl;
+	volatile u32 HcCommandStatus;
+	volatile u32 HcInterruptStatus;
+	volatile u32 HcInterruptEnable;
+	volatile u32 HcInterruptDisable;
+
+	// Memory Pointer Partition
+	volatile u32 HcHCCA;
+	volatile u32 HcPeriodCurrentED;
+	volatile u32 HcControlHeadED;
+	volatile u32 HcControlCurrentED;
+	volatile u32 HcBulkHeadED;
+	volatile u32 HcBulkCurrentED;
+	volatile u32 HcDoneHead;
+
+	// Frame Counter Partition
+	volatile u32 HcFmInterval;
+	volatile u32 HcFmRemaining;
+	volatile u32 HcFmNumber;
+	volatile u32 HcPeriodicStart;
+	volatile u32 HcLSThreshold;
+
+	// Root Hub Partition
+	volatile u32 HcRhDescriptorA;
+	volatile u32 HcRhDescriptorB;
+	volatile u32 HcRhStatus;
+	/* all bits in HcRhPortStatus registers are R/WC, so
+	   _DO NOT_ use |= to set the bits,
+	   this clears the entire state */
+	volatile u32 HcRhPortStatus[];
+     } __attribute__ ((packed)) opreg_t;
+
+	typedef struct { /* should be 256 bytes according to spec */
+		u32 HccaInterruptTable[32];
+		volatile u16 HccaFrameNumber;
+		volatile u16 HccaPad1;
+		volatile u32 HccaDoneHead;
+		u8 reserved[116]; /* pad according to spec */
+		u8 what[4]; /* really pad to 256 as spec only covers 252 */
+	} __attribute__ ((packed)) hcca_t;
+
+	typedef volatile struct {
+		u32 config;
+		u32 tail_pointer;
+		u32 head_pointer;
+		u32 next_ed;
+	} __attribute__ ((packed)) ed_t;
+#define ED_HALTED 1
+#define ED_TOGGLE 2
+
+#define ED_FUNC_SHIFT 0
+#define ED_FUNC_MASK MASK(0, 7)
+#define ED_EP_SHIFT 7
+#define ED_EP_MASK MASK(7, 4)
+#define ED_DIR_SHIFT 11
+#define ED_DIR_MASK MASK(11, 2)
+#define ED_LOWSPEED (1 << 13)
+#define ED_MPS_SHIFT 16
+
+	typedef volatile struct {
+		u32 config;
+		u32 current_buffer_pointer;
+		u32 next_td;
+		u32 buffer_end;
+	} __attribute__ ((packed)) td_t;
+/*
+ * Bits 0 through 17 of .config won't be interpreted by the host controller
+ * (HC) and, after processing the TD, the HC has to ensure those bits have
+ * the same state as before. So we are free to use those bits for our own
+ * purpose.
+ */
+#define TD_QUEUETYPE_SHIFT	0
+#define TD_QUEUETYPE_MASK	MASK(TD_QUEUETYPE_SHIFT, 2)
+#define TD_QUEUETYPE_ASYNC	(0 << TD_QUEUETYPE_SHIFT)
+#define TD_QUEUETYPE_INTR	(1 << TD_QUEUETYPE_SHIFT)
+
+#define TD_DIRECTION_SHIFT 19
+#define TD_DIRECTION_MASK MASK(TD_DIRECTION_SHIFT, 2)
+#define TD_DIRECTION_SETUP OHCI_SETUP << TD_DIRECTION_SHIFT
+#define TD_DIRECTION_IN OHCI_IN << TD_DIRECTION_SHIFT
+#define TD_DIRECTION_OUT OHCI_OUT << TD_DIRECTION_SHIFT
+#define TD_DELAY_INTERRUPT_SHIFT	21
+#define TD_DELAY_INTERRUPT_MASK		MASK(TD_DELAY_INTERRUPT_SHIFT, 3)
+#define TD_DELAY_INTERRUPT_ZERO		0
+#define TD_DELAY_INTERRUPT_NOINTR	(7 << TD_DELAY_INTERRUPT_SHIFT)
+#define TD_TOGGLE_DATA0 0
+#define TD_TOGGLE_DATA1 (1 << 24)
+#define TD_TOGGLE_FROM_ED 0
+#define TD_TOGGLE_FROM_TD (1 << 25)
+#define TD_CC_SHIFT 28
+#define TD_CC_MASK MASK(TD_CC_SHIFT, 4)
+#define TD_CC_NOERR 0
+#define TD_CC_NOACCESS (14 << TD_CC_SHIFT) /* the lower of the two values, so "no access" can be tested with >= */
+
+#define OHCI_INST(controller) ((ohci_t*)((controller)->instance))
+
+	typedef struct ohci {
+		opreg_t *opreg;
+		hcca_t *hcca;
+		usbdev_t *roothub;
+		ed_t *periodic_ed;
+	} ohci_t;
+
+	typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t;
+
+#endif
diff --git a/openbios-devel/drivers/usbohci_rh.c b/openbios-devel/drivers/usbohci_rh.c
new file mode 100644
index 0000000..55503be
--- /dev/null
+++ b/openbios-devel/drivers/usbohci_rh.c
@@ -0,0 +1,212 @@
+/*
+ * Driver for USB OHCI Root Hubs ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2010 Patrick Georgi
+ *
+ * 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 "timer.h"
+#include "usbohci_private.h"
+#include "usbohci.h"
+
+typedef struct {
+	int numports;
+	int *port;
+} rh_inst_t;
+
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+
+static void
+ohci_rh_enable_port (usbdev_t *dev, int port)
+{
+	/* Reset RH port should hold 50ms with pulses of at least 10ms and
+	 * gaps of at most 3ms (usb20 spec 7.1.7.5).
+	 * After reset, the port will be enabled automatically (ohci spec
+	 * 7.4.4).
+	 */
+	int total_delay = 100; /* 100 * 500us == 50ms */
+	while (total_delay > 0) {
+		if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port])
+					& CurrentConnectStatus))
+			return;
+
+		/* start reset */
+		OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] =
+			__cpu_to_le32(SetPortReset);
+		int timeout = 200; /* timeout after 200 * 500us == 100ms */
+		while ((READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port])
+					& PortResetStatus)
+				&& timeout--) {
+			udelay(500); total_delay--;
+		}
+		if (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port])
+				& PortResetStatus) {
+			usb_debug("Warning: root-hub port reset timed out.\n");
+			break;
+		}
+		if ((200-timeout) < 20) {
+			usb_debug("Warning: port reset too short: %dms; "
+					"should be at least 10ms.\n",
+					(200-timeout)/2);
+                        total_delay = 0; /* can happen on QEMU */
+		}
+		/* clear reset status change */
+		OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] =
+			__cpu_to_le32(PortResetStatusChange);
+		usb_debug ("rh port reset finished after %dms.\n", (200-timeout)/2);
+	}
+}
+
+/* disable root hub */
+static void
+ohci_rh_disable_port (usbdev_t *dev, int port)
+{
+	OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] =
+		__cpu_to_le32(ClearPortEnable); // disable port
+	int timeout = 50; /* timeout after 50 * 100us == 5ms */
+	while ((READ_OPREG(OHCI_INST (dev->controller), HcRhPortStatus[port])
+				& PortEnableStatus)
+			&& timeout--) {
+		udelay(100);
+	}
+}
+
+static void
+ohci_rh_scanport (usbdev_t *dev, int port)
+{
+	if (port >= RH_INST(dev)->numports) {
+		usb_debug("Invalid port %d\n", port);
+		return;
+	}
+
+	/* device registered, and device change logged, so something must have happened */
+	if (RH_INST (dev)->port[port] != -1) {
+		usb_detach_device(dev->controller, RH_INST (dev)->port[port]);
+		RH_INST (dev)->port[port] = -1;
+	}
+
+	/* no device attached
+	   previously registered devices are detached, nothing left to do */
+	if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & CurrentConnectStatus))
+		return;
+
+	// clear port state change
+	OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] = __cpu_to_le32(ConnectStatusChange);
+	ohci_rh_enable_port (dev, port);
+
+	mdelay(100); // wait for signal to stabilize
+
+	if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & PortEnableStatus)) {
+		usb_debug ("port enable failed\n");
+		return;
+	}
+
+	int speed = (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & LowSpeedDeviceAttached) != 0;
+	RH_INST (dev)->port[port] = usb_attach_device(dev->controller, dev->address, port, speed);
+}
+
+static int
+ohci_rh_report_port_changes (usbdev_t *dev)
+{
+	ohci_t *const ohcic = OHCI_INST (dev->controller);
+
+	int i;
+
+	for (i = 0; i < RH_INST(dev)->numports; i++) {
+		// maybe detach+attach happened between two scans?
+		if (READ_OPREG(ohcic, HcRhPortStatus[i]) & ConnectStatusChange) {
+			ohcic->opreg->HcRhPortStatus[i] = __cpu_to_le32(ConnectStatusChange);
+			usb_debug("attachment change on port %d\n", i);
+			return i;
+		}
+	}
+
+	// no change
+	return -1;
+}
+
+static void
+ohci_rh_destroy (usbdev_t *dev)
+{
+	int i;
+	for (i = 0; i < RH_INST (dev)->numports; i++)
+		ohci_rh_disable_port (dev, i);
+	free (RH_INST (dev));
+}
+
+static void
+ohci_rh_poll (usbdev_t *dev)
+{
+	ohci_t *const ohcic = OHCI_INST (dev->controller);
+
+	int port;
+
+	/* Check if anything changed. */
+	if (!(READ_OPREG(ohcic, HcInterruptStatus) & RootHubStatusChange))
+		return;
+	ohcic->opreg->HcInterruptStatus = __cpu_to_le32(RootHubStatusChange);
+	usb_debug("root hub status change\n");
+
+	/* Scan ports with changed connection status. */
+	while ((port = ohci_rh_report_port_changes (dev)) != -1)
+		ohci_rh_scanport (dev, port);
+}
+
+void
+ohci_rh_init (usbdev_t *dev)
+{
+	int i;
+
+	dev->destroy = ohci_rh_destroy;
+	dev->poll = ohci_rh_poll;
+
+	dev->data = malloc (sizeof (rh_inst_t));
+	if (!dev->data) {
+		printk("Not enough memory for OHCI RH.\n");
+		return;
+	}
+
+	RH_INST (dev)->numports = READ_OPREG(OHCI_INST(dev->controller), HcRhDescriptorA) & NumberDownstreamPortsMask;
+	RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports);
+	usb_debug("%d ports registered\n", RH_INST (dev)->numports);
+
+	for (i = 0; i < RH_INST (dev)->numports; i++) {
+		ohci_rh_enable_port (dev, i);
+		RH_INST (dev)->port[i] = -1;
+	}
+
+	/* we can set them here because a root hub _really_ shouldn't
+	   appear elsewhere */
+	dev->address = 0;
+	dev->hub = -1;
+	dev->port = -1;
+
+	usb_debug("rh init done\n");
+}
diff --git a/openbios-devel/include/drivers/pci.h b/openbios-devel/include/drivers/pci.h
index 1f0af6c..2eb5685 100644
--- a/openbios-devel/include/drivers/pci.h
+++ b/openbios-devel/include/drivers/pci.h
@@ -188,6 +188,7 @@ extern const pci_arch_t *arch;
 #define PCI_DEVICE_ID_APPLE_UNI_N_PCI    0x001f
 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP    0x0020
 #define PCI_DEVICE_ID_APPLE_UNI_N_KEYL   0x0022
+#define PCI_DEVICE_ID_APPLE_KEYL_USB     0x003f
 #define PCI_DEVICE_ID_APPLE_U3_AGP       0x004b
 
 #define PCI_VENDOR_ID_SUN                0x108e
diff --git a/openbios-devel/include/drivers/usb.h b/openbios-devel/include/drivers/usb.h
new file mode 100644
index 0000000..d2d94d2
--- /dev/null
+++ b/openbios-devel/include/drivers/usb.h
@@ -0,0 +1,8 @@
+#ifndef USB_H
+#define USB_H
+
+int ob_usb_ohci_init(const char *path, phys_addr_t addr);
+void ob_usb_hid_add_keyboard(const char *path);
+int usb_exit(void);
+
+#endif /* USB_H */
-- 
1.8.1.5




More information about the OpenBIOS mailing list