summaryrefslogtreecommitdiff
path: root/roms
diff options
context:
space:
mode:
authorChanho Park <chanho61.park@samsung.com>2014-12-10 15:42:55 +0900
committerChanho Park <chanho61.park@samsung.com>2014-12-10 15:42:55 +0900
commit0d6a2f7e595218b5632ba7005128470e65138951 (patch)
tree596b09930ef1538e6606450e2d8b88ec2e296a9b /roms
parent16b1353a36171ae06d63fd309f4772dbfb1da113 (diff)
downloadqemu-0d6a2f7e595218b5632ba7005128470e65138951.tar.gz
qemu-0d6a2f7e595218b5632ba7005128470e65138951.tar.bz2
qemu-0d6a2f7e595218b5632ba7005128470e65138951.zip
Imported Upstream version 2.2.0upstream/2.2.1upstream/2.2.0
Diffstat (limited to 'roms')
-rw-r--r--roms/openbios/arch/ppc/qemu/methods.c2
-rw-r--r--roms/openbios/arch/ppc/qemu/tree.fs3
-rw-r--r--roms/openbios/arch/sparc64/openbios.c18
-rw-r--r--roms/openbios/config/examples/ppc_config.xml3
-rw-r--r--roms/openbios/drivers/Kconfig20
-rw-r--r--roms/openbios/drivers/build.xml4
-rw-r--r--roms/openbios/drivers/ide.c7
-rw-r--r--roms/openbios/drivers/macio.c72
-rw-r--r--roms/openbios/drivers/pc_serial.c23
-rw-r--r--roms/openbios/drivers/pci.c246
-rw-r--r--roms/openbios/drivers/pci.h1
-rw-r--r--roms/openbios/drivers/pci_database.c20
-rw-r--r--roms/openbios/drivers/pci_database.h1
-rw-r--r--roms/openbios/drivers/tcx.fs139
-rw-r--r--roms/openbios/drivers/usb.c587
-rw-r--r--roms/openbios/drivers/usb.h357
-rw-r--r--roms/openbios/drivers/usbhid.c579
-rw-r--r--roms/openbios/drivers/usbohci.c926
-rw-r--r--roms/openbios/drivers/usbohci.h45
-rw-r--r--roms/openbios/drivers/usbohci_private.h270
-rw-r--r--roms/openbios/drivers/usbohci_rh.c212
-rw-r--r--roms/openbios/include/arch/common/fw_cfg.h1
-rw-r--r--roms/openbios/include/drivers/pci.h1
-rw-r--r--roms/openbios/include/drivers/usb.h8
-rw-r--r--roms/openbios/libopenbios/ofmem_common.c4
-rw-r--r--roms/seabios/.version2
-rw-r--r--roms/seabios/Makefile4
-rw-r--r--roms/seabios/src/boot.c8
-rw-r--r--roms/seabios/src/fw/pciinit.c34
-rw-r--r--roms/seabios/src/hw/megasas.c4
-rw-r--r--roms/seabios/src/hw/usb-ehci.c2
-rw-r--r--roms/seabios/src/stacks.c2
-rw-r--r--roms/seabios/vgasrc/vgabios.c18
33 files changed, 3399 insertions, 224 deletions
diff --git a/roms/openbios/arch/ppc/qemu/methods.c b/roms/openbios/arch/ppc/qemu/methods.c
index 8219cff90..fd993daa9 100644
--- a/roms/openbios/arch/ppc/qemu/methods.c
+++ b/roms/openbios/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/roms/openbios/arch/ppc/qemu/tree.fs b/roms/openbios/arch/ppc/qemu/tree.fs
index 1b9c2934f..1ed838397 100644
--- a/roms/openbios/arch/ppc/qemu/tree.fs
+++ b/roms/openbios/arch/ppc/qemu/tree.fs
@@ -12,7 +12,8 @@ include config.fs
\ -------------------------------------------------------------
" /" find-device
-
+\ Apple calls the root node device-tree
+" device-tree" device-name
[IFDEF] CONFIG_PPC64 2 [ELSE] 1 [THEN] encode-int " #address-cells" property
1 encode-int " #size-cells" property
h# 05f5e100 encode-int " clock-frequency" property
diff --git a/roms/openbios/arch/sparc64/openbios.c b/roms/openbios/arch/sparc64/openbios.c
index 9cc1ac223..3a3614652 100644
--- a/roms/openbios/arch/sparc64/openbios.c
+++ b/roms/openbios/arch/sparc64/openbios.c
@@ -116,6 +116,20 @@ sparc64_reset_all(void)
: : "r" (val), "r" (addr) : "memory");
}
+/* PCI Target Address Space Register (see UltraSPARC IIi User's Manual
+ section 19.3.0.4) */
+#define PBM_PCI_TARGET_AS 0x2028
+#define PBM_PCI_TARGET_AS_CD_ENABLE 0x40
+
+static void
+sparc64_set_tas_register(unsigned long val)
+{
+ unsigned long addr = APB_SPECIAL_BASE + PBM_PCI_TARGET_AS;
+
+ asm("stxa %0, [%1] 0x15\n\t"
+ : : "r" (val), "r" (addr) : "memory");
+}
+
static void cpu_generic_init(const struct cpudef *cpu, uint32_t clock_frequency)
{
unsigned long iu_version;
@@ -574,6 +588,10 @@ arch_init( void )
modules_init();
#ifdef CONFIG_DRIVER_PCI
ob_pci_init();
+
+ /* Set TAS register to match the virtual-dma properties
+ set during sabre configure */
+ sparc64_set_tas_register(PBM_PCI_TARGET_AS_CD_ENABLE);
#endif
nvconf_init();
device_end();
diff --git a/roms/openbios/config/examples/ppc_config.xml b/roms/openbios/config/examples/ppc_config.xml
index 621b65d9c..4c14eb6c8 100644
--- a/roms/openbios/config/examples/ppc_config.xml
+++ b/roms/openbios/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/roms/openbios/drivers/Kconfig b/roms/openbios/drivers/Kconfig
index 7be334b1c..3bebc0293 100644
--- a/roms/openbios/drivers/Kconfig
+++ b/roms/openbios/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/roms/openbios/drivers/build.xml b/roms/openbios/drivers/build.xml
index edec6b512..bd1abd358 100644
--- a/roms/openbios/drivers/build.xml
+++ b/roms/openbios/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/roms/openbios/drivers/ide.c b/roms/openbios/drivers/ide.c
index 0e0f0cf24..327c64a40 100644
--- a/roms/openbios/drivers/ide.c
+++ b/roms/openbios/drivers/ide.c
@@ -1526,6 +1526,10 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels)
u32 props[8];
struct ide_channel *chan;
+ /* IDE ports on Macs are numbered from 3.
+ * Also see comments in macio.c:openpic_init() */
+ current_channel = 3;
+
for (i = 0; i < nb_channels; i++, current_channel++) {
chan = malloc(sizeof(struct ide_channel));
@@ -1574,7 +1578,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels)
dnode = find_dev(nodebuff);
- set_property(dnode, "compatible", "heathrow-ata", 13);
+ set_property(dnode, "compatible", (is_oldworld() ?
+ "heathrow-ata" : "keylargo-ata"), 13);
props[0] = 0x00000526;
props[1] = 0x00000085;
diff --git a/roms/openbios/drivers/macio.c b/roms/openbios/drivers/macio.c
index 00d714990..f54bc86dc 100644
--- a/roms/openbios/drivers/macio.c
+++ b/roms/openbios/drivers/macio.c
@@ -27,20 +27,31 @@
#define NW_IO_NVRAM_SIZE 0x00004000
#define NW_IO_NVRAM_OFFSET 0xfff04000
-#define NW_IO_NVRAM_SHIFT 1
#define IO_OPENPIC_SIZE 0x00040000
#define IO_OPENPIC_OFFSET 0x00040000
static char *nvram;
+static int macio_nvram_shift(void)
+{
+ int nvram_flat;
+
+ if (is_oldworld())
+ return OW_IO_NVRAM_SHIFT;
+
+ nvram_flat = fw_cfg_read_i32(FW_CFG_PPC_NVRAM_FLAT);
+ return nvram_flat ? 0 : 1;
+}
+
int
macio_get_nvram_size(void)
{
+ int shift = macio_nvram_shift();
if (is_oldworld())
- return OW_IO_NVRAM_SIZE >> OW_IO_NVRAM_SHIFT;
+ return OW_IO_NVRAM_SIZE >> shift;
else
- return NW_IO_NVRAM_SIZE >> NW_IO_NVRAM_SHIFT;
+ return NW_IO_NVRAM_SIZE >> shift;
}
static unsigned long macio_nvram_offset(void)
@@ -123,14 +134,9 @@ void
macio_nvram_put(char *buf)
{
int i;
- unsigned int it_shift;
+ unsigned int it_shift = macio_nvram_shift();
- if (is_oldworld())
- it_shift = OW_IO_NVRAM_SHIFT;
- else
- it_shift = NW_IO_NVRAM_SHIFT;
-
- for (i=0; i< arch_nvram_size() ; i++)
+ for (i=0; i < arch_nvram_size(); i++)
nvram[i << it_shift] = buf[i];
#ifdef DUMP_NVRAM
printk("new nvram:\n");
@@ -142,12 +148,7 @@ void
macio_nvram_get(char *buf)
{
int i;
- unsigned int it_shift;
-
- if (is_oldworld())
- it_shift = OW_IO_NVRAM_SHIFT;
- else
- it_shift = NW_IO_NVRAM_SHIFT;
+ unsigned int it_shift = macio_nvram_shift();
for (i=0; i< arch_nvram_size(); i++)
buf[i] = nvram[i << it_shift];
@@ -161,7 +162,6 @@ macio_nvram_get(char *buf)
static void
openpic_init(const char *path, phys_addr_t addr)
{
- phandle_t target_node;
phandle_t dnode;
int props[2];
char buf[128];
@@ -186,42 +186,6 @@ openpic_init(const char *path, phys_addr_t addr)
set_int_property(dnode, "clock-frequency", 4166666);
fword("finish-device");
-
- u32 *interrupt_map;
- int len, i;
-
- /* patch in interrupt parent */
- dnode = find_dev(buf);
-
- target_node = find_dev("/pci/mac-io");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/escc/ch-a");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/escc/ch-b");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/ata-1");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/ata-2");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/ata-3");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci/mac-io/via-cuda");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- target_node = find_dev("/pci");
- set_int_property(target_node, "interrupt-parent", dnode);
-
- interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len);
- for (i = 0; i < 8; i++) {
- interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode;
- }
- set_property(target_node, "interrupt-map", (char *)interrupt_map, len);
}
DECLARE_NODE(ob_macio, INSTALL_OPEN, sizeof(int), "Tmac-io");
@@ -311,7 +275,7 @@ ob_macio_keylargo_init(const char *path, phys_addr_t addr)
/* The NewWorld NVRAM is not located in the MacIO device */
macio_nvram_init("", 0);
escc_init(path, addr);
- macio_ide_init(path, addr, 3);
+ macio_ide_init(path, addr, 2);
openpic_init(path, addr);
ob_unin_init();
}
diff --git a/roms/openbios/drivers/pc_serial.c b/roms/openbios/drivers/pc_serial.c
index f63fcaa47..a638e1f99 100644
--- a/roms/openbios/drivers/pc_serial.c
+++ b/roms/openbios/drivers/pc_serial.c
@@ -142,22 +142,19 @@ pc_serial_close(void)
static void
pc_serial_open(unsigned long *address)
{
- int len;
- phandle_t ph;
- unsigned long *prop;
-
- fword("my-self");
- fword("ihandle>phandle");
- ph = (phandle_t)POP();
- prop = (unsigned long *)get_property(ph, "address", &len);
- *address = *prop;
-
RET ( -1 );
}
+static void
+pc_serial_init(unsigned long *address)
+{
+ *address = POP();
+}
+
DECLARE_UNNAMED_NODE(pc_serial, INSTALL_OPEN, sizeof(unsigned long));
NODE_METHODS(pc_serial) = {
+ { "init", pc_serial_init },
{ "open", pc_serial_open },
{ "close", pc_serial_close },
{ "read", pc_serial_read },
@@ -177,6 +174,10 @@ ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base,
push_str(nodebuff);
fword("find-device");
+ PUSH(offset);
+ PUSH(find_package_method("init", get_cur_dev()));
+ fword("execute");
+
push_str("serial");
fword("device-type");
@@ -191,10 +192,12 @@ ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base,
push_str("reg");
fword("property");
+#if !defined(CONFIG_SPARC64)
PUSH(offset);
fword("encode-int");
push_str("address");
fword("property");
+#endif
#if defined(CONFIG_SPARC64)
set_int_property(get_cur_dev(), "interrupts", 1);
diff --git a/roms/openbios/drivers/pci.c b/roms/openbios/drivers/pci.c
index ca92d63bf..3260354be 100644
--- a/roms/openbios/drivers/pci.c
+++ b/roms/openbios/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__)
@@ -414,62 +417,6 @@ static void pci_set_bus_range(const pci_config_t *config)
set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0]));
}
-static void pci_host_set_interrupt_map(const pci_config_t *config)
-{
-/* XXX We currently have a hook in the MPIC init code to fill in its handle.
- * If you want to have interrupt maps for your PCI host bus, add your
- * architecture to the #if and make your bridge detect code fill in its
- * handle too.
- *
- * It would be great if someone clever could come up with a more universal
- * mechanism here.
- */
-#if defined(CONFIG_PPC)
- phandle_t dev = get_cur_dev();
- u32 props[7 * 8];
- int i;
-
-#if defined(CONFIG_PPC)
- /* Oldworld macs do interrupt maps differently */
- if(!is_newworld())
- return;
-#endif
-
- for (i = 0; i < (7*8); i+=7) {
- props[i+PCI_INT_MAP_PCI0] = 0;
- props[i+PCI_INT_MAP_PCI1] = 0;
- props[i+PCI_INT_MAP_PCI2] = 0;
- props[i+PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1
- props[i+PCI_INT_MAP_PIC_HANDLE] = 0; // gets patched in later
- props[i+PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7];
- props[i+PCI_INT_MAP_PIC_POL] = 3;
- }
- set_property(dev, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0]));
-
- props[PCI_INT_MAP_PCI0] = 0;
- props[PCI_INT_MAP_PCI1] = 0;
- props[PCI_INT_MAP_PCI2] = 0;
- props[PCI_INT_MAP_PCI_INT] = 0x7;
-
- set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0]));
-#elif defined(CONFIG_SPARC64)
- phandle_t dev = get_cur_dev();
- uint32_t props[5];
-
- props[0] = 0x000001fe;
- props[1] = 0x020003f8;
- props[2] = 1;
- props[3] = find_dev("/");
- props[4] = 0x2b;
- set_property(dev, "interrupt-map", (char *)props, 5 * sizeof(props[0]));
-
- props[0] = 0x000001ff;
- props[1] = 0xffffffff;
- props[2] = 3;
- set_property(dev, "interrupt-map-mask", (char *)props, 3 * sizeof(props[0]));
-#endif
-}
-
static void pci_host_set_reg(phandle_t phandle)
{
phandle_t dev = phandle;
@@ -536,7 +483,6 @@ int host_config_cb(const pci_config_t *config)
//XXX this overrides "reg" property
pci_host_set_reg(get_cur_dev());
pci_host_set_ranges(config);
- pci_host_set_interrupt_map(config);
return 0;
}
@@ -553,6 +499,9 @@ static int sabre_configure(phandle_t dev)
sizeof(props[0]));
set_property(dev, "#virtual-dma-addr-cells", (char *)props,
sizeof(props[0]));
+
+ set_property(dev, "no-streaming-cache", (char *)props, 0);
+
props[0] = 0x000007f0;
props[1] = 0x000007ee;
props[2] = 0x000007ef;
@@ -841,10 +790,14 @@ int ebus_config_cb(const pci_config_t *config)
{
#ifdef CONFIG_DRIVER_EBUS
phandle_t dev = get_cur_dev();
- uint32_t props[5];
+ uint32_t props[12];
+ int ncells;
+ int i;
+ uint32_t mask;
+ int flags, space_code;
- props[0] = 0x000001fe;
- props[1] = 0x020003f8;
+ props[0] = 0x14;
+ props[1] = 0x3f8;
props[2] = 1;
props[3] = find_dev("/");
props[4] = 0x2b;
@@ -855,14 +808,37 @@ int ebus_config_cb(const pci_config_t *config)
props[2] = 3;
set_property(dev, "interrupt-map-mask", (char *)props, 3 * sizeof(props[0]));
+ /* Build ranges property from the BARs */
+ ncells = 0;
+ for (i = 0; i < 6; i++) {
+ /* consider only bars with non-zero region size */
+ if (!config->sizes[i])
+ continue;
+
+ pci_decode_pci_addr(config->assigned[i],
+ &flags, &space_code, &mask);
+
+ props[ncells++] = PCI_BASE_ADDR_0 + (i * sizeof(uint32_t));
+ props[ncells++] = 0x0;
+
+ ncells += pci_encode_phys_addr(props + ncells,
+ flags, space_code, config->dev,
+ PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)),
+ 0);
+
+ props[ncells++] = config->sizes[i];
+ }
+
+ set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0]));
+
#ifdef CONFIG_DRIVER_FLOPPY
ob_floppy_init(config->path, "fdthree", 0x3f0ULL, 0);
#endif
#ifdef CONFIG_DRIVER_PC_SERIAL
- ob_pc_serial_init(config->path, "su", arch->io_base, 0x3f8ULL, 0);
+ ob_pc_serial_init(config->path, "su", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x3f8ULL, 0);
#endif
#ifdef CONFIG_DRIVER_PC_KBD
- ob_pc_kbd_init(config->path, "kb_ps2", arch->io_base, 0x60ULL, 0);
+ ob_pc_kbd_init(config->path, "kb_ps2", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x60ULL, 0);
#endif
#endif
return 0;
@@ -883,6 +859,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)
@@ -933,10 +917,7 @@ static void ob_pci_add_properties(phandle_t phandle,
OLDWORLD(set_int_property(dev, "AAPL,interrupts",
config->irq_line));
#if defined(CONFIG_SPARC64)
- /* direct mapping bssnn (Bus, Slot, interrupt Number */
- set_int_property(get_cur_dev(), "interrupts",
- ((((config->dev >> 11) << 2)
- + config->irq_pin - 1) & 0x1f));
+ set_int_property(dev, "interrupts", config->irq_pin);
#else
NEWWORLD(set_int_property(dev, "interrupts", config->irq_pin));
#endif
@@ -1375,6 +1356,132 @@ static void ob_configure_pci_device(const char* parent_path,
}
}
+static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigned long io_base)
+{
+ /* Create an available property for both memory and IO space */
+ uint32_t props[10];
+ int ncells;
+
+ ncells = 0;
+ ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32, 0, 0, mem_base);
+ ncells += pci_encode_size(props + ncells, arch->mem_len - mem_base);
+ ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE, 0, 0, io_base);
+ ncells += pci_encode_size(props + ncells, arch->io_len - io_base);
+
+ set_property(host, "available", (char *)props, ncells * sizeof(props[0]));
+}
+
+/* Convert device/irq pin to interrupt property */
+#define SUN4U_INTERRUPT(dev, irq_pin) \
+ ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f)
+
+static void ob_pci_host_set_interrupt_map(phandle_t host)
+{
+ phandle_t dnode = 0;
+ u32 props[128];
+ int i;
+
+#if defined(CONFIG_PPC)
+ phandle_t target_node;
+
+ /* Oldworld macs do interrupt maps differently */
+ if (!is_newworld())
+ return;
+
+ dnode = dt_iterate_type(0, "open-pic");
+ if (dnode) {
+ /* patch in openpic interrupt-parent properties */
+ target_node = find_dev("/pci/mac-io");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ target_node = find_dev("/pci/mac-io/escc/ch-a");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ target_node = find_dev("/pci/mac-io/escc/ch-b");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ /* QEMU only emulates 2 of the 3 ata buses currently */
+ /* On a new world Mac these are not numbered but named by the
+ * ATA version they support. Thus we have: ata-3, ata-3, ata-4
+ * On g3beige they all called just ide.
+ * We take ata-3 and ata-4 which seems to work for both
+ * at least for clients we care about */
+ target_node = find_dev("/pci/mac-io/ata-3");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ target_node = find_dev("/pci/mac-io/ata-4");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ target_node = find_dev("/pci/mac-io/via-cuda");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ target_node = find_dev("/pci");
+ set_int_property(target_node, "interrupt-parent", dnode);
+
+ /* openpic interrupt mapping */
+ for (i = 0; i < (7*8); i += 7) {
+ props[i + PCI_INT_MAP_PCI0] = 0;
+ props[i + PCI_INT_MAP_PCI1] = 0;
+ props[i + PCI_INT_MAP_PCI2] = 0;
+ props[i + PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1
+ props[i + PCI_INT_MAP_PIC_HANDLE] = dnode;
+ props[i + PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7];
+ props[i + PCI_INT_MAP_PIC_POL] = 3;
+ }
+ set_property(host, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0]));
+
+ props[PCI_INT_MAP_PCI0] = 0;
+ props[PCI_INT_MAP_PCI1] = 0;
+ props[PCI_INT_MAP_PCI2] = 0;
+ props[PCI_INT_MAP_PCI_INT] = 0x7;
+
+ set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0]));
+ }
+#elif defined(CONFIG_SPARC64)
+ int ncells, len;
+ u32 *val, addr;
+ char *reg;
+
+ /* Set interrupt-map for PCI devices with an interrupt pin present */
+ ncells = 0;
+
+ PUSH(host);
+ fword("child");
+ dnode = POP();
+ while (dnode) {
+ if (get_int_property(dnode, "interrupts", &len)) {
+ reg = get_property(dnode, "reg", &len);
+ if (reg) {
+ val = (u32 *)reg;
+
+ for (i = 0; i < (len / sizeof(u32)); i += 5) {
+ addr = val[i];
+
+ /* Device address is in 1st 32-bit word of encoded PCI address for config space */
+ if (!(addr & 0x03000000)) {
+ ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0);
+ props[ncells++] = 1; /* always interrupt pin 1 for QEMU */
+ props[ncells++] = host;
+ props[ncells++] = SUN4U_INTERRUPT(addr, 1);
+ }
+ }
+ }
+ }
+
+ PUSH(dnode);
+ fword("peer");
+ dnode = POP();
+ }
+ set_property(host, "interrupt-map", (char *)props, ncells * sizeof(props[0]));
+
+ props[0] = 0x0000f800;
+ props[1] = 0x0;
+ props[2] = 0x0;
+ props[3] = 7;
+ set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0]));
+#endif
+}
+
int ob_pci_init(void)
{
int bus, devnum, fn;
@@ -1382,7 +1489,7 @@ int ob_pci_init(void)
unsigned long mem_base, io_base;
pci_config_t config = {}; /* host bridge */
- phandle_t phandle_host;
+ phandle_t phandle_host = 0;
PCI_DPRINTF("Initializing PCI host bridge...\n");
@@ -1430,6 +1537,11 @@ int ob_pci_init(void)
break;
}
+ /* create available attributes for the PCI bridge */
+ ob_pci_set_available(phandle_host, mem_base, io_base);
+
+ /* configure the host bridge interrupt map */
+ ob_pci_host_set_interrupt_map(phandle_host);
device_end();
diff --git a/roms/openbios/drivers/pci.h b/roms/openbios/drivers/pci.h
index ab8f18437..84a2b2cf6 100644
--- a/roms/openbios/drivers/pci.h
+++ b/roms/openbios/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/roms/openbios/drivers/pci_database.c b/roms/openbios/drivers/pci_database.c
index 0bd854a51..0070a78ba 100644
--- a/roms/openbios/drivers/pci_database.c
+++ b/roms/openbios/drivers/pci_database.c
@@ -845,6 +845,24 @@ static const pci_subclass_t cpu_subclass[] = {
},
};
+static const pci_dev_t usb_devices[] = {
+#if defined(CONFIG_QEMU)
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KEYL_USB,
+ "usb", "usb", NULL,
+ "pci106b,3f\0pciclass,0c0310\0",
+ 1, 0, 0,
+ NULL, NULL,
+ },
+#endif
+ {
+ 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 +870,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/roms/openbios/drivers/pci_database.h b/roms/openbios/drivers/pci_database.h
index 16b385bf1..7f053d808 100644
--- a/roms/openbios/drivers/pci_database.h
+++ b/roms/openbios/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/roms/openbios/drivers/tcx.fs b/roms/openbios/drivers/tcx.fs
index e1ec5ad70..af8991fd0 100644
--- a/roms/openbios/drivers/tcx.fs
+++ b/roms/openbios/drivers/tcx.fs
@@ -72,54 +72,53 @@ fcode-version3
\ Registers
\
-h# 0 constant tcx-off1
-h# 10000 constant /tcx-off1
+h# 0 constant tcx-off-rom
+h# 10000 constant /tcx-off-rom
-h# 200000 constant tcx-off-dac
-h# 4000 constant /tcx-off-dac-24
-h# 4 constant /tcx-off-dac-8
+h# 200000 constant tcx-off-cmap
+h# 4000 constant /tcx-off-cmap-24
+h# 4 constant /tcx-off-cmap-8
-h# 240000 constant tcx-off3
-h# 4000 constant /tcx-off3-24
-h# 4 constant /tcx-off3-8
+h# 240000 constant tcx-off-dhc
+h# 4000 constant /tcx-off-dhc-24
+h# 4 constant /tcx-off-dhc-8
-h# 280000 constant tcx-off4
-h# 8000 constant /tcx-off4-24
-h# 1 constant /tcx-off4-8
+h# 280000 constant tcx-off-alt
+h# 8000 constant /tcx-off-alt-24
+h# 1 constant /tcx-off-alt-8
-h# 301000 constant tcx-off5-24
-h# 300000 constant tcx-off5-8
-h# 1000 constant /tcx-off5-24
-h# 81c constant /tcx-off5-8
+h# 301000 constant tcx-off-thc-24
+h# 300000 constant tcx-off-thc-8
+h# 1000 constant /tcx-off-thc-24
+h# 81c constant /tcx-off-thc-8
-h# 700000 constant tcx-off6
-h# 1000 constant /tcx-off6
+h# 701000 constant tcx-off-tec
+h# 1000 constant /tcx-off-tec
-h# 800000 constant tcx-off-fb
-h# 100000 constant /tcx-off-fb
+h# 800000 constant tcx-off-dfb8
+h# 100000 constant /tcx-off-dfb8
-h# 2000000 constant tcx-off8
-h# 400000 constant /tcx-off8-24
-h# 1 constant /tcx-off8-8
+h# 2000000 constant tcx-off-dfb24
+h# 400000 constant /tcx-off-dfb24-24
+h# 1 constant /tcx-off-dfb24-8
-h# 4000000 constant tcx-off9
-h# 400000 constant /tcx-off9-24
-h# 1 constant /tcx-off9-8
+h# 4000000 constant tcx-off-stip
+h# 800000 constant /tcx-off-stip
-h# 6000000 constant tcx-off10
-h# 800000 constant /tcx-off10
+h# 6000000 constant tcx-off-blit
+h# 800000 constant /tcx-off-blit
-h# a000000 constant tcx-off11
-h# 400000 constant /tcx-off11-24
-h# 1 constant /tcx-off11-8
+h# a000000 constant tcx-off-rdfb32
+h# 400000 constant /tcx-off-rdfb32-24
+h# 1 constant /tcx-off-rdfb32-8
-h# c000000 constant tcx-off12
-h# 800000 constant /tcx-off12-24
-h# 1 constant /tcx-off12-8
+h# c000000 constant tcx-off-rstip
+h# 800000 constant /tcx-off-rstip-24
+h# 1 constant /tcx-off-rstip-8
-h# e000000 constant tcx-off13
-h# 800000 constant /tcx-off13-24
-h# 1 constant /tcx-off13-8
+h# e000000 constant tcx-off-rblit
+h# 800000 constant /tcx-off-rblit-24
+h# 1 constant /tcx-off-rblit-8
: >tcx-reg-spec ( offset size -- encoded-reg )
>r 0 my-address d+ my-space encode-phys r> encode-int encode+
@@ -127,37 +126,37 @@ h# 1 constant /tcx-off13-8
: tcx-8bit-reg
\ WARNING: order is important (at least to Solaris)
- tcx-off-fb /tcx-off-fb >tcx-reg-spec
- tcx-off8 /tcx-off8-8 >tcx-reg-spec encode+
- tcx-off9 /tcx-off9-8 >tcx-reg-spec encode+
- tcx-off10 /tcx-off10 >tcx-reg-spec encode+
- tcx-off11 /tcx-off11-8 >tcx-reg-spec encode+
- tcx-off12 /tcx-off12-8 >tcx-reg-spec encode+
- tcx-off13 /tcx-off13-8 >tcx-reg-spec encode+
- tcx-off6 /tcx-off6 >tcx-reg-spec encode+
- tcx-off-dac /tcx-off-dac-8 >tcx-reg-spec encode+
- tcx-off5-8 /tcx-off5-8 >tcx-reg-spec encode+
- tcx-off1 /tcx-off1 >tcx-reg-spec encode+
- tcx-off3 /tcx-off3-8 >tcx-reg-spec encode+
- tcx-off4 /tcx-off4-8 >tcx-reg-spec encode+
+ tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec
+ tcx-off-dfb24 /tcx-off-dfb24-8 >tcx-reg-spec encode+
+ tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+
+ tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+
+ tcx-off-rdfb32 /tcx-off-rdfb32-8 >tcx-reg-spec encode+
+ tcx-off-rstip /tcx-off-rstip-8 >tcx-reg-spec encode+
+ tcx-off-rblit /tcx-off-rblit-8 >tcx-reg-spec encode+
+ tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+
+ tcx-off-cmap /tcx-off-cmap-8 >tcx-reg-spec encode+
+ tcx-off-thc-8 /tcx-off-thc-8 >tcx-reg-spec encode+
+ tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+
+ tcx-off-dhc /tcx-off-dhc-8 >tcx-reg-spec encode+
+ tcx-off-alt /tcx-off-alt-8 >tcx-reg-spec encode+
" reg" property
;
: tcx-24bit-reg
\ WARNING: order is important (at least to Solaris)
- tcx-off-fb /tcx-off-fb >tcx-reg-spec
- tcx-off8 /tcx-off8-24 >tcx-reg-spec encode+
- tcx-off9 /tcx-off9-24 >tcx-reg-spec encode+
- tcx-off10 /tcx-off10 >tcx-reg-spec encode+
- tcx-off11 /tcx-off11-24 >tcx-reg-spec encode+
- tcx-off12 /tcx-off12-24 >tcx-reg-spec encode+
- tcx-off13 /tcx-off13-24 >tcx-reg-spec encode+
- tcx-off6 /tcx-off6 >tcx-reg-spec encode+
- tcx-off-dac /tcx-off-dac-24 >tcx-reg-spec encode+
- tcx-off5-24 /tcx-off5-24 >tcx-reg-spec encode+
- tcx-off1 /tcx-off1 >tcx-reg-spec encode+
- tcx-off3 /tcx-off3-24 >tcx-reg-spec encode+
- tcx-off4 /tcx-off4-24 >tcx-reg-spec encode+
+ tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec
+ tcx-off-dfb24 /tcx-off-dfb24-24 >tcx-reg-spec encode+
+ tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+
+ tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+
+ tcx-off-rdfb32 /tcx-off-rdfb32-24 >tcx-reg-spec encode+
+ tcx-off-rstip /tcx-off-rstip-24 >tcx-reg-spec encode+
+ tcx-off-rblit /tcx-off-rblit-24 >tcx-reg-spec encode+
+ tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+
+ tcx-off-cmap /tcx-off-cmap-24 >tcx-reg-spec encode+
+ tcx-off-thc-24 /tcx-off-thc-24 >tcx-reg-spec encode+
+ tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+
+ tcx-off-dhc /tcx-off-dhc-24 >tcx-reg-spec encode+
+ tcx-off-alt /tcx-off-alt-24 >tcx-reg-spec encode+
" reg" property
;
@@ -198,11 +197,11 @@ headerless
\
: dac-map
- tcx-off-dac /tcx-dac do-map-in to tcx-dac
+ tcx-off-cmap /tcx-dac do-map-in to tcx-dac
;
: fb-map
- tcx-off-fb h# c0000 do-map-in to fb-addr
+ tcx-off-dfb8 h# c0000 do-map-in to fb-addr
;
: map-regs
@@ -228,7 +227,11 @@ headerless
fb-addr to frame-buffer-adr
default-font set-font
- frame-buffer-adr encode-int " address" property
+ \ Sun TCX adapters don't have an address property, but it is useful for
+ \ OpenBIOS developers. Unfortunately NetBSD SPARC32 has a bug that causes
+ \ it to fail initialising TCX if the address property is present; so work
+ \ around this by adding an underscore prefix
+ frame-buffer-adr encode-int " _address" property
openbios-video-width openbios-video-height over char-width / over char-height /
fb8-install
@@ -240,11 +243,11 @@ headerless
\ Handle differences between 8-bit/24-bit mode
depth-bits 8 = if
tcx-8bit-reg
- /tcx-off-dac-8 to /tcx-dac
+ /tcx-off-cmap-8 to /tcx-dac
" true" encode-string " tcx-8-bit" property
else
tcx-24bit-reg
- /tcx-off-dac-24 to /tcx-dac
+ /tcx-off-cmap-24 to /tcx-dac
\ Even with a 24-bit enabled TCX card, the control plane is
\ used in 8-bit mode. So force the video subsystem into 8-bit
@@ -266,7 +269,7 @@ headerless
openbios-video-width encode-int " width" property
line-bytes encode-int " linebytes" property
- 5 encode-int 0 encode-int encode+ " intr" property
+ h# 39 encode-int 0 encode-int encode+ " intr" property
5 encode-int " interrupts" property
['] qemu-tcx-driver-install is-install
diff --git a/roms/openbios/drivers/usb.c b/roms/openbios/drivers/usb.c
new file mode 100644
index 000000000..c6e37174e
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/drivers/usb.h b/roms/openbios/drivers/usb.h
new file mode 100644
index 000000000..2e23a1370
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/drivers/usbhid.c b/roms/openbios/drivers/usbhid.c
new file mode 100644
index 000000000..a423278a8
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/drivers/usbohci.c b/roms/openbios/drivers/usbohci.c
new file mode 100644
index 000000000..774164b0b
--- /dev/null
+++ b/roms/openbios/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)
+{
+ u32 reg_base;
+ uint16_t cmd;
+
+ 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/roms/openbios/drivers/usbohci.h b/roms/openbios/drivers/usbohci.h
new file mode 100644
index 000000000..690332871
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/drivers/usbohci_private.h b/roms/openbios/drivers/usbohci_private.h
new file mode 100644
index 000000000..b3a723e21
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/drivers/usbohci_rh.c b/roms/openbios/drivers/usbohci_rh.c
new file mode 100644
index 000000000..55503be61
--- /dev/null
+++ b/roms/openbios/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/roms/openbios/include/arch/common/fw_cfg.h b/roms/openbios/include/arch/common/fw_cfg.h
index bd4808bbd..df44c2e89 100644
--- a/roms/openbios/include/arch/common/fw_cfg.h
+++ b/roms/openbios/include/arch/common/fw_cfg.h
@@ -46,6 +46,7 @@
#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
#define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08)
#define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09)
+#define FW_CFG_PPC_NVRAM_FLAT (FW_CFG_ARCH_LOCAL + 0x0a)
#define FW_CFG_INVALID 0xffff
diff --git a/roms/openbios/include/drivers/pci.h b/roms/openbios/include/drivers/pci.h
index 1f0af6c9f..2eb5685d3 100644
--- a/roms/openbios/include/drivers/pci.h
+++ b/roms/openbios/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/roms/openbios/include/drivers/usb.h b/roms/openbios/include/drivers/usb.h
new file mode 100644
index 000000000..143ed27bc
--- /dev/null
+++ b/roms/openbios/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 */
diff --git a/roms/openbios/libopenbios/ofmem_common.c b/roms/openbios/libopenbios/ofmem_common.c
index 157ca8741..3b8ca152f 100644
--- a/roms/openbios/libopenbios/ofmem_common.c
+++ b/roms/openbios/libopenbios/ofmem_common.c
@@ -634,8 +634,8 @@ ucell ofmem_claim( ucell addr, ucell size, ucell align )
} else {
if( align < PAGE_SIZE )
align = PAGE_SIZE;
- phys = ofmem_claim_phys_( addr, size, align, 0, ofmem_arch_get_phys_top(), 1 /* reverse */ );
- virt = ofmem_claim_virt_( addr, size, align, 0, get_ram_size(), 1 /* reverse */ );
+ phys = ofmem_claim_phys_( -1, size, align, 0, ofmem_arch_get_phys_top(), 1 /* reverse */ );
+ virt = ofmem_claim_virt_( phys, size, 0, 0, 0, 0 );
if( phys == -1 || virt == -1 ) {
OFMEM_TRACE("ofmem_claim failed\n");
return -1;
diff --git a/roms/seabios/.version b/roms/seabios/.version
index 88fc6a6d5..0bd308b59 100644
--- a/roms/seabios/.version
+++ b/roms/seabios/.version
@@ -1 +1 @@
-rel-1.7.5-0-ge51488c
+rel-1.7.5.1-0-g8936dbb
diff --git a/roms/seabios/Makefile b/roms/seabios/Makefile
index 78b598eee..7c2b33c3a 100644
--- a/roms/seabios/Makefile
+++ b/roms/seabios/Makefile
@@ -112,8 +112,8 @@ endif
# Do a whole file compile by textually including all C code.
define whole-compile
@echo " Compiling whole program $3"
-$(Q)printf '$(foreach i,$2,#include "$(CURDIR)/$i"\n)' > $3.tmp.c
-$(Q)$(CC) $1 $(CFLAGSWHOLE) -c $3.tmp.c -o $3
+$(Q)printf '$(foreach i,$2,#include "$i"\n)' > $3.tmp.c
+$(Q)$(CC) -I. $1 $(CFLAGSWHOLE) -c $3.tmp.c -o $3
endef
%.strip.o: %.o
diff --git a/roms/seabios/src/boot.c b/roms/seabios/src/boot.c
index 133e2063f..97de89cfa 100644
--- a/roms/seabios/src/boot.c
+++ b/roms/seabios/src/boot.c
@@ -145,7 +145,7 @@ int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun)
// Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0
char desc[256], *p;
p = build_pci_path(desc, sizeof(desc), "*", pci);
- snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%d,%d", target, lun);
+ snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%x,%x", target, lun);
return find_prio(desc);
}
@@ -189,7 +189,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance)
char desc[256], *p;
p = build_pci_path(desc, sizeof(desc), "*", pci);
if (instance)
- snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
+ snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance);
return find_prio(desc);
}
@@ -201,7 +201,7 @@ int bootprio_find_named_rom(const char *name, int instance)
char desc[256], *p;
p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name);
if (instance)
- snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
+ snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance);
return find_prio(desc);
}
@@ -224,7 +224,7 @@ int bootprio_find_usb(struct usbdevice_s *usbdev, int lun)
char desc[256], *p;
p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci);
p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub);
- snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%d"
+ snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x"
, usbdev->port+1, lun);
int ret = find_prio(desc);
if (ret >= 0)
diff --git a/roms/seabios/src/fw/pciinit.c b/roms/seabios/src/fw/pciinit.c
index 2e6382f98..0e5d51b91 100644
--- a/roms/seabios/src/fw/pciinit.c
+++ b/roms/seabios/src/fw/pciinit.c
@@ -635,6 +635,36 @@ pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev,
return entry;
}
+static int pci_bus_hotplug_support(struct pci_bus *bus)
+{
+ u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP);
+ u8 shpc_cap;
+
+ if (pcie_cap) {
+ u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf,
+ pcie_cap + PCI_EXP_FLAGS);
+ u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >>
+ (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1));
+ u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) ||
+ (port_type == PCI_EXP_TYPE_ROOT_PORT);
+ /*
+ * PCI Express SPEC, 7.8.2:
+ * Slot Implemented – When Set, this bit indicates that the Link
+ * HwInit associated with this Port is connected to a slot (as
+ * compared to being connected to a system-integrated device or
+ * being disabled).
+ * This bit is valid for Downstream Ports. This bit is undefined
+ * for Upstream Ports.
+ */
+ u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT;
+
+ return downstream_port && slot_implemented;
+ }
+
+ shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC);
+ return !!shpc_cap;
+}
+
static int pci_bios_check_devices(struct pci_bus *busses)
{
dprintf(1, "PCI: check devices\n");
@@ -677,7 +707,7 @@ static int pci_bios_check_devices(struct pci_bus *busses)
continue;
struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)];
int type;
- u8 shpc_cap = pci_find_capability(s->bus_dev, PCI_CAP_ID_SHPC);
+ int hotplug_support = pci_bus_hotplug_support(s);
for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
u64 align = (type == PCI_REGION_TYPE_IO) ?
PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN;
@@ -686,7 +716,7 @@ static int pci_bios_check_devices(struct pci_bus *busses)
if (pci_region_align(&s->r[type]) > align)
align = pci_region_align(&s->r[type]);
u64 sum = pci_region_sum(&s->r[type]);
- if (!sum && shpc_cap)
+ if (!sum && hotplug_support)
sum = align; /* reserve min size for hot-plug */
u64 size = ALIGN(sum, align);
int is64 = pci_bios_bridge_region_is64(&s->r[type],
diff --git a/roms/seabios/src/hw/megasas.c b/roms/seabios/src/hw/megasas.c
index a5dc14fcf..b2a65e48b 100644
--- a/roms/seabios/src/hw/megasas.c
+++ b/roms/seabios/src/hw/megasas.c
@@ -357,6 +357,10 @@ init_megasas(struct pci_device *pci)
u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2)
& PCI_BASE_ADDRESS_IO_MASK;
+ if (!iobase)
+ iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
+ & PCI_BASE_ADDRESS_IO_MASK;
+
dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n",
pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf),
pci_bdf_to_fn(bdf), iobase);
diff --git a/roms/seabios/src/hw/usb-ehci.c b/roms/seabios/src/hw/usb-ehci.c
index 9d9427b6c..17a7e8b28 100644
--- a/roms/seabios/src/hw/usb-ehci.c
+++ b/roms/seabios/src/hw/usb-ehci.c
@@ -372,7 +372,7 @@ ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev
struct ehci_pipe *hpipe = container_of(
hubdev->defpipe, struct ehci_pipe, pipe);
if (hpipe->pipe.speed == USB_HIGHSPEED)
- pipe->qh.info2 |= ((usbdev->port << QH_HUBPORT_SHIFT)
+ pipe->qh.info2 |= (((usbdev->port+1) << QH_HUBPORT_SHIFT)
| (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT));
else
pipe->qh.info2 = hpipe->qh.info2;
diff --git a/roms/seabios/src/stacks.c b/roms/seabios/src/stacks.c
index 6bcb31940..beccc0fa6 100644
--- a/roms/seabios/src/stacks.c
+++ b/roms/seabios/src/stacks.c
@@ -287,7 +287,7 @@ thread_init(void)
int
threads_during_optionroms(void)
{
- return CONFIG_THREADS && ThreadControl == 2;
+ return CONFIG_THREADS && ThreadControl == 2 && in_post();
}
// Switch to next thread stack.
diff --git a/roms/seabios/vgasrc/vgabios.c b/roms/seabios/vgasrc/vgabios.c
index 400e29675..e87b7ebff 100644
--- a/roms/seabios/vgasrc/vgabios.c
+++ b/roms/seabios/vgasrc/vgabios.c
@@ -891,15 +891,15 @@ handle_1011(struct bregs *regs)
{
if (CONFIG_VGA_STDVGA_PORTS) {
switch (regs->al) {
- case 0x00: handle_101100(regs); break;
- case 0x01: handle_101101(regs); break;
- case 0x02: handle_101102(regs); break;
- case 0x03: handle_101103(regs); break;
- case 0x04: handle_101104(regs); break;
- case 0x10: handle_101110(regs); break;
- case 0x11: handle_101111(regs); break;
- case 0x12: handle_101112(regs); break;
- case 0x14: handle_101114(regs); break;
+ case 0x00: handle_101100(regs); return;
+ case 0x01: handle_101101(regs); return;
+ case 0x02: handle_101102(regs); return;
+ case 0x03: handle_101103(regs); return;
+ case 0x04: handle_101104(regs); return;
+ case 0x10: handle_101110(regs); return;
+ case 0x11: handle_101111(regs); return;
+ case 0x12: handle_101112(regs); return;
+ case 0x14: handle_101114(regs); return;
}
}
switch (regs->al) {