[OpenBIOS] [PATCH v2] 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>
---

 v2: improve init and teardown by moving pci configuration to the driver
     and giving up using the controller on quiesce interface call also
     resetting it to avoid runaway DMA until the OS driver takes over

 openbios-devel/arch/ppc/qemu/methods.c        |   2 +
 openbios-devel/config/examples/ppc_config.xml |   3 +
 openbios-devel/drivers/Kconfig                |  20 +
 openbios-devel/drivers/build.xml              |   4 +
 openbios-devel/drivers/pci.c                  |  11 +
 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              | 926 ++++++++++++++++++++++++++
 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 +
 17 files changed, 3044 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/arch/ppc/qemu/methods.c b/openbios-devel/arch/ppc/qemu/methods.c
index 8219cff..fd993da 100644
--- a/openbios-devel/arch/ppc/qemu/methods.c
+++ b/openbios-devel/arch/ppc/qemu/methods.c
@@ -25,6 +25,7 @@
 #include "qemu/qemu.h"
 #include "libopenbios/ofmem.h"
 #include "arch/ppc/processor.h"
+#include "drivers/usb.h"
 
 /************************************************************************/
 /*	RTAS (run-time abstraction services)				*/
@@ -112,6 +113,7 @@ DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" );
 static void
 ciface_quiesce( unsigned long args[], unsigned long ret[] )
 {
+	usb_exit();
 #if 0
 	unsigned long msr;
 	/* This seems to be the correct thing to do - but I'm not sure */
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..1154426 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,14 @@ 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
+    ob_usb_ohci_init(config->path, 0x80000000 | config->dev);
+#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..8c9e728
--- /dev/null
+++ b/openbios-devel/drivers/usbohci.c
@@ -0,0 +1,926 @@
+/*
+ * 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/pci.h"
+#include "pci.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;
+}
+
+hci_t *
+ohci_pci_init (pci_addr addr)
+{
+	uint16_t cmd;
+	u32 reg_base;
+
+	cmd = pci_config_read16(addr, PCI_COMMAND);
+	cmd |= PCI_COMMAND_BUS_MASTER;
+	pci_config_write16(addr, PCI_COMMAND, cmd);
+
+	/* 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_config_read32 (addr, PCI_BASE_ADDR_0) & 0xfffff000;
+
+	return ohci_init((void *)(unsigned long)reg_base);
+}
+
+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);
+	controller->reset (controller);
+	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, uint32_t addr)
+{
+	hci_t *ctrl;
+	int i;
+
+	usb_debug("ohci_init: %s addr = %x\n", path, addr);
+	ctrl = ohci_pci_init(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..143ed27
--- /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, uint32_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