summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/drivers')
-rw-r--r--qemu/roms/openbios/drivers/Kconfig59
-rw-r--r--qemu/roms/openbios/drivers/adb_bus.c257
-rw-r--r--qemu/roms/openbios/drivers/adb_bus.h104
-rw-r--r--qemu/roms/openbios/drivers/adb_kbd.c595
-rw-r--r--qemu/roms/openbios/drivers/adb_kbd.h22
-rw-r--r--qemu/roms/openbios/drivers/adb_mouse.c67
-rw-r--r--qemu/roms/openbios/drivers/adb_mouse.h22
-rw-r--r--qemu/roms/openbios/drivers/build.xml41
-rw-r--r--qemu/roms/openbios/drivers/cgthree.fs197
-rw-r--r--qemu/roms/openbios/drivers/cuda.c424
-rw-r--r--qemu/roms/openbios/drivers/cuda.h17
-rw-r--r--qemu/roms/openbios/drivers/escc.c476
-rw-r--r--qemu/roms/openbios/drivers/escc.h9
-rw-r--r--qemu/roms/openbios/drivers/esp.c598
-rw-r--r--qemu/roms/openbios/drivers/esp.fs18
-rw-r--r--qemu/roms/openbios/drivers/esp.h269
-rw-r--r--qemu/roms/openbios/drivers/floppy.c1179
-rw-r--r--qemu/roms/openbios/drivers/floppy.h9
-rw-r--r--qemu/roms/openbios/drivers/fw_cfg.c76
-rw-r--r--qemu/roms/openbios/drivers/hdreg.h289
-rw-r--r--qemu/roms/openbios/drivers/ide.c1685
-rw-r--r--qemu/roms/openbios/drivers/ide.h213
-rw-r--r--qemu/roms/openbios/drivers/iommu.c207
-rw-r--r--qemu/roms/openbios/drivers/iommu.h102
-rw-r--r--qemu/roms/openbios/drivers/kbd.c116
-rw-r--r--qemu/roms/openbios/drivers/kbd.h108
-rw-r--r--qemu/roms/openbios/drivers/macio.c281
-rw-r--r--qemu/roms/openbios/drivers/macio.h5
-rw-r--r--qemu/roms/openbios/drivers/obio.c525
-rw-r--r--qemu/roms/openbios/drivers/obio.h165
-rw-r--r--qemu/roms/openbios/drivers/pc_kbd.c236
-rw-r--r--qemu/roms/openbios/drivers/pc_serial.c208
-rw-r--r--qemu/roms/openbios/drivers/pci.c1569
-rw-r--r--qemu/roms/openbios/drivers/pci.fs92
-rw-r--r--qemu/roms/openbios/drivers/pci.h87
-rw-r--r--qemu/roms/openbios/drivers/pci_database.c1275
-rw-r--r--qemu/roms/openbios/drivers/pci_database.h57
-rw-r--r--qemu/roms/openbios/drivers/sbus.c523
-rw-r--r--qemu/roms/openbios/drivers/sbus.fs94
-rw-r--r--qemu/roms/openbios/drivers/scsi.h262
-rw-r--r--qemu/roms/openbios/drivers/tcx.fs280
-rw-r--r--qemu/roms/openbios/drivers/timer.c100
-rw-r--r--qemu/roms/openbios/drivers/timer.h62
-rw-r--r--qemu/roms/openbios/drivers/usb.c587
-rw-r--r--qemu/roms/openbios/drivers/usb.h357
-rw-r--r--qemu/roms/openbios/drivers/usbhid.c579
-rw-r--r--qemu/roms/openbios/drivers/usbohci.c926
-rw-r--r--qemu/roms/openbios/drivers/usbohci.h45
-rw-r--r--qemu/roms/openbios/drivers/usbohci_private.h270
-rw-r--r--qemu/roms/openbios/drivers/usbohci_rh.c212
-rw-r--r--qemu/roms/openbios/drivers/vga.fs204
-rw-r--r--qemu/roms/openbios/drivers/vga.h231
-rw-r--r--qemu/roms/openbios/drivers/vga_load_regs.c496
-rw-r--r--qemu/roms/openbios/drivers/vga_set_mode.c148
54 files changed, 17035 insertions, 0 deletions
diff --git a/qemu/roms/openbios/drivers/Kconfig b/qemu/roms/openbios/drivers/Kconfig
new file mode 100644
index 000000000..3bebc0293
--- /dev/null
+++ b/qemu/roms/openbios/drivers/Kconfig
@@ -0,0 +1,59 @@
+
+
+menu "Drivers"
+
+config DRIVER_PCI
+ bool "PCI driver"
+ default y
+ help
+ Builtin PCI driver
+
+config DEBUG_PCI
+ bool "Debug PCI driver"
+ default n
+ help
+ Debug PCI driver
+
+config DRIVER_IDE
+ depends X86 || AMD64 || PPC
+ bool "Legacy IDE"
+ default y
+ help
+ If you want to be able to boot from IDE, enable this option.
+
+config IDE_NUM_CHANNELS
+ depends DRIVER_IDE
+ int "Number of IDE channels to be probed"
+ default 4
+ help
+ Number of IDE channels to be probed. This should be set to
+ one or two if you build OpenBIOS for the Total Impact BRIQ.
+
+config DEBUG_IDE
+ depends DRIVER_IDE
+ bool "Debug IDE driver"
+ default n
+ 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/qemu/roms/openbios/drivers/adb_bus.c b/qemu/roms/openbios/drivers/adb_bus.c
new file mode 100644
index 000000000..d67d1f1a4
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_bus.c
@@ -0,0 +1,257 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB bus support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/vsprintf.h"
+
+#include "adb_bus.h"
+#include "adb_kbd.h"
+#include "adb_mouse.h"
+
+DECLARE_UNNAMED_NODE( adb, INSTALL_OPEN, sizeof(int));
+
+static void
+adb_initialize (int *idx)
+{
+ phandle_t ph=get_cur_dev();
+
+ push_str("adb");
+ fword("device-type");
+
+ set_property(ph, "compatible", "adb", 4);
+ set_int_property(ph, "#address-cells", 1);
+ set_int_property(ph, "#size-cells", 0);
+}
+
+static void
+adb_open(int *idx)
+{
+ RET(-1);
+}
+
+static void
+adb_close(int *idx)
+{
+}
+
+NODE_METHODS( adb ) = {
+ { NULL, adb_initialize },
+ { "open", adb_open },
+ { "close", adb_close },
+};
+
+adb_bus_t *adb_bus_new (void *host,
+ int (*req)(void *host, const uint8_t *snd_buf,
+ int len, uint8_t *rcv_buf))
+{
+ adb_bus_t *new;
+
+ new = malloc(sizeof(adb_bus_t));
+ if (new == NULL)
+ return NULL;
+ new->host = host;
+ new->req = req;
+
+ return new;
+}
+
+/* Check and relocate all ADB devices as suggested in
+ * ADB_manager Apple documentation
+ */
+
+int adb_bus_init (char *path, adb_bus_t *bus)
+{
+ char buf[64];
+ uint8_t buffer[ADB_BUF_SIZE];
+ uint8_t adb_addresses[16] =
+ { 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, 0, };
+ adb_dev_t tmp_device, **cur;
+ int address;
+ int reloc = 0, next_free = 7;
+ int keep;
+
+ snprintf(buf, sizeof(buf), "%s/adb", path);
+ REGISTER_NAMED_NODE( adb, buf);
+ /* Reset the bus */
+ // ADB_DPRINTF("\n");
+ adb_reset(bus);
+ cur = &bus->devices;
+ memset(&tmp_device, 0, sizeof(adb_dev_t));
+ tmp_device.bus = bus;
+ for (address = 1; address < 8 && adb_addresses[reloc] > 0;) {
+ if (address == ADB_RES) {
+ /* Reserved */
+ address++;
+ continue;
+ }
+ //ADB_DPRINTF("Check device on ADB address %d\n", address);
+ tmp_device.addr = address;
+ switch (adb_reg_get(&tmp_device, 3, buffer)) {
+ case 0:
+ //ADB_DPRINTF("No device on ADB address %d\n", address);
+ /* Register this address as free */
+ if (adb_addresses[next_free] != 0)
+ adb_addresses[next_free++] = address;
+ /* Check next ADB address */
+ address++;
+ break;
+ case 2:
+ /* One device answered :
+ * make it available and relocate it to a free address
+ */
+ if (buffer[0] == ADB_CHADDR) {
+ /* device self test failed */
+ ADB_DPRINTF("device on ADB address %d self-test failed "
+ "%02x %02x %02x\n", address,
+ buffer[0], buffer[1], buffer[2]);
+ keep = 0;
+ } else {
+ //ADB_DPRINTF("device on ADB address %d self-test OK\n",
+ // address);
+ keep = 1;
+ }
+ ADB_DPRINTF("Relocate device on ADB address %d to %d (%d)\n",
+ address, adb_addresses[reloc], reloc);
+ buffer[0] = ((buffer[0] & 0x40) & ~0x90) | adb_addresses[reloc];
+ if (keep == 1)
+ buffer[0] |= 0x20;
+ buffer[1] = ADB_CHADDR_NOCOLL;
+ if (adb_reg_set(&tmp_device, 3, buffer, 2) < 0) {
+ ADB_DPRINTF("ADB device relocation failed\n");
+ return -1;
+ }
+ if (keep == 1) {
+ *cur = malloc(sizeof(adb_dev_t));
+ if (*cur == NULL) {
+ return -1;
+ }
+ (*cur)->type = address;
+ (*cur)->bus = bus;
+ (*cur)->addr = adb_addresses[reloc++];
+ /* Flush buffers */
+ adb_flush(*cur);
+ switch ((*cur)->type) {
+ case ADB_PROTECT:
+ ADB_DPRINTF("Found one protected device\n");
+ break;
+ case ADB_KEYBD:
+ ADB_DPRINTF("Found one keyboard on address %d\n", address);
+ adb_kbd_new(buf, *cur);
+ break;
+ case ADB_MOUSE:
+ ADB_DPRINTF("Found one mouse on address %d\n", address);
+ adb_mouse_new(buf, *cur);
+ break;
+ case ADB_ABS:
+ ADB_DPRINTF("Found one absolute positioning device\n");
+ break;
+ case ADB_MODEM:
+ ADB_DPRINTF("Found one modem\n");
+ break;
+ case ADB_RES:
+ ADB_DPRINTF("Found one ADB res device\n");
+ break;
+ case ADB_MISC:
+ ADB_DPRINTF("Found one ADB misc device\n");
+ break;
+ }
+ cur = &((*cur)->next);
+ }
+ break;
+ case 1:
+ case 3 ... 7:
+ /* SHOULD NOT HAPPEN : register 3 is always two bytes long */
+ ADB_DPRINTF("Invalid returned len for ADB register 3\n");
+ return -1;
+ case -1:
+ /* ADB ERROR */
+ ADB_DPRINTF("error gettting ADB register 3\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg,
+ uint8_t *buf, int len)
+{
+ uint8_t adb_send[ADB_BUF_SIZE], adb_rcv[ADB_BUF_SIZE];
+
+ //ADB_DPRINTF("cmd: %d reg: %d len: %d\n", cmd, reg, len);
+ if (dev->bus == NULL || dev->bus->req == NULL) {
+ ADB_DPRINTF("ERROR: invalid bus !\n");
+ for (;;);
+ }
+ /* Sanity checks */
+ if (cmd != ADB_LISTEN && len != 0) {
+ /* No buffer transmitted but for LISTEN command */
+ ADB_DPRINTF("in buffer for cmd %d\n", cmd);
+ return -1;
+ }
+ if (cmd == ADB_LISTEN && ((len < 2 || len > 8) || buf == NULL)) {
+ /* Need a buffer with a regular register size for LISTEN command */
+ ADB_DPRINTF("no/invalid buffer for ADB_LISTEN (%d)\n", len);
+ return -1;
+ }
+ if ((cmd == ADB_TALK || cmd == ADB_LISTEN) && reg > 3) {
+ /* Need a valid register number for LISTEN and TALK commands */
+ ADB_DPRINTF("invalid reg for TALK/LISTEN command (%d %d)\n", cmd, reg);
+ return -1;
+ }
+ switch (cmd) {
+ case ADB_SEND_RESET:
+ adb_send[0] = ADB_SEND_RESET;
+ break;
+ case ADB_FLUSH:
+ adb_send[0] = (dev->addr << 4) | ADB_FLUSH;
+ break;
+ case ADB_LISTEN:
+ memcpy(adb_send + 1, buf, len);
+ /* No break here */
+ case ADB_TALK:
+ adb_send[0] = (dev->addr << 4) | cmd | reg;
+ break;
+ }
+ memset(adb_rcv, 0, ADB_BUF_SIZE);
+ len = (*dev->bus->req)(dev->bus->host, adb_send, len + 1, adb_rcv);
+#ifdef DEBUG_ADB
+ //printk("%x %x %x %x\n", adb_rcv[0], adb_rcv[1], adb_rcv[2], adb_rcv[3]);
+#endif
+ switch (len) {
+ case 0:
+ /* No data */
+ break;
+ case 2 ... 8:
+ /* Register transmitted */
+ if (buf != NULL)
+ memcpy(buf, adb_rcv, len);
+ break;
+ default:
+ /* Should never happen */
+ //ADB_DPRINTF("Cmd %d returned %d bytes !\n", cmd, len);
+ return -1;
+ }
+ //ADB_DPRINTF("retlen: %d\n", len);
+
+ return len;
+}
diff --git a/qemu/roms/openbios/drivers/adb_bus.h b/qemu/roms/openbios/drivers/adb_bus.h
new file mode 100644
index 000000000..205b37536
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_bus.h
@@ -0,0 +1,104 @@
+/*
+ * ADB bus definitions for Open Hack'Ware
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+typedef struct adb_bus_t adb_bus_t;
+typedef struct adb_dev_t adb_dev_t;
+
+#define ADB_BUF_SIZE 8
+struct adb_bus_t {
+ void *host;
+ int (*req)(void *host, const uint8_t *snd_buf, int len, uint8_t *rcv_buf);
+ adb_dev_t *devices;
+};
+
+struct adb_dev_t {
+ adb_dev_t *next;
+ adb_bus_t *bus;
+ uint8_t addr;
+ uint8_t type;
+ void *state;
+};
+
+#define ADB_BUF_SIZE 8
+
+/* ADB commands */
+enum {
+ ADB_SEND_RESET = 0x00,
+ ADB_FLUSH = 0x01,
+ ADB_LISTEN = 0x08,
+ ADB_TALK = 0x0C,
+};
+/* ADB default IDs before relocation */
+enum {
+ ADB_PROTECT = 0x01,
+ ADB_KEYBD = 0x02,
+ ADB_MOUSE = 0x03,
+ ADB_ABS = 0x04,
+ ADB_MODEM = 0x05,
+ ADB_RES = 0x06,
+ ADB_MISC = 0x07,
+};
+/* ADB special device handlers IDs */
+enum {
+ ADB_CHADDR = 0x00,
+ ADB_CHADDR_ACTIV = 0xFD,
+ ADB_CHADDR_NOCOLL = 0xFE,
+ ADB_SELF_TEST = 0xFF,
+};
+
+int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg,
+ uint8_t *buf, int len);
+void adb_bus_reset (adb_bus_t *bus);
+adb_bus_t *adb_bus_new (void *host,
+ int (*req)(void *host, const uint8_t *snd_buf,
+ int len, uint8_t *rcv_buf));
+int adb_bus_init (char *path, adb_bus_t *bus);
+
+static inline int adb_reset (adb_bus_t *bus)
+{
+ adb_dev_t fake_device;
+
+ memset(&fake_device, 0, sizeof(adb_dev_t));
+ fake_device.bus = bus;
+
+ return adb_cmd(&fake_device, ADB_SEND_RESET, 0, NULL, 0);
+}
+
+static inline int adb_flush (adb_dev_t *dev)
+{
+ return adb_cmd(dev, ADB_FLUSH, 0, NULL, 0);
+}
+
+static inline int adb_reg_get (adb_dev_t *dev, uint8_t reg, uint8_t *buf)
+{
+ return adb_cmd(dev, ADB_TALK, reg, buf, 0);
+}
+
+static inline int adb_reg_set (adb_dev_t *dev, uint8_t reg,
+ uint8_t *buf, int len)
+{
+ return adb_cmd(dev, ADB_LISTEN, reg, buf, len);
+}
+
+#ifdef DEBUG_ADB
+#define ADB_DPRINTF(fmt, args...) \
+do { printk("ADB - %s: " fmt, __func__ , ##args); } while (0)
+#else
+#define ADB_DPRINTF(fmt, args...) do { } while (0)
+#endif
diff --git a/qemu/roms/openbios/drivers/adb_kbd.c b/qemu/roms/openbios/drivers/adb_kbd.c
new file mode 100644
index 000000000..be6099a03
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_kbd.c
@@ -0,0 +1,595 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB keyboard support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "kbd.h"
+
+#include "adb_bus.h"
+#include "adb_kbd.h"
+
+DECLARE_UNNAMED_NODE( keyboard, INSTALL_OPEN, sizeof(int));
+
+static void
+keyboard_open(int *idx)
+{
+ RET(-1);
+}
+
+static void
+keyboard_close(int *idx)
+{
+}
+
+static void keyboard_read(void);
+static void keyboard_getkeymap(void);
+
+NODE_METHODS( keyboard ) = {
+ { "open", keyboard_open },
+ { "close", keyboard_close },
+ { "read", keyboard_read },
+ { "get-key-map", keyboard_getkeymap },
+};
+
+/* VT100 escape sequences */
+
+enum {
+ KEY_UP = 0, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_PAGE_UP, KEY_PAGE_DOWN,
+ KEY_DELETE, KEY_HOME, KEY_END, KEY_HELP,
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8,
+ KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16
+};
+
+#define ADB_MAX_SEQUENCE_LEN 16
+
+static const char *ADB_sequences[] = {
+ [KEY_UP] = "A[",
+ [KEY_DOWN] = "B[",
+ [KEY_RIGHT] = "C[",
+ [KEY_LEFT] = "D[",
+ [KEY_PAGE_UP] = "~5[",
+ [KEY_PAGE_DOWN] = "~6[",
+ [KEY_DELETE] = "~3[",
+ [KEY_HOME] = "HO",
+ [KEY_END] = "FO",
+ [KEY_HELP] = "~2[",
+ [KEY_F1] = "PO",
+ [KEY_F2] = "QO",
+ [KEY_F3] = "RO",
+ [KEY_F4] = "SO",
+ [KEY_F5] = "~15[",
+ [KEY_F6] = "~17[",
+ [KEY_F7] = "~18[",
+ [KEY_F8] = "~19[",
+ [KEY_F9] = "~20[",
+ [KEY_F10] = "~21[",
+ [KEY_F11] = "~23[",
+ [KEY_F12] = "~24[",
+ [KEY_F13] = "~25[",
+ [KEY_F14] = "~26[",
+ [KEY_F15] = "~28[",
+ [KEY_F15] = "~29[",
+};
+
+/* ADB US keyboard translation map */
+
+static const keymap_t ADB_kbd_us[] = {
+ /* 0x00 */
+ { KBD_SH_CAPS, { 0x61, 0x41, 0x01, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x73, 0x53, 0x13, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x64, 0x44, 0x04, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x66, 0x46, 0x06, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x68, 0x48, 0x08, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x67, 0x47, 0x07, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x7A, 0x5A, 0x1A, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x78, 0x58, 0x18, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x08 */
+ { KBD_SH_CAPS, { 0x63, 0x43, 0x03, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x76, 0x56, 0x16, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x60, 0x40, 0x00, -1, -1, -1, -1, -1, /* ? */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x62, 0x42, 0x02, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x71, 0x51, 0x11, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x77, 0x57, 0x17, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x65, 0x45, 0x05, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x72, 0x52, 0x12, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x10 */
+ { KBD_SH_CAPS, { 0x79, 0x59, 0x19, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x74, 0x54, 0x14, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x31, 0x21, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x32, 0x40, 0x00, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x33, 0x23, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x34, 0x24, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x36, 0x5E, 0x1E, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x35, 0x25, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x18 */
+ { KBD_SH_CAPS, { 0x3D, 0x2B, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x39, 0x28, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x37, 0x26, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x2D, 0x5F, 0x1F, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x38, 0x2A, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x30, 0x29, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x5D, 0x7D, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x6F, 0x4F, 0x0F, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x20 */
+ { KBD_SH_CAPS, { 0x75, 0x55, 0x15, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x5B, 0x7B, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x69, 0x49, 0x09, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x70, 0x50, 0x10, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MOD_MAP(0x0D), },
+ { KBD_SH_CAPS, { 0x6C, 0x4C, 0x0C, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x6A, 0x4A, 0x0A, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x27, 0x22, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x28 */
+ { KBD_SH_CAPS, { 0x6B, 0x4B, 0x0B, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x3B, 0x3A, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x5C, 0x7C, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x2C, 0x3C, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x2F, 0x3F, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x6E, 0x4E, 0x0E, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x6D, 0x4D, 0x0D, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_CAPS, { 0x2E, 0x3E, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x30 : tab */
+ { KBD_MOD_MAP(0x09), },
+ /* 0x31 : space */
+ { KBD_MOD_MAP(0x20), },
+ /* 0x32 : '<' '>' */
+ { KBD_SH_CAPS, { 0x3C, 0x3E, -1, -1, -1, -1, -1, -1, /* ? */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x33 : backspace */
+ { KBD_MOD_MAP(0x08), },
+ { KBD_MAP_NONE, },
+ /* 0x35 : ESC */
+ { KBD_MOD_MAP(0x1B), },
+ /* 0x36 : control */
+ { KBD_MOD_MAP_LCTRL, },
+ /* 0x37 : command */
+ { KBD_MOD_MAP_LCMD, },
+ /* 0x38 : left shift */
+ { KBD_MOD_MAP_LSHIFT, },
+ /* 0x39 : caps-lock */
+ { KBD_MOD_MAP_CAPS, },
+ /* 0x3A : option */
+ { KBD_MOD_MAP_LOPT, },
+ /* 0x3B : left */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_LEFT)), },
+ /* 0x3C : right */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_RIGHT)), },
+ /* 0x3D : down */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_DOWN)), },
+ /* 0x3E : up */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_UP)), },
+ { KBD_MAP_NONE, },
+ /* 0x40 */
+ { KBD_MAP_NONE, },
+ { KBD_SH_NUML, { 0x7F, 0x2E, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ { KBD_SH_NONE, { 0x2A, 0x2A, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ { KBD_SH_NONE, { 0x2B, 0x2B, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ { KBD_MOD_MAP(0x7F), },
+ /* 0x48 */
+ { KBD_MAP_NONE, },
+ { KBD_MAP_NONE, },
+ { KBD_MAP_NONE, },
+ { KBD_SH_NONE, { 0x2F, 0x2F, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MOD_MAP(0x0D), },
+ { KBD_MAP_NONE, },
+ { KBD_SH_NONE, { 0x2D, 0x2D, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ /* 0x50 */
+ { KBD_MAP_NONE, },
+ { KBD_SH_NONE, { 0x3D, 0x3D, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x30, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x31, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x32, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x33, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x34, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ /* 0x58 */
+ { KBD_SH_NUML, { -1, 0x36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x37, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ { KBD_SH_NUML, { -1, 0x38, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_SH_NUML, { -1, 0x39, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, }, },
+ { KBD_MAP_NONE, },
+ { KBD_MOD_MAP(0x2F), },
+ { KBD_MAP_NONE, },
+ /* 0x60 : F5 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F5)), },
+ /* 0x61 : F6 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F6)), },
+ /* 0x62 : F7 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F7)), },
+ /* 0x63 : F3 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F3)), },
+ /* 0x64 : F8 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F8)), },
+ /* 0x65 : F9 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F9)), },
+ { KBD_MAP_NONE, },
+ /* 0x67 : F11 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F11)), },
+ /* 0x68 */
+ { KBD_MAP_NONE, },
+ /* 0x69 : F13 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F13)), },
+ { KBD_MAP_NONE, },
+ /* 0x6B : F14 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F14)), },
+ { KBD_MAP_NONE, },
+ /* 0x6D : F10 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F10)), },
+ { KBD_MAP_NONE, },
+ /* 0x6F : F12 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F12)), },
+ /* 0x70 */
+ { KBD_MAP_NONE, },
+ /* 0x71 : F15 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F15)), },
+ /* 0x72 : help */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_HELP)), },
+ /* 0x73 : home */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_HOME)), },
+ /* 0x74 : page up */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_PAGE_UP)), },
+ /* 0x75 : del */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_DELETE)), },
+ /* 0x76 : F4 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F4)), },
+ /* 0x77 : end */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_END)), },
+ /* 0x78 : F2 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F2)), },
+ /* 0x79 : page down */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_PAGE_UP)), },
+ /* 0x7A : F1 */
+ { KBD_MOD_MAP(KBD_SEQUENCE(KEY_F1)), },
+ /* 0x7B : right shift */
+ { KBD_MOD_MAP_RSHIFT, },
+ /* 0x7C : right option */
+ { KBD_MOD_MAP_ROPT, },
+ /* 0x7D : right control */
+ { KBD_MOD_MAP_RCTRL, },
+ { KBD_MAP_NONE, },
+ /* 0x7F : power */
+ { KBD_MAP_NONE, },
+};
+
+typedef struct adb_kbd_t adb_kbd_t;
+struct adb_kbd_t {
+ kbd_t kbd;
+ int next_key;
+ char sequence[ADB_MAX_SEQUENCE_LEN];
+ int len;
+ char keytable[32];
+};
+
+static adb_dev_t *my_adb_dev = NULL;
+
+static int adb_kbd_read (void *private)
+{
+ uint8_t buffer[ADB_BUF_SIZE];
+ adb_dev_t *dev = private;
+ adb_kbd_t *kbd;
+ int key;
+ int ret;
+
+ kbd = dev->state;
+
+ if (kbd->len > 0) {
+ ret = kbd->sequence[kbd->len-- - 1];
+ ADB_DPRINTF("Buffered %d (%02x)\n", ret, ret);
+ return ret;
+ }
+
+ /* Get saved state */
+ ret = -1;
+ for (key = -1; key == -1; ) {
+ if (kbd->next_key != -1) {
+ key = kbd->next_key;
+ kbd->next_key = -1;
+ } else {
+ if (adb_reg_get(dev, 0, buffer) != 2)
+ break;
+ kbd->next_key = buffer[1] == 0xFF ? -1 : buffer[1];
+ key = buffer[0];
+ }
+ ret = kbd_translate_key(&kbd->kbd, key & 0x7F, key >> 7, kbd->sequence);
+ if (ret > 0) {
+ kbd->len = ret;
+ ret = kbd->sequence[kbd->len-- - 1];
+ }
+
+ ADB_DPRINTF("Translated %d (%02x) into %d (%02x)\n",
+ key, key, ret, ret);
+ }
+
+ return ret;
+}
+
+
+void *adb_kbd_new (char *path, void *private)
+{
+ char buf[64];
+ int props[1];
+ phandle_t ph, aliases;
+ adb_kbd_t *kbd;
+ adb_dev_t *dev = private;
+ kbd = (adb_kbd_t*)malloc(sizeof(adb_kbd_t));
+ if (kbd != NULL) {
+ memset(kbd, 0, sizeof(adb_kbd_t));
+ kbd_set_keymap(&kbd->kbd, sizeof(ADB_kbd_us) / sizeof(keymap_t),
+ ADB_kbd_us, ADB_sequences);
+ kbd->next_key = -1;
+ kbd->len = 0;
+
+ /* Debugging BootX: the lines below force get-key-map to report that
+ * cmd-V is being held down, which forces BootX to run in verbose mode
+ * for debugging.
+ *
+ * TODO: if we can find a mapping between the get-key-map bitmap and
+ * ADB scancodes, the keyboard driver should be altered to update this
+ * accordingly.
+ */
+
+ /*
+ kbd->keytable[3] = 0x40;
+ kbd->keytable[28] = 0x10;
+ */
+
+ dev->state = kbd;
+ my_adb_dev = dev;
+ }
+
+ snprintf(buf, sizeof(buf), "%s/keyboard", path);
+ REGISTER_NAMED_NODE( keyboard, buf);
+
+ ph = find_dev(buf);
+
+ set_property(ph, "device_type", "keyboard", 9);
+ props[0] = __cpu_to_be32(dev->addr);
+ set_property(ph, "reg", (char *)&props, sizeof(props));
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "adb-keyboard", buf, strlen(buf) + 1);
+
+ return kbd;
+}
+
+/* ( addr len -- actual ) */
+static void keyboard_read(void)
+{
+ char *addr;
+ int len, key, i;
+ len=POP();
+ addr=(char *)cell2pointer(POP());
+
+ for (i = 0; i < len; i++) {
+ key = adb_kbd_read(my_adb_dev);
+ if (key == -1 || key == -2)
+ break;
+ *addr++ = (char)key;
+ }
+ PUSH(i);
+}
+
+/* ( -- keymap ) (?) */
+/* should return a pointer to an array with 32 bytes (256 bits) */
+static void keyboard_getkeymap(void)
+{
+ adb_kbd_t *kbd = my_adb_dev->state;
+
+ PUSH( pointer2cell(kbd->keytable) );
+}
diff --git a/qemu/roms/openbios/drivers/adb_kbd.h b/qemu/roms/openbios/drivers/adb_kbd.h
new file mode 100644
index 000000000..b219c12e7
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_kbd.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB keyboard support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+void *adb_kbd_new (char *path, void *private);
diff --git a/qemu/roms/openbios/drivers/adb_mouse.c b/qemu/roms/openbios/drivers/adb_mouse.c
new file mode 100644
index 000000000..b94a34420
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_mouse.c
@@ -0,0 +1,67 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB mouse support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "adb_bus.h"
+#include "adb_mouse.h"
+
+DECLARE_UNNAMED_NODE( mouse, INSTALL_OPEN, sizeof(int));
+
+static void
+mouse_open(int *idx)
+{
+ RET(-1);
+}
+
+static void
+mouse_close(int *idx)
+{
+}
+
+NODE_METHODS( mouse ) = {
+ { "open", mouse_open },
+ { "close", mouse_close },
+};
+
+void adb_mouse_new (char *path, void *private)
+{
+ char buf[64];
+ int props[1];
+ phandle_t ph, aliases;
+ adb_dev_t *dev = private;
+
+ snprintf(buf, sizeof(buf), "%s/mouse", path);
+ REGISTER_NAMED_NODE( mouse, buf);
+
+ ph = find_dev(buf);
+
+ set_property(ph, "device_type", "mouse", 6);
+ props[0] = __cpu_to_be32(dev->addr);
+ set_property(ph, "reg", (char *)&props, sizeof(props));
+ set_int_property(ph, "#buttons", 3);
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "adb-mouse", buf, strlen(buf) + 1);
+}
diff --git a/qemu/roms/openbios/drivers/adb_mouse.h b/qemu/roms/openbios/drivers/adb_mouse.h
new file mode 100644
index 000000000..1f37dac7c
--- /dev/null
+++ b/qemu/roms/openbios/drivers/adb_mouse.h
@@ -0,0 +1,22 @@
+/*
+ *
+ * Open Hack'Ware BIOS ADB mouse support, ported to OpenBIOS
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ * Copyright (c) 2005 Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+void adb_mouse_new (char *path, void *private);
diff --git a/qemu/roms/openbios/drivers/build.xml b/qemu/roms/openbios/drivers/build.xml
new file mode 100644
index 000000000..bd1abd358
--- /dev/null
+++ b/qemu/roms/openbios/drivers/build.xml
@@ -0,0 +1,41 @@
+<build>
+
+ <library name="drivers" type="static" target="target">
+ <object source="pci.c" condition="DRIVER_PCI"/>
+ <object source="pci_database.c" condition="DRIVER_PCI"/>
+ <object source="ide.c" condition="DRIVER_IDE"/>
+ <object source="timer.c" condition="DRIVER_IDE"/>
+ <object source="kbd.c" condition="DRIVER_ADB"/>
+ <object source="adb_bus.c" condition="DRIVER_ADB"/>
+ <object source="adb_kbd.c" condition="DRIVER_ADB"/>
+ <object source="adb_mouse.c" condition="DRIVER_ADB"/>
+ <object source="cuda.c" condition="DRIVER_ADB"/>
+ <object source="floppy.c" condition="DRIVER_FLOPPY"/>
+ <object source="iommu.c" condition="DRIVER_SBUS"/>
+ <object source="sbus.c" condition="DRIVER_SBUS"/>
+ <object source="esp.c" condition="DRIVER_ESP"/>
+ <object source="obio.c" condition="DRIVER_OBIO"/>
+ <object source="vga_load_regs.c" condition="DRIVER_VGA"/>
+ <object source="vga_set_mode.c" condition="DRIVER_VGA"/>
+ <object source="macio.c" condition="DRIVER_MACIO"/>
+ <object source="pc_kbd.c" condition="DRIVER_PC_KBD"/>
+ <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">
+ <object source="pci.fs" condition="DRIVER_PCI"/>
+ <object source="sbus.fs" condition="DRIVER_SBUS"/>
+ <object source="esp.fs" condition="DRIVER_ESP"/>
+ </dictionary>
+
+ <fcode source="tcx.fs" name="QEMU,tcx.bin" condition="DRIVER_SBUS" />
+ <fcode source="cgthree.fs" name="QEMU,cgthree.bin" condition="DRIVER_SBUS" />
+ <fcode source="vga.fs" name="QEMU,VGA.bin" condition="DRIVER_VGA" />
+
+</build>
diff --git a/qemu/roms/openbios/drivers/cgthree.fs b/qemu/roms/openbios/drivers/cgthree.fs
new file mode 100644
index 000000000..aa0c90cdd
--- /dev/null
+++ b/qemu/roms/openbios/drivers/cgthree.fs
@@ -0,0 +1,197 @@
+\
+\ Fcode payload for QEMU CG3 graphics card
+\
+\ This is the Forth source for an Fcode payload to initialise
+\ the QEMU CG3 graphics card.
+\
+\ (C) Copyright 2013 Mark Cave-Ayland
+\
+
+fcode-version3
+
+\
+\ Instead of using fixed values for the framebuffer address and the width
+\ and height, grab the ones passed in by QEMU/generated by OpenBIOS
+\
+
+: (find-xt) \ ( str len -- xt | -1 )
+ $find if
+ exit
+ else
+ 2drop
+ -1
+ then
+;
+
+: (is-openbios) \ ( -- true | false )
+ " openbios-video-width" (find-xt) -1 <> if
+ -1
+ else
+ 0
+ then
+;
+
+" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt
+" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt
+" depth-bits" (find-xt) cell+ value depth-bits-xt
+" line-bytes" (find-xt) cell+ value line-bytes-xt
+" debug-type" (find-xt) value debug-type-xt
+
+: openbios-video-width
+ (is-openbios) if
+ openbios-video-width-xt @
+ else
+ h# 400
+ then
+;
+
+: openbios-video-height
+ (is-openbios) if
+ openbios-video-height-xt @
+ else
+ h# 300
+ then
+;
+
+: depth-bits
+ (is-openbios) if
+ depth-bits-xt @
+ else
+ h# 8
+ then
+;
+
+: line-bytes
+ (is-openbios) if
+ line-bytes-xt @
+ else
+ h# 400
+ then
+;
+
+: debug-type debug-type-xt execute ;
+
+\
+\ Registers
+\
+
+h# 400000 constant cg3-off-dac
+h# 20 constant /cg3-off-dac
+
+h# 800000 constant cg3-off-fb
+h# c0000 constant /cg3-off-fb
+
+: >cg3-reg-spec ( offset size -- encoded-reg )
+ >r 0 my-address d+ my-space encode-phys r> encode-int encode+
+;
+
+: cg3-reg
+ \ A real cg3 rom appears to just map the entire region with a
+ \ single entry
+ h# 0 h# 1000000 >cg3-reg-spec
+ " reg" property
+;
+
+: do-map-in ( offset size -- virt )
+ >r my-space r> " map-in" $call-parent
+;
+
+: do-map-out ( virt size )
+ " map-out" $call-parent
+;
+
+\
+\ DAC
+\
+
+-1 value cg3-dac
+-1 value fb-addr
+
+: dac! ( data reg# -- )
+ cg3-dac + c!
+;
+
+external
+
+: color! ( r g b c# -- )
+ 0 dac! ( r g b )
+ swap rot ( b g r )
+ 4 dac! ( b g )
+ 4 dac! ( b )
+ 4 dac! ( )
+;
+
+headerless
+
+\
+\ Mapping
+\
+
+: dac-map
+ cg3-off-dac /cg3-off-dac do-map-in to cg3-dac
+;
+
+: fb-map
+ cg3-off-fb h# c0000 do-map-in to fb-addr
+;
+
+: map-regs
+ dac-map fb-map
+;
+
+\
+\ Installation
+\
+
+" cgthree" device-name
+" display" device-type
+" SUNW,501-1415" model
+
+: qemu-cg3-driver-install ( -- )
+ cg3-dac -1 = if
+ map-regs
+
+ \ Initial pallette taken from Sun's "Writing FCode Programs"
+ h# ff h# ff h# ff h# 0 color! \ Background white
+ h# 0 h# 0 h# 0 h# ff color! \ Foreground black
+ h# 64 h# 41 h# b4 h# 1 color! \ SUN-blue logo
+
+ fb-addr to frame-buffer-adr
+ default-font set-font
+
+ frame-buffer-adr encode-int " address" property
+
+ openbios-video-width openbios-video-height over char-width / over char-height /
+ fb8-install
+ then
+;
+
+: qemu-cg3-driver-init
+
+ cg3-reg
+
+ openbios-video-height encode-int " height" property
+ openbios-video-width encode-int " width" property
+ line-bytes encode-int " linebytes" property
+
+ h# 39 encode-int 0 encode-int encode+ " intr" property
+
+ \ Monitor sense. Some searching suggests that this is
+ \ 5 for 1024x768 and 7 for 1152x900
+ openbios-video-width h# 480 = if
+ h# 7
+ else
+ h# 5
+ then
+ encode-int " monitor-sense" property
+
+ " SUNW" encode-string " manufacturer" property
+ " ISO8859-1" encode-string " character-set" property
+ h# c encode-int " cursorshift" property
+
+ ['] qemu-cg3-driver-install is-install
+;
+
+qemu-cg3-driver-init
+
+end0
diff --git a/qemu/roms/openbios/drivers/cuda.c b/qemu/roms/openbios/drivers/cuda.c
new file mode 100644
index 000000000..9555dea49
--- /dev/null
+++ b/qemu/roms/openbios/drivers/cuda.c
@@ -0,0 +1,424 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "drivers/drivers.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "macio.h"
+#include "cuda.h"
+
+//#define DEBUG_CUDA
+#ifdef DEBUG_CUDA
+#define CUDA_DPRINTF(fmt, args...) \
+ do { printk("CUDA - %s: " fmt, __func__ , ##args); } while (0)
+#else
+#define CUDA_DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+#define IO_CUDA_OFFSET 0x00016000
+#define IO_CUDA_SIZE 0x00002000
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS 0x200 /* skip between registers */
+#define B 0 /* B-side data */
+#define A RS /* A-side data */
+#define DIRB (2*RS) /* B-side direction (1=output) */
+#define DIRA (3*RS) /* A-side direction (1=output) */
+#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
+#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
+#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
+#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */
+#define SR (10*RS) /* Shift register */
+#define ACR (11*RS) /* Auxiliary control register */
+#define PCR (12*RS) /* Peripheral control register */
+#define IFR (13*RS) /* Interrupt flag register */
+#define IER (14*RS) /* Interrupt enable register */
+#define ANH (15*RS) /* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+
+#define CUDA_BUF_SIZE 16
+
+#define ADB_PACKET 0
+#define CUDA_PACKET 1
+
+/* CUDA commands (2nd byte) */
+#define CUDA_GET_TIME 0x03
+#define CUDA_SET_TIME 0x09
+#define CUDA_POWERDOWN 0x0a
+#define CUDA_RESET_SYSTEM 0x11
+
+static uint8_t cuda_readb (cuda_t *dev, int reg)
+{
+ return *(volatile uint8_t *)(dev->base + reg);
+}
+
+static void cuda_writeb (cuda_t *dev, int reg, uint8_t val)
+{
+ *(volatile uint8_t *)(dev->base + reg) = val;
+}
+
+static void cuda_wait_irq (cuda_t *dev)
+{
+ int val;
+
+// CUDA_DPRINTF("\n");
+ for(;;) {
+ val = cuda_readb(dev, IFR);
+ cuda_writeb(dev, IFR, val & 0x7f);
+ if (val & SR_INT)
+ break;
+ }
+}
+
+
+
+static int cuda_request (cuda_t *dev, uint8_t pkt_type, const uint8_t *buf,
+ int buf_len, uint8_t *obuf)
+{
+ int i, obuf_len, val;
+
+ cuda_writeb(dev, ACR, cuda_readb(dev, ACR) | SR_OUT);
+ cuda_writeb(dev, SR, pkt_type);
+ cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP);
+ if (buf) {
+ //CUDA_DPRINTF("Send buf len: %d\n", buf_len);
+ /* send 'buf' */
+ for(i = 0; i < buf_len; i++) {
+ cuda_wait_irq(dev);
+ cuda_writeb(dev, SR, buf[i]);
+ cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK);
+ }
+ }
+ cuda_wait_irq(dev);
+ cuda_writeb(dev, ACR, cuda_readb(dev, ACR) & ~SR_OUT);
+ cuda_readb(dev, SR);
+ cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK);
+
+ obuf_len = 0;
+ if (obuf) {
+ cuda_wait_irq(dev);
+ cuda_readb(dev, SR);
+ cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP);
+ for(;;) {
+ cuda_wait_irq(dev);
+ val = cuda_readb(dev, SR);
+ if (obuf_len < CUDA_BUF_SIZE)
+ obuf[obuf_len++] = val;
+ if (cuda_readb(dev, B) & TREQ)
+ break;
+ cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK);
+ }
+ cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK);
+
+ cuda_wait_irq(dev);
+ cuda_readb(dev, SR);
+ }
+// CUDA_DPRINTF("Got len: %d\n", obuf_len);
+
+ return obuf_len;
+}
+
+
+
+static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len,
+ uint8_t *rcv_buf)
+{
+ uint8_t buffer[CUDA_BUF_SIZE], *pos;
+
+ // CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]);
+ len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer);
+ if (len > 1 && buffer[0] == ADB_PACKET) {
+ pos = buffer + 2;
+ len -= 2;
+ } else {
+ pos = buffer + 1;
+ len = -1;
+ }
+ memcpy(rcv_buf, pos, len);
+
+ return len;
+}
+
+
+DECLARE_UNNAMED_NODE(ob_cuda, INSTALL_OPEN, sizeof(int));
+
+static cuda_t *main_cuda;
+
+static void
+ppc32_reset_all(void)
+{
+ uint8_t cmdbuf[2], obuf[64];
+
+ cmdbuf[0] = CUDA_RESET_SYSTEM;
+ cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+static void
+ppc32_poweroff(void)
+{
+ uint8_t cmdbuf[2], obuf[64];
+
+ cmdbuf[0] = CUDA_POWERDOWN;
+ cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+static void
+ob_cuda_initialize (int *idx)
+{
+ phandle_t ph=get_cur_dev();
+ int props[2];
+
+ push_str("via-cuda");
+ fword("device-type");
+
+ set_int_property(ph, "#address-cells", 1);
+ set_int_property(ph, "#size-cells", 0);
+
+ set_property(ph, "compatible", "cuda", 5);
+
+ props[0] = __cpu_to_be32(IO_CUDA_OFFSET);
+ props[1] = __cpu_to_be32(IO_CUDA_SIZE);
+
+ set_property(ph, "reg", (char *)&props, sizeof(props));
+
+ /* on newworld machines the cuda is on interrupt 0x19 */
+
+ props[0] = 0x19;
+ props[1] = 0;
+ NEWWORLD(set_property(ph, "interrupts", (char *)props, sizeof(props)));
+ NEWWORLD(set_int_property(ph, "#interrupt-cells", 2));
+
+ /* we emulate an oldworld hardware, so we must use
+ * non-standard oldworld property (needed by linux 2.6.18)
+ */
+
+ OLDWORLD(set_int_property(ph, "AAPL,interrupts", 0x12));
+
+ bind_func("ppc32-reset-all", ppc32_reset_all);
+ push_str("' ppc32-reset-all to reset-all");
+ fword("eval");
+}
+
+static void
+ob_cuda_open(int *idx)
+{
+ RET(-1);
+}
+
+static void
+ob_cuda_close(int *idx)
+{
+}
+
+NODE_METHODS(ob_cuda) = {
+ { NULL, ob_cuda_initialize },
+ { "open", ob_cuda_open },
+ { "close", ob_cuda_close },
+};
+
+DECLARE_UNNAMED_NODE(rtc, INSTALL_OPEN, sizeof(int));
+
+static void
+rtc_open(int *idx)
+{
+ RET(-1);
+}
+
+/*
+ * get-time ( -- second minute hour day month year )
+ *
+ */
+
+static const int days_month[12] =
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static const int days_month_leap[12] =
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static inline int is_leap(int year)
+{
+ return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+}
+
+static void
+rtc_get_time(int *idx)
+{
+ uint8_t cmdbuf[2], obuf[64];
+ ucell second, minute, hour, day, month, year;
+ uint32_t now;
+ int current;
+ const int *days;
+
+ cmdbuf[0] = CUDA_GET_TIME;
+ cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+
+ /* seconds since 01/01/1904 */
+
+ now = (obuf[3] << 24) + (obuf[4] << 16) + (obuf[5] << 8) + obuf[6];
+
+ second = now % 60;
+ now /= 60;
+
+ minute = now % 60;
+ now /= 60;
+
+ hour = now % 24;
+ now /= 24;
+
+ year = now * 100 / 36525;
+ now -= year * 36525 / 100;
+ year += 1904;
+
+ days = is_leap(year) ? days_month_leap : days_month;
+
+ current = 0;
+ month = 0;
+ while (month < 12) {
+ if (now <= current + days[month]) {
+ break;
+ }
+ current += days[month];
+ month++;
+ }
+ month++;
+
+ day = now - current;
+
+ PUSH(second);
+ PUSH(minute);
+ PUSH(hour);
+ PUSH(day);
+ PUSH(month);
+ PUSH(year);
+}
+
+/*
+ * set-time ( second minute hour day month year -- )
+ *
+ */
+
+static void
+rtc_set_time(int *idx)
+{
+ uint8_t cmdbuf[5], obuf[3];
+ ucell second, minute, hour, day, month, year;
+ const int *days;
+ uint32_t now;
+ unsigned int nb_days;
+ int i;
+
+ year = POP();
+ month = POP();
+ day = POP();
+ hour = POP();
+ minute = POP();
+ second = POP();
+
+ days = is_leap(year) ? days_month_leap : days_month;
+ nb_days = (year - 1904) * 36525 / 100 + day;
+ for (i = 0; i < month - 1; i++)
+ nb_days += days[i];
+
+ now = (((nb_days * 24) + hour) * 60 + minute) * 60 + second;
+
+ cmdbuf[0] = CUDA_SET_TIME;
+ cmdbuf[1] = now >> 24;
+ cmdbuf[2] = now >> 16;
+ cmdbuf[3] = now >> 8;
+ cmdbuf[4] = now;
+
+ cuda_request(main_cuda, CUDA_PACKET, cmdbuf, sizeof(cmdbuf), obuf);
+}
+
+NODE_METHODS(rtc) = {
+ { "open", rtc_open },
+ { "get-time", rtc_get_time },
+ { "set-time", rtc_set_time },
+};
+
+static void
+rtc_init(char *path)
+{
+ phandle_t ph, aliases;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%s/rtc", path);
+ REGISTER_NAMED_NODE(rtc, buf);
+
+ ph = find_dev(buf);
+ set_property(ph, "device_type", "rtc", 4);
+ set_property(ph, "compatible", "rtc", 4);
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "rtc", buf, strlen(buf) + 1);
+
+}
+
+static void
+powermgt_init(char *path)
+{
+ phandle_t ph;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%s/power-mgt", path);
+ REGISTER_NAMED_NODE(rtc, buf);
+
+ ph = find_dev(buf);
+ set_property(ph, "device_type", "power-mgt", 10);
+ set_property(ph, "compatible", "power-mgt", 10);
+}
+
+cuda_t *cuda_init (const char *path, phys_addr_t base)
+{
+ cuda_t *cuda;
+ char buf[64];
+ phandle_t aliases;
+
+ base += IO_CUDA_OFFSET;
+ CUDA_DPRINTF(" base=" FMT_plx "\n", base);
+ cuda = malloc(sizeof(cuda_t));
+ if (cuda == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf), "%s/via-cuda", path);
+ REGISTER_NAMED_NODE(ob_cuda, buf);
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "via-cuda", buf, strlen(buf) + 1);
+
+ cuda->base = base;
+ cuda_writeb(cuda, B, cuda_readb(cuda, B) | TREQ | TIP);
+#ifdef CONFIG_DRIVER_ADB
+ cuda->adb_bus = adb_bus_new(cuda, &cuda_adb_req);
+ if (cuda->adb_bus == NULL) {
+ free(cuda);
+ return NULL;
+ }
+ adb_bus_init(buf, cuda->adb_bus);
+#endif
+
+ rtc_init(buf);
+ powermgt_init(buf);
+
+ main_cuda = cuda;
+
+ device_end();
+ bind_func("poweroff", ppc32_poweroff);
+
+ return cuda;
+}
diff --git a/qemu/roms/openbios/drivers/cuda.h b/qemu/roms/openbios/drivers/cuda.h
new file mode 100644
index 000000000..d3818c03c
--- /dev/null
+++ b/qemu/roms/openbios/drivers/cuda.h
@@ -0,0 +1,17 @@
+#include "adb_bus.h"
+
+struct cuda_t {
+ phys_addr_t base;
+ adb_bus_t *adb_bus;
+};
+typedef struct cuda_t cuda_t;
+
+enum {
+ CHARDEV_KBD = 0,
+ CHARDEV_MOUSE,
+ CHARDEV_SERIAL,
+ CHARDEV_DISPLAY,
+ CHARDEV_LAST,
+};
+
+cuda_t *cuda_init (const char *path, phys_addr_t base);
diff --git a/qemu/roms/openbios/drivers/escc.c b/qemu/roms/openbios/drivers/escc.c
new file mode 100644
index 000000000..240043be3
--- /dev/null
+++ b/qemu/roms/openbios/drivers/escc.c
@@ -0,0 +1,476 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "drivers/drivers.h"
+#include "libopenbios/ofmem.h"
+
+#include "escc.h"
+
+/* ******************************************************************
+ * serial console functions
+ * ****************************************************************** */
+
+static volatile unsigned char *escc_serial_dev;
+
+#define CTRL(addr) (*(volatile unsigned char *)(uintptr_t)(addr))
+#ifdef CONFIG_DRIVER_ESCC_SUN
+#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 2))
+#else
+#define DATA(addr) (*(volatile unsigned char *)(uintptr_t)(addr + 16))
+#endif
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+#ifdef CONFIG_DRIVER_ESCC_SUN
+#define ESCC_CLOCK 4915200 /* Zilog input clock rate. */
+#else
+#define ESCC_CLOCK 3686400
+#endif
+#define ESCC_CLOCK_DIVISOR 16 /* Divisor this driver uses. */
+
+/* Write Register 3 */
+#define RxENAB 0x1 /* Rx Enable */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+#define SB1 0x4 /* 1 stop bit/char */
+#define X16CLK 0x40 /* x16 clock mode */
+
+/* Write Register 5 */
+#define RTS 0x2 /* RTS */
+#define TxENAB 0x8 /* Tx Enable */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define DTR 0x80 /* DTR */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENAB 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+
+int escc_uart_charav(uintptr_t port)
+{
+ return (CTRL(port) & Rx_CH_AV) != 0;
+}
+
+char escc_uart_getchar(uintptr_t port)
+{
+ while (!escc_uart_charav(port))
+ ;
+ return DATA(port) & 0177;
+}
+
+static void escc_uart_port_putchar(uintptr_t port, unsigned char c)
+{
+ if (!escc_serial_dev)
+ return;
+
+ if (c == '\n')
+ escc_uart_port_putchar(port, '\r');
+ while (!(CTRL(port) & Tx_BUF_EMP))
+ ;
+ DATA(port) = c;
+}
+
+static void uart_init_line(volatile unsigned char *port, unsigned long baud)
+{
+ CTRL(port) = 4; // reg 4
+ CTRL(port) = SB1 | X16CLK; // no parity, async, 1 stop bit, 16x
+ // clock
+
+ baud = BPS_TO_BRG(baud, ESCC_CLOCK / ESCC_CLOCK_DIVISOR);
+
+ CTRL(port) = 12; // reg 12
+ CTRL(port) = baud & 0xff;
+ CTRL(port) = 13; // reg 13
+ CTRL(port) = (baud >> 8) & 0xff;
+ CTRL(port) = 14; // reg 14
+ CTRL(port) = BRSRC | BRENAB;
+
+ CTRL(port) = 3; // reg 3
+ CTRL(port) = RxENAB | Rx8; // enable rx, 8 bits/char
+
+ CTRL(port) = 5; // reg 5
+ CTRL(port) = RTS | TxENAB | Tx8 | DTR; // enable tx, 8 bits/char,
+ // set RTS & DTR
+
+}
+
+int escc_uart_init(phys_addr_t port, unsigned long speed)
+{
+#ifdef CONFIG_DRIVER_ESCC_SUN
+ escc_serial_dev = (unsigned char *)ofmem_map_io(port & ~7ULL, ZS_REGS);
+ escc_serial_dev += port & 7ULL;
+#else
+ escc_serial_dev = (unsigned char *)(uintptr_t)port;
+#endif
+ uart_init_line(escc_serial_dev, speed);
+ return -1;
+}
+
+void escc_uart_putchar(int c)
+{
+ escc_uart_port_putchar((uintptr_t)escc_serial_dev, (unsigned char) (c & 0xff));
+}
+
+void serial_cls(void)
+{
+ escc_uart_putchar(27);
+ escc_uart_putchar('[');
+ escc_uart_putchar('H');
+ escc_uart_putchar(27);
+ escc_uart_putchar('[');
+ escc_uart_putchar('J');
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_read(ucell *address)
+{
+ char *addr;
+ int len;
+
+ len = POP();
+ addr = (char *)cell2pointer(POP());
+
+ if (len < 1)
+ printk("escc_read: bad len, addr %p len %x\n", addr, len);
+
+ if (escc_uart_charav(*address)) {
+ *addr = (char)escc_uart_getchar(*address);
+ PUSH(1);
+ } else {
+ PUSH(0);
+ }
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_write(ucell *address)
+{
+ unsigned char *addr;
+ int i, len;
+
+ len = POP();
+ addr = (unsigned char *)cell2pointer(POP());
+
+ for (i = 0; i < len; i++) {
+ escc_uart_port_putchar(*address, addr[i]);
+ }
+ PUSH(len);
+}
+
+static void
+escc_close(void)
+{
+}
+
+static void
+escc_open(ucell *address)
+{
+#ifdef CONFIG_DRIVER_ESCC_SUN
+ int len;
+ phandle_t ph;
+ unsigned long *prop;
+ char *args;
+
+ fword("my-self");
+ fword("ihandle>phandle");
+ ph = (phandle_t)POP();
+ prop = (unsigned long *)get_property(ph, "address", &len);
+ *address = *prop;
+ fword("my-args");
+ args = pop_fstr_copy();
+ if (args) {
+ if (args[0] == 'a')
+ *address += 4;
+ //printk("escc_open: address %lx, args %s\n", *address, args);
+ free(args);
+ }
+#else
+ *address = (unsigned long)escc_serial_dev; // XXX
+#endif
+ RET ( -1 );
+}
+
+DECLARE_UNNAMED_NODE(escc, INSTALL_OPEN, sizeof(ucell));
+
+NODE_METHODS(escc) = {
+ { "open", escc_open },
+ { "close", escc_close },
+ { "read", escc_read },
+ { "write", escc_write },
+};
+
+#ifdef CONFIG_DRIVER_ESCC_SUN
+static volatile unsigned char *kbd_dev;
+
+void kbd_init(phys_addr_t base)
+{
+ kbd_dev = (unsigned char *)ofmem_map_io(base, 2 * 4);
+ kbd_dev += 4;
+}
+
+static const unsigned char sunkbd_keycode[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '\\', 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ' ',
+};
+
+static const unsigned char sunkbd_keycode_shifted[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '|', 13,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ' ',
+};
+
+static int shiftstate;
+
+int
+keyboard_dataready(void)
+{
+ return ((kbd_dev[0] & 1) == 1);
+}
+
+unsigned char
+keyboard_readdata(void)
+{
+ unsigned char ch;
+
+ while (!keyboard_dataready()) { }
+
+ do {
+ ch = kbd_dev[2] & 0xff;
+ if (ch == 99)
+ shiftstate |= 1;
+ else if (ch == 110)
+ shiftstate |= 2;
+ else if (ch == 227)
+ shiftstate &= ~1;
+ else if (ch == 238)
+ shiftstate &= ~2;
+ //printk("getch: %d\n", ch);
+ } // If release, wait for key press
+ while ((ch & 0x80) == 0x80 || ch == 238 || ch == 227);
+ //printk("getch rel: %d\n", ch);
+ ch &= 0x7f;
+ if (shiftstate)
+ ch = sunkbd_keycode_shifted[ch];
+ else
+ ch = sunkbd_keycode[ch];
+ //printk("getch xlate: %d\n", ch);
+
+ return ch;
+}
+
+/* ( addr len -- actual ) */
+static void
+escc_read_keyboard(void)
+{
+ unsigned char *addr;
+ int len;
+
+ len = POP();
+ addr = (unsigned char *)POP();
+
+ if (len < 1)
+ printk("escc_read: bad len, addr %p len %x\n", addr, len);
+
+ if (keyboard_dataready()) {
+ *addr = keyboard_readdata();
+ PUSH(1);
+ } else {
+ PUSH(0);
+ }
+}
+
+DECLARE_UNNAMED_NODE(escc_keyboard, INSTALL_OPEN, sizeof(ucell));
+
+NODE_METHODS(escc_keyboard) = {
+ { "open", escc_open },
+ { "close", escc_close },
+ { "read", escc_read_keyboard },
+};
+
+void
+ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard)
+{
+ char nodebuff[256];
+ phandle_t aliases;
+
+ ob_new_obio_device("zs", "serial");
+
+ ob_reg(base, offset, ZS_REGS, 1);
+
+ PUSH(slave);
+ fword("encode-int");
+ push_str("slave");
+ fword("property");
+
+ if (keyboard) {
+ PUSH(0);
+ PUSH(0);
+ push_str("keyboard");
+ fword("property");
+
+ PUSH(0);
+ PUSH(0);
+ push_str("mouse");
+ fword("property");
+ }
+
+ ob_intr(intr);
+
+ PUSH(0);
+ PUSH(0);
+ push_str("port-a-ignore-cd");
+ fword("property");
+
+ PUSH(0);
+ PUSH(0);
+ push_str("port-b-ignore-cd");
+ fword("property");
+
+ fword("finish-device");
+
+ snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x",
+ (int)offset & 0xffffffff);
+ if (keyboard) {
+ REGISTER_NODE_METHODS(escc_keyboard, nodebuff);
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
+ } else {
+ REGISTER_NODE_METHODS(escc, nodebuff);
+
+ aliases = find_dev("/aliases");
+ snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:a",
+ (int)offset & 0xffffffff);
+ set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
+
+ snprintf(nodebuff, sizeof(nodebuff), "/obio/zs@0,%x:b",
+ (int)offset & 0xffffffff);
+ set_property(aliases, "ttyb", nodebuff, strlen(nodebuff) + 1);
+
+ }
+}
+
+#else
+
+static void
+escc_add_channel(const char *path, const char *node, phys_addr_t addr,
+ uint32_t offset)
+{
+ char buf[64], tty[32];
+ phandle_t dnode, aliases;
+ int len;
+ cell props[2];
+
+ /* add device */
+
+ snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
+
+ REGISTER_NAMED_NODE(escc, buf);
+
+ activate_device(buf);
+
+ /* add aliases */
+
+ aliases = find_dev("/aliases");
+
+ snprintf(buf, sizeof(buf), "%s/ch-%s", path, node);
+ OLDWORLD(snprintf(tty, sizeof(tty), "tty%s", node));
+ OLDWORLD(set_property(aliases, tty, buf, strlen(buf) + 1));
+ snprintf(tty, sizeof(tty), "scc%s", node);
+ set_property(aliases, tty, buf, strlen(buf) + 1);
+
+ /* add properties */
+
+ dnode = find_dev(buf);
+ set_property(dnode, "device_type", "serial",
+ strlen("serial") + 1);
+
+ snprintf(buf, sizeof(buf), "ch-%s", node);
+ len = strlen(buf) + 1;
+ snprintf(buf + len, sizeof(buf) - len, "CHRP,es2");
+ set_property(dnode, "compatible", buf, len + 9);
+
+ props[0] = IO_ESCC_OFFSET + offset * 0x20;
+ props[1] = 0x00000020;
+ set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell));
+
+ props[0] = addr + IO_ESCC_OFFSET + offset * 0x20;
+ OLDWORLD(set_property(dnode, "AAPL,address",
+ (char *)&props, 1 * sizeof(cell)));
+
+ props[0] = 0x00000010 - offset;
+ OLDWORLD(set_property(dnode, "AAPL,interrupts",
+ (char *)&props, 1 * sizeof(cell)));
+
+ props[0] = (0x24) + offset;
+ props[1] = 0;
+ NEWWORLD(set_property(dnode, "interrupts",
+ (char *)&props, 2 * sizeof(cell)));
+
+ device_end();
+
+ uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20,
+ CONFIG_SERIAL_SPEED);
+}
+
+void
+escc_init(const char *path, phys_addr_t addr)
+{
+ char buf[64];
+ int props[2];
+ phandle_t dnode;
+
+ push_str(path);
+ fword("find-device");
+ fword("new-device");
+
+ push_str("escc");
+ fword("device-name");
+
+ snprintf(buf, sizeof(buf), "%s/escc", path);
+
+ dnode = find_dev(buf);
+
+ set_int_property(dnode, "#address-cells", 1);
+ props[0] = __cpu_to_be32(IO_ESCC_OFFSET);
+ props[1] = __cpu_to_be32(IO_ESCC_SIZE);
+ set_property(dnode, "reg", (char *)&props, sizeof(props));
+ set_property(dnode, "device_type", "escc",
+ strlen("escc") + 1);
+ set_property(dnode, "compatible", "escc\0CHRP,es0", 14);
+
+ fword("finish-device");
+
+ escc_add_channel(buf, "a", addr, 1);
+ escc_add_channel(buf, "b", addr, 0);
+
+ escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET +
+ (CONFIG_SERIAL_PORT ? 0 : 0x20);
+}
+#endif
diff --git a/qemu/roms/openbios/drivers/escc.h b/qemu/roms/openbios/drivers/escc.h
new file mode 100644
index 000000000..caaf00d40
--- /dev/null
+++ b/qemu/roms/openbios/drivers/escc.h
@@ -0,0 +1,9 @@
+
+#define IO_ESCC_SIZE 0x00001000
+#define IO_ESCC_OFFSET 0x00013000
+
+#define ZS_REGS 8
+
+void escc_init(const char *path, phys_addr_t addr);
+void ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave,
+ int keyboard);
diff --git a/qemu/roms/openbios/drivers/esp.c b/qemu/roms/openbios/drivers/esp.c
new file mode 100644
index 000000000..ad3db280d
--- /dev/null
+++ b/qemu/roms/openbios/drivers/esp.c
@@ -0,0 +1,598 @@
+/*
+ * OpenBIOS ESP driver
+ *
+ * Copyright (C) 2004 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2005 Stefan Reinauer <stepan@openbios.org>
+ *
+ * Credit goes to Hale Landis for his excellent ata demo software
+ * OF node handling and some fixes by Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+#include "asm/io.h"
+#include "scsi.h"
+#include "asm/dma.h"
+#include "esp.h"
+#include "libopenbios/ofmem.h"
+
+#define BUFSIZE 4096
+
+#ifdef CONFIG_DEBUG_ESP
+#define DPRINTF(fmt, args...) \
+ do { printk(fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+struct esp_dma {
+ volatile struct sparc_dma_registers *regs;
+ enum dvma_rev revision;
+};
+
+typedef struct sd_private {
+ unsigned int bs;
+ const char *media_str[2];
+ uint32_t sectors;
+ uint8_t media;
+ uint8_t id;
+ uint8_t present;
+ char model[40];
+} sd_private_t;
+
+struct esp_regs {
+ unsigned char regs[ESP_REG_SIZE];
+};
+
+typedef struct esp_private {
+ volatile struct esp_regs *ll;
+ uint32_t buffer_dvma;
+ unsigned int irq; /* device IRQ number */
+ struct esp_dma espdma;
+ unsigned char *buffer;
+ sd_private_t sd[8];
+} esp_private_t;
+
+static esp_private_t *global_esp;
+
+/* DECLARE data structures for the nodes. */
+DECLARE_UNNAMED_NODE(ob_sd, INSTALL_OPEN, sizeof(sd_private_t *));
+DECLARE_UNNAMED_NODE(ob_esp, INSTALL_OPEN, sizeof(esp_private_t *));
+
+#ifdef CONFIG_DEBUG_ESP
+static void dump_drive(sd_private_t *drive)
+{
+ printk("SCSI DRIVE @%lx:\n", (unsigned long)drive);
+ printk("id: %d\n", drive->id);
+ printk("media: %s\n", drive->media_str[0]);
+ printk("media: %s\n", drive->media_str[1]);
+ printk("model: %s\n", drive->model);
+ printk("sectors: %d\n", drive->sectors);
+ printk("present: %d\n", drive->present);
+ printk("bs: %d\n", drive->bs);
+}
+#endif
+
+static int
+do_command(esp_private_t *esp, sd_private_t *sd, int cmdlen, int replylen)
+{
+ int status;
+
+ // Set SCSI target
+ esp->ll->regs[ESP_BUSID] = sd->id & 7;
+ // Set DMA address
+ esp->espdma.regs->st_addr = esp->buffer_dvma;
+ // Set DMA length
+ esp->ll->regs[ESP_TCLOW] = cmdlen & 0xff;
+ esp->ll->regs[ESP_TCMED] = (cmdlen >> 8) & 0xff;
+ // Set DMA direction and enable DMA
+ esp->espdma.regs->cond_reg = DMA_ENABLE;
+ // Set ATN, issue command
+ esp->ll->regs[ESP_CMD] = ESP_CMD_SELA | ESP_CMD_DMA;
+ // Wait for DMA to complete. Can this fail?
+ while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */;
+ // Check status
+ status = esp->ll->regs[ESP_STATUS];
+ // Clear interrupts to avoid guests seeing spurious interrupts
+ (void)esp->ll->regs[ESP_INTRPT];
+
+ DPRINTF("do_command: id %d, cmd[0] 0x%x, status 0x%x\n", sd->id, esp->buffer[1], status);
+
+ /* Target didn't want all command data? */
+ if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT) {
+ return status;
+ }
+ if (replylen == 0) {
+ return 0;
+ }
+ /* Target went to status phase instead of data phase? */
+ if ((status & ESP_STAT_PMASK) == ESP_STATP) {
+ return status;
+ }
+
+ // Get reply
+ // Set DMA address
+ esp->espdma.regs->st_addr = esp->buffer_dvma;
+ // Set DMA length
+ esp->ll->regs[ESP_TCLOW] = replylen & 0xff;
+ esp->ll->regs[ESP_TCMED] = (replylen >> 8) & 0xff;
+ // Set DMA direction
+ esp->espdma.regs->cond_reg = DMA_ST_WRITE | DMA_ENABLE;
+ // Transfer
+ esp->ll->regs[ESP_CMD] = ESP_CMD_TI | ESP_CMD_DMA;
+ // Wait for DMA to complete
+ while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */;
+ // Check status
+ status = esp->ll->regs[ESP_STATUS];
+ // Clear interrupts to avoid guests seeing spurious interrupts
+ (void)esp->ll->regs[ESP_INTRPT];
+
+ DPRINTF("do_command_reply: status 0x%x\n", status);
+
+ if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT)
+ return status;
+ else
+ return 0; // OK
+}
+
+// offset is in sectors
+static int
+ob_sd_read_sector(esp_private_t *esp, sd_private_t *sd, int offset)
+{
+ DPRINTF("ob_sd_read_sector id %d sector=%d\n",
+ sd->id, offset);
+
+ // Setup command = Read(10)
+ memset(esp->buffer, 0, 11);
+ esp->buffer[0] = 0x80;
+ esp->buffer[1] = READ_10;
+
+ esp->buffer[3] = (offset >> 24) & 0xff;
+ esp->buffer[4] = (offset >> 16) & 0xff;
+ esp->buffer[5] = (offset >> 8) & 0xff;
+ esp->buffer[6] = offset & 0xff;
+
+ esp->buffer[8] = 0;
+ esp->buffer[9] = 1;
+
+ if (do_command(esp, sd, 11, sd->bs))
+ return 0;
+
+ return 0;
+}
+
+static unsigned int
+read_capacity(esp_private_t *esp, sd_private_t *sd)
+{
+ // Setup command = Read Capacity
+ memset(esp->buffer, 0, 11);
+ esp->buffer[0] = 0x80;
+ esp->buffer[1] = READ_CAPACITY;
+
+ if (do_command(esp, sd, 11, 8)) {
+ sd->sectors = 0;
+ sd->bs = 0;
+ DPRINTF("read_capacity id %d failed\n", sd->id);
+ return 0;
+ }
+ sd->bs = (esp->buffer[4] << 24) | (esp->buffer[5] << 16) | (esp->buffer[6] << 8) | esp->buffer[7];
+ sd->sectors = ((esp->buffer[0] << 24) | (esp->buffer[1] << 16) | (esp->buffer[2] << 8) | esp->buffer[3]) * (sd->bs / 512);
+
+ DPRINTF("read_capacity id %d bs %d sectors %d\n", sd->id, sd->bs,
+ sd->sectors);
+ return 1;
+}
+
+static unsigned int
+test_unit_ready(esp_private_t *esp, sd_private_t *sd)
+{
+ /* Setup command = Test Unit Ready */
+ memset(esp->buffer, 0, 7);
+ esp->buffer[0] = 0x80;
+ esp->buffer[1] = TEST_UNIT_READY;
+
+ if (do_command(esp, sd, 7, 0)) {
+ DPRINTF("test_unit_ready id %d failed\n", sd->id);
+ return 0;
+ }
+
+ DPRINTF("test_unit_ready id %d success\n", sd->id);
+ return 1;
+}
+
+static unsigned int
+inquiry(esp_private_t *esp, sd_private_t *sd)
+{
+ const char *media[2] = { "UNKNOWN", "UNKNOWN"};
+
+ // Setup command = Inquiry
+ memset(esp->buffer, 0, 7);
+ esp->buffer[0] = 0x80;
+ esp->buffer[1] = INQUIRY;
+
+ esp->buffer[5] = 36;
+
+ if (do_command(esp, sd, 7, 36)) {
+ sd->present = 0;
+ sd->media = -1;
+ return 0;
+ }
+ sd->present = 1;
+ sd->media = esp->buffer[0];
+
+ switch (sd->media) {
+ case TYPE_DISK:
+ media[0] = "disk";
+ media[1] = "hd";
+ break;
+ case TYPE_ROM:
+ media[0] = "cdrom";
+ media[1] = "cd";
+ break;
+ }
+ sd->media_str[0] = media[0];
+ sd->media_str[1] = media[1];
+ memcpy(sd->model, &esp->buffer[16], 16);
+ sd->model[17] = '\0';
+
+ return 1;
+}
+
+
+static void
+ob_sd_read_blocks(sd_private_t **sd)
+{
+ cell n = POP(), cnt = n;
+ ucell blk = POP();
+ char *dest = (char*)POP();
+ int pos, spb, sect_offset;
+
+ DPRINTF("ob_sd_read_blocks id %d %lx block=%d n=%d\n", (*sd)->id, (unsigned long)dest, blk, n );
+
+ if ((*sd)->bs == 0) {
+ PUSH(0);
+ return;
+ }
+ spb = (*sd)->bs / 512;
+ while (n) {
+ sect_offset = blk / spb;
+ pos = (blk - sect_offset * spb) * 512;
+
+ if (ob_sd_read_sector(global_esp, *sd, sect_offset)) {
+ DPRINTF("ob_sd_read_blocks: error\n");
+ RET(0);
+ }
+ while (n && pos < spb * 512) {
+ memcpy(dest, global_esp->buffer + pos, 512);
+ pos += 512;
+ dest += 512;
+ n--;
+ blk++;
+ }
+ }
+ PUSH(cnt);
+}
+
+static void
+ob_sd_block_size(__attribute__((unused))sd_private_t **sd)
+{
+ PUSH(512);
+}
+
+static void
+ob_sd_open(__attribute__((unused))sd_private_t **sd)
+{
+ int ret = 1, id;
+ phandle_t ph;
+
+ fword("my-unit");
+ id = POP();
+ POP(); // unit id is 2 ints but we only need one.
+ *sd = &global_esp->sd[id];
+
+#ifdef CONFIG_DEBUG_ESP
+ {
+ char *args;
+
+ fword("my-args");
+ args = pop_fstr_copy();
+ DPRINTF("opening drive %d args %s\n", id, args);
+ free(args);
+ }
+#endif
+
+ selfword("open-deblocker");
+
+ /* interpose disk-label */
+ ph = find_dev("/packages/disk-label");
+ fword("my-args");
+ PUSH_ph( ph );
+ fword("interpose");
+
+ RET ( -ret );
+}
+
+static void
+ob_sd_close(__attribute__((unused)) sd_private_t **sd)
+{
+ selfword("close-deblocker");
+}
+
+NODE_METHODS(ob_sd) = {
+ { "open", ob_sd_open },
+ { "close", ob_sd_close },
+ { "read-blocks", ob_sd_read_blocks },
+ { "block-size", ob_sd_block_size },
+};
+
+
+static int
+espdma_init(unsigned int slot, uint64_t base, unsigned long offset,
+ struct esp_dma *espdma)
+{
+ espdma->regs = (void *)ofmem_map_io(base + (uint64_t)offset, 0x10);
+
+ if (espdma->regs == NULL) {
+ DPRINTF("espdma_init: cannot map registers\n");
+ return -1;
+ }
+
+ DPRINTF("dma1: ");
+
+ switch ((espdma->regs->cond_reg) & DMA_DEVICE_ID) {
+ case DMA_VERS0:
+ espdma->revision = dvmarev0;
+ DPRINTF("Revision 0 ");
+ break;
+ case DMA_ESCV1:
+ espdma->revision = dvmaesc1;
+ DPRINTF("ESC Revision 1 ");
+ break;
+ case DMA_VERS1:
+ espdma->revision = dvmarev1;
+ DPRINTF("Revision 1 ");
+ break;
+ case DMA_VERS2:
+ espdma->revision = dvmarev2;
+ DPRINTF("Revision 2 ");
+ break;
+ case DMA_VERHME:
+ espdma->revision = dvmahme;
+ DPRINTF("HME DVMA gate array ");
+ break;
+ case DMA_VERSPLUS:
+ espdma->revision = dvmarevplus;
+ DPRINTF("Revision 1 PLUS ");
+ break;
+ default:
+ DPRINTF("unknown dma version %x",
+ (espdma->regs->cond_reg) & DMA_DEVICE_ID);
+ /* espdma->allocated = 1; */
+ break;
+ }
+ DPRINTF("\n");
+
+ push_str("/iommu/sbus/espdma");
+ fword("find-device");
+
+ /* set reg */
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(offset);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(0x00000010);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ return 0;
+}
+
+static void
+ob_esp_initialize(__attribute__((unused)) esp_private_t **esp)
+{
+ phandle_t ph = get_cur_dev();
+
+ set_int_property(ph, "#address-cells", 2);
+ set_int_property(ph, "#size-cells", 0);
+
+ /* set device type */
+ push_str("scsi");
+ fword("device-type");
+
+ /* QEMU's ESP emulation does not support mixing DMA and FIFO messages. By
+ setting this attribute, we prevent the Solaris ESP kernel driver from
+ trying to use this feature when booting a disk image (and failing) */
+ PUSH(0x58);
+ fword("encode-int");
+ push_str("scsi-options");
+ fword("property");
+
+ PUSH(0x24);
+ fword("encode-int");
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ push_str("intr");
+ fword("property");
+}
+
+static void
+ob_esp_decodeunit(__attribute__((unused)) esp_private_t **esp)
+{
+ fword("decode-unit-scsi");
+}
+
+
+static void
+ob_esp_encodeunit(__attribute__((unused)) esp_private_t **esp)
+{
+ fword("encode-unit-scsi");
+}
+
+NODE_METHODS(ob_esp) = {
+ { NULL, ob_esp_initialize },
+ { "decode-unit", ob_esp_decodeunit },
+ { "encode-unit", ob_esp_encodeunit },
+};
+
+static void
+add_alias(const char *device, const char *alias)
+{
+ DPRINTF("add_alias dev \"%s\" = alias \"%s\"\n", device, alias);
+ push_str("/aliases");
+ fword("find-device");
+ push_str(device);
+ fword("encode-string");
+ push_str(alias);
+ fword("property");
+}
+
+int
+ob_esp_init(unsigned int slot, uint64_t base, unsigned long espoffset,
+ unsigned long dmaoffset)
+{
+ int id, diskcount = 0, cdcount = 0, *counter_ptr;
+ char nodebuff[256], aliasbuff[256];
+ esp_private_t *esp;
+ unsigned int i;
+
+ DPRINTF("Initializing SCSI...");
+
+ esp = malloc(sizeof(esp_private_t));
+ if (!esp) {
+ DPRINTF("Can't allocate ESP private structure\n");
+ return -1;
+ }
+
+ global_esp = esp;
+
+ if (espdma_init(slot, base, dmaoffset, &esp->espdma) != 0) {
+ return -1;
+ }
+ /* Get the IO region */
+ esp->ll = (void *)ofmem_map_io(base + (uint64_t)espoffset,
+ sizeof(struct esp_regs));
+ if (esp->ll == NULL) {
+ DPRINTF("Can't map ESP registers\n");
+ return -1;
+ }
+
+ esp->buffer = (void *)dvma_alloc(BUFSIZE, &esp->buffer_dvma);
+ if (!esp->buffer || !esp->buffer_dvma) {
+ DPRINTF("Can't get a DVMA buffer\n");
+ return -1;
+ }
+
+ // Chip reset
+ esp->ll->regs[ESP_CMD] = ESP_CMD_RC;
+
+ DPRINTF("ESP at 0x%lx, buffer va 0x%lx dva 0x%lx\n", (unsigned long)esp,
+ (unsigned long)esp->buffer, (unsigned long)esp->buffer_dvma);
+ DPRINTF("done\n");
+ DPRINTF("Initializing SCSI devices...");
+
+ for (id = 0; id < 8; id++) {
+ esp->sd[id].id = id;
+ if (!inquiry(esp, &esp->sd[id])) {
+ DPRINTF("Unit %d not present\n", id);
+ continue;
+ }
+ /* Clear Unit Attention condition from reset */
+ for (i = 0; i < 5; i++) {
+ if (test_unit_ready(esp, &esp->sd[id])) {
+ break;
+ }
+ }
+ if (i == 5) {
+ DPRINTF("Unit %d present but won't become ready\n", id);
+ continue;
+ }
+ DPRINTF("Unit %d present\n", id);
+ read_capacity(esp, &esp->sd[id]);
+
+#ifdef CONFIG_DEBUG_ESP
+ dump_drive(&esp->sd[id]);
+#endif
+ }
+
+ REGISTER_NAMED_NODE(ob_esp, "/iommu/sbus/espdma/esp");
+ device_end();
+ /* set reg */
+ push_str("/iommu/sbus/espdma/esp");
+ fword("find-device");
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(espoffset);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(0x00000010);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ PUSH(0x02625a00);
+ fword("encode-int");
+ push_str("clock-frequency");
+ fword("property");
+
+ for (id = 0; id < 8; id++) {
+ if (!esp->sd[id].present)
+ continue;
+ push_str("/iommu/sbus/espdma/esp");
+ fword("find-device");
+ fword("new-device");
+ push_str("sd");
+ fword("device-name");
+ push_str("block");
+ fword("device-type");
+ fword("is-deblocker");
+ PUSH(id);
+ fword("encode-int");
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+ fword("finish-device");
+ snprintf(nodebuff, sizeof(nodebuff), "/iommu/sbus/espdma/esp/sd@%d,0",
+ id);
+ REGISTER_NODE_METHODS(ob_sd, nodebuff);
+ if (esp->sd[id].media == TYPE_ROM) {
+ counter_ptr = &cdcount;
+ } else {
+ counter_ptr = &diskcount;
+ }
+ if (*counter_ptr == 0) {
+ add_alias(nodebuff, esp->sd[id].media_str[0]);
+ add_alias(nodebuff, esp->sd[id].media_str[1]);
+ }
+ snprintf(aliasbuff, sizeof(aliasbuff), "%s%d",
+ esp->sd[id].media_str[0], *counter_ptr);
+ add_alias(nodebuff, aliasbuff);
+ snprintf(aliasbuff, sizeof(aliasbuff), "%s%d",
+ esp->sd[id].media_str[1], *counter_ptr);
+ add_alias(nodebuff, aliasbuff);
+ snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)", id);
+ add_alias(nodebuff, aliasbuff);
+ snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)@0,0", id);
+ add_alias(nodebuff, aliasbuff);
+ (*counter_ptr)++;
+ }
+ DPRINTF("done\n");
+
+ return 0;
+}
diff --git a/qemu/roms/openbios/drivers/esp.fs b/qemu/roms/openbios/drivers/esp.fs
new file mode 100644
index 000000000..9e37c0a0e
--- /dev/null
+++ b/qemu/roms/openbios/drivers/esp.fs
@@ -0,0 +1,18 @@
+\ -------------------------------------------------------------------------
+\ SCSI encode/decode unit
+\ -------------------------------------------------------------------------
+
+: decode-unit-scsi ( str len -- id lun )
+ ascii , left-split
+ ( addr-R len-R addr-L len-L )
+ parse-hex
+ -rot parse-hex
+ swap
+;
+
+: encode-unit-scsi ( id lun -- str len)
+ swap
+ pocket tohexstr
+ " ," pocket tmpstrcat >r
+ rot pocket tohexstr r> tmpstrcat drop
+;
diff --git a/qemu/roms/openbios/drivers/esp.h b/qemu/roms/openbios/drivers/esp.h
new file mode 100644
index 000000000..2b9bd5e01
--- /dev/null
+++ b/qemu/roms/openbios/drivers/esp.h
@@ -0,0 +1,269 @@
+/* $Id: esp.h,v 1.28 2000/03/30 01:33:17 davem Exp $
+ * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI
+ * Processor) driver under Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef _SPARC_ESP_H
+#define _SPARC_ESP_H
+
+/* For dvma controller register definitions. */
+#include "asm/dma.h"
+
+/* The ESP SCSI controllers have their register sets in three
+ * "classes":
+ *
+ * 1) Registers which are both read and write.
+ * 2) Registers which are read only.
+ * 3) Registers which are write only.
+ *
+ * Yet, they all live within the same IO space.
+ */
+
+/* All the ESP registers are one byte each and are accessed longwords
+ * apart with a big-endian ordering to the bytes.
+ */
+ /* Access Description Offset */
+#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */
+#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */
+#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */
+#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */
+#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */
+#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */
+#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */
+#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */
+#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */
+#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */
+#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */
+#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */
+#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */
+#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */
+#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */
+#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */
+#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */
+#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */
+#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */
+#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
+#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
+#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */
+#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */
+#define ESP_REG_SIZE 0x40UL
+
+/* Various revisions of the ESP board. */
+enum esp_rev {
+ esp100 = 0x00, /* NCR53C90 - very broken */
+ esp100a = 0x01, /* NCR53C90A */
+ esp236 = 0x02,
+ fas236 = 0x03,
+ fas100a = 0x04,
+ fast = 0x05,
+ fashme = 0x06,
+ espunknown = 0x07
+};
+
+/* Bitfield meanings for the above registers. */
+
+/* ESP config reg 1, read-write, found on all ESP chips */
+#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */
+#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */
+#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */
+#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */
+#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */
+#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */
+
+/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */
+#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */
+#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */
+#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */
+#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */
+#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */
+#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */
+#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */
+#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */
+#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */
+#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */
+#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */
+#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */
+#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */
+
+/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */
+#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */
+#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */
+#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */
+#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */
+#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */
+#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */
+#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */
+#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */
+#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */
+#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */
+#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */
+#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */
+#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */
+#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */
+#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
+#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
+
+/* ESP command register read-write */
+/* Group 1 commands: These may be sent at any point in time to the ESP
+ * chip. None of them can generate interrupts 'cept
+ * the "SCSI bus reset" command if you have not disabled
+ * SCSI reset interrupts in the config1 ESP register.
+ */
+#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */
+#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */
+#define ESP_CMD_RC 0x02 /* Chip reset */
+#define ESP_CMD_RS 0x03 /* SCSI bus reset */
+
+/* Group 2 commands: ESP must be an initiator and connected to a target
+ * for these commands to work.
+ */
+#define ESP_CMD_TI 0x10 /* Transfer Information */
+#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */
+#define ESP_CMD_MOK 0x12 /* Message okie-dokie */
+#define ESP_CMD_TPAD 0x18 /* Transfer Pad */
+#define ESP_CMD_SATN 0x1a /* Set ATN */
+#define ESP_CMD_RATN 0x1b /* De-assert ATN */
+
+/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected
+ * to a target as the initiator for these commands to work.
+ */
+#define ESP_CMD_SMSG 0x20 /* Send message */
+#define ESP_CMD_SSTAT 0x21 /* Send status */
+#define ESP_CMD_SDATA 0x22 /* Send data */
+#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */
+#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */
+#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */
+#define ESP_CMD_DCNCT 0x27 /* Disconnect */
+#define ESP_CMD_RMSG 0x28 /* Receive Message */
+#define ESP_CMD_RCMD 0x29 /* Receive Command */
+#define ESP_CMD_RDATA 0x2a /* Receive Data */
+#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */
+
+/* Group 4 commands: The ESP must be in the disconnected state and must
+ * not be connected to any targets as initiator for
+ * these commands to work.
+ */
+#define ESP_CMD_RSEL 0x40 /* Reselect */
+#define ESP_CMD_SEL 0x41 /* Select w/o ATN */
+#define ESP_CMD_SELA 0x42 /* Select w/ATN */
+#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */
+#define ESP_CMD_ESEL 0x44 /* Enable selection */
+#define ESP_CMD_DSEL 0x45 /* Disable selections */
+#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */
+#define ESP_CMD_RSEL3 0x47 /* Reselect3 */
+
+/* This bit enables the ESP's DMA on the SBus */
+#define ESP_CMD_DMA 0x80 /* Do DMA? */
+
+
+/* ESP status register read-only */
+#define ESP_STAT_PIO 0x01 /* IO phase bit */
+#define ESP_STAT_PCD 0x02 /* CD phase bit */
+#define ESP_STAT_PMSG 0x04 /* MSG phase bit */
+#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */
+#define ESP_STAT_TDONE 0x08 /* Transfer Completed */
+#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */
+#define ESP_STAT_PERR 0x20 /* Parity error */
+#define ESP_STAT_SPAM 0x40 /* Real bad error */
+/* This indicates the 'interrupt pending' condition on esp236, it is a reserved
+ * bit on other revs of the ESP.
+ */
+#define ESP_STAT_INTR 0x80 /* Interrupt */
+
+/* HME only: status 2 register */
+#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */
+#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */
+#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */
+#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */
+#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */
+#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */
+#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */
+#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */
+
+/* The status register can be masked with ESP_STAT_PMASK and compared
+ * with the following values to determine the current phase the ESP
+ * (at least thinks it) is in. For our purposes we also add our own
+ * software 'done' bit for our phase management engine.
+ */
+#define ESP_DOP (0) /* Data Out */
+#define ESP_DIP (ESP_STAT_PIO) /* Data In */
+#define ESP_CMDP (ESP_STAT_PCD) /* Command */
+#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */
+#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */
+#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */
+
+/* ESP interrupt register read-only */
+#define ESP_INTR_S 0x01 /* Select w/o ATN */
+#define ESP_INTR_SATN 0x02 /* Select w/ATN */
+#define ESP_INTR_RSEL 0x04 /* Reselected */
+#define ESP_INTR_FDONE 0x08 /* Function done */
+#define ESP_INTR_BSERV 0x10 /* Bus service */
+#define ESP_INTR_DC 0x20 /* Disconnect */
+#define ESP_INTR_IC 0x40 /* Illegal command given */
+#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */
+
+/* Interrupt status macros */
+#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR))
+#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC))
+#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN))
+#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S))
+#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \
+ (ESP_SELECT_WITHOUT_ATN_IRQ(esp)))
+#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL))
+
+/* ESP sequence step register read-only */
+#define ESP_STEP_VBITS 0x07 /* Valid bits */
+#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */
+#define ESP_STEP_SID 0x01 /* One msg byte sent */
+#define ESP_STEP_NCMD 0x02 /* Was not in command phase */
+#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd
+ * bytes to be lost
+ */
+#define ESP_STEP_FINI4 0x04 /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5 0x05
+#define ESP_STEP_FINI6 0x06
+#define ESP_STEP_FINI7 0x07
+
+/* ESP chip-test register read-write */
+#define ESP_TEST_TARG 0x01 /* Target test mode */
+#define ESP_TEST_INI 0x02 /* Initiator test mode */
+#define ESP_TEST_TS 0x04 /* Tristate test mode */
+
+/* ESP unique ID register read-only, found on fas236+fas100a only */
+#define ESP_UID_F100A 0x00 /* ESP FAS100A */
+#define ESP_UID_F236 0x02 /* ESP FAS236 */
+#define ESP_UID_REV 0x07 /* ESP revision */
+#define ESP_UID_FAM 0xf8 /* ESP family */
+
+/* ESP fifo flags register read-only */
+/* Note that the following implies a 16 byte FIFO on the ESP. */
+#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */
+#define ESP_FF_SSTEP 0xe0 /* Sequence step */
+
+/* ESP clock conversion factor register write-only */
+#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */
+#define ESP_CCF_NEVER 0x01 /* Set it to this and die */
+#define ESP_CCF_F2 0x02 /* 10MHz */
+#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */
+#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */
+#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */
+#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */
+#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */
+
+/* HME only... */
+#define ESP_BUSID_RESELID 0x10
+#define ESP_BUSID_CTR32BIT 0x40
+
+#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */
+#define ESP_TIMEO_CONST 8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+ ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000))
+
+#endif /* !(_SPARC_ESP_H) */
diff --git a/qemu/roms/openbios/drivers/floppy.c b/qemu/roms/openbios/drivers/floppy.c
new file mode 100644
index 000000000..329987b17
--- /dev/null
+++ b/qemu/roms/openbios/drivers/floppy.c
@@ -0,0 +1,1179 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+
+#include "timer.h"
+
+/* DECLARE data structures for the nodes. */
+DECLARE_UNNAMED_NODE( ob_floppy, INSTALL_OPEN, 2*sizeof(int) );
+
+#ifdef CONFIG_DEBUG_FLOPPY
+#define printk_info printk
+#define printk_debug printk
+#else
+#define printk_info(x ...)
+#define printk_debug(x ...)
+#endif
+#define printk_err printk
+
+#define FD_DRIVE 0
+
+
+#define FD_STATUS_A (0) /* Status register A */
+#define FD_STATUS_B (1) /* Status register B */
+#define FD_DOR (2) /* Digital Output Register */
+#define FD_TDR (3) /* Tape Drive Register */
+#define FD_STATUS (4) /* Main Status Register */
+#define FD_DSR (4) /* Data Rate Select Register (old) */
+#define FD_DATA (5) /* Data Transfer (FIFO) register */
+#define FD_DIR (7) /* Digital Input Register (read) */
+#define FD_DCR (7) /* Diskette Control Register (write)*/
+
+/* Bit of FD_STATUS_A */
+#define STA_INT_PENDING 0x80 /* Interrupt Pending */
+
+/* DOR */
+#define DOR_DRIVE0 0x00
+#define DOR_DRIVE1 0x01
+#define DOR_DRIVE2 0x02
+#define DOR_DRIVE3 0x03
+#define DOR_DRIVE_MASK 0x03
+#define DOR_NO_RESET 0x04
+#define DOR_DMA_EN 0x08
+#define DOR_MOT_EN0 0x10
+#define DOR_MOT_EN1 0x20
+#define DOR_MOT_EN2 0x40
+#define DOR_MOT_EN3 0x80
+
+/* Bits of main status register */
+#define STATUS_BUSYMASK 0x0F /* drive busy mask */
+#define STATUS_BUSY 0x10 /* FDC busy */
+#define STATUS_NON_DMA 0x20 /* 0- DMA mode */
+#define STATUS_DIR 0x40 /* 0- cpu->fdc */
+#define STATUS_READY 0x80 /* Data reg ready */
+
+/* Bits of FD_ST0 */
+#define ST0_DS 0x03 /* drive select mask */
+#define ST0_HA 0x04 /* Head (Address) */
+#define ST0_NR 0x08 /* Not Ready */
+#define ST0_ECE 0x10 /* Equipment check error */
+#define ST0_SE 0x20 /* Seek end */
+#define ST0_INTR 0xC0 /* Interrupt code mask */
+#define ST0_INTR_OK (0 << 6)
+#define ST0_INTR_ERROR (1 << 6)
+#define ST0_INTR_INVALID (2 << 6)
+#define ST0_INTR_POLL_ERROR (3 << 6)
+
+/* Bits of FD_ST1 */
+#define ST1_MAM 0x01 /* Missing Address Mark */
+#define ST1_WP 0x02 /* Write Protect */
+#define ST1_ND 0x04 /* No Data - unreadable */
+#define ST1_OR 0x10 /* OverRun */
+#define ST1_CRC 0x20 /* CRC error in data or addr */
+#define ST1_EOC 0x80 /* End Of Cylinder */
+
+/* Bits of FD_ST2 */
+#define ST2_MAM 0x01 /* Missing Address Mark (again) */
+#define ST2_BC 0x02 /* Bad Cylinder */
+#define ST2_SNS 0x04 /* Scan Not Satisfied */
+#define ST2_SEH 0x08 /* Scan Equal Hit */
+#define ST2_WC 0x10 /* Wrong Cylinder */
+#define ST2_CRC 0x20 /* CRC error in data field */
+#define ST2_CM 0x40 /* Control Mark = deleted */
+
+/* Bits of FD_ST3 */
+#define ST3_HA 0x04 /* Head (Address) */
+#define ST3_DS 0x08 /* drive is double-sided */
+#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */
+#define ST3_RY 0x20 /* drive is ready */
+#define ST3_WP 0x40 /* Write Protect */
+#define ST3_FT 0x80 /* Drive Fault */
+
+/* Values for FD_COMMAND */
+#define FD_RECALIBRATE 0x07 /* move to track 0 */
+#define FD_SEEK 0x0F /* seek track */
+#define FD_READ 0xA6 /* read with MT, SKip deleted */
+#define FD_WRITE 0xC5 /* write with MT, MFM */
+#define FD_SENSEI 0x08 /* Sense Interrupt Status */
+#define FD_SPECIFY 0x03 /* specify HUT etc */
+#define FD_FORMAT 0x4D /* format one track */
+#define FD_VERSION 0x10 /* get version code */
+#define FD_CONFIGURE 0x13 /* configure FIFO operation */
+#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */
+#define FD_GETSTATUS 0x04 /* read ST3 */
+#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */
+#define FD_READID 0xEA /* prints the header of a sector */
+#define FD_UNLOCK 0x14 /* Fifo config unlock */
+#define FD_LOCK 0x94 /* Fifo config lock */
+#define FD_RSEEK_OUT 0x8f /* seek out (i.e. to lower tracks) */
+#define FD_RSEEK_IN 0xcf /* seek in (i.e. to higher tracks) */
+
+
+/* the following commands are new in the 82078. They are not used in the
+ * floppy driver, except the first three. These commands may be useful for apps
+ * which use the FDRAWCMD interface. For doc, get the 82078 spec sheets at
+ * http://www-techdoc.intel.com/docs/periph/fd_contr/datasheets/ */
+
+#define FD_PARTID 0x18 /* part id ("extended" version cmd) */
+#define FD_SAVE 0x2e /* save fdc regs for later restore */
+#define FD_DRIVESPEC 0x8e /* drive specification: Access to the
+ * 2 Mbps data transfer rate for tape
+ * drives */
+
+#define FD_RESTORE 0x4e /* later restore */
+#define FD_POWERDOWN 0x27 /* configure FDC's powersave features */
+#define FD_FORMAT_N_WRITE 0xef /* format and write in one go. */
+#define FD_OPTION 0x33 /* ISO format (which is a clean way to
+ * pack more sectors on a track) */
+
+/* FDC version return types */
+#define FDC_NONE 0x00
+#define FDC_UNKNOWN 0x10 /* DO NOT USE THIS TYPE EXCEPT IF IDENTIFICATION
+ FAILS EARLY */
+#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */
+#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */
+#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */
+#define FDC_82072A 0x45 /* 82072A (on Sparcs) */
+#define FDC_82077_ORIG 0x51 /* Original version of 82077AA, sans LOCK */
+#define FDC_82077 0x52 /* 82077AA-1 */
+#define FDC_82078_UNKN 0x5f /* Unknown 82078 variant */
+#define FDC_82078 0x60 /* 44pin 82078 or 64pin 82078SL */
+#define FDC_82078_1 0x61 /* 82078-1 (2Mbps fdc) */
+#define FDC_S82078B 0x62 /* S82078B (first seen on Adaptec AVA-2825 VLB
+ * SCSI/EIDE/Floppy controller) */
+#define FDC_87306 0x63 /* National Semiconductor PC 87306 */
+
+/*
+ * Beware: the fdc type list is roughly sorted by increasing features.
+ * Presence of features is tested by comparing the FDC version id with the
+ * "oldest" version that has the needed feature.
+ * If during FDC detection, an obscure test fails late in the sequence, don't
+ * assign FDC_UNKNOWN. Else the FDC will be treated as a dumb 8272a, or worse.
+ * This is especially true if the tests are unneeded.
+ */
+
+/* Parameters for a 1.44 3.5" disk */
+#define DISK_H1440_SIZE 2880
+#define DISK_H1440_SECT 18
+#define DISK_H1440_HEAD 2
+#define DISK_H1440_TRACK 80
+#define DISK_H1440_STRETCH 0
+#define DISK_H1440_GAP 0x1B
+#define DISK_H1440_RATE 0x00
+#define DISK_H1440_SPEC1 0xCF
+#define DISK_H1440_FMT_GAP 0x6C
+
+/* Parameters for a 1.44 3.5" drive */
+#define DRIVE_H1440_MAX_DTR 500
+#define DRIVE_H1440_HLT 16 /* ms */
+#define DRIVE_H1440_HUT 16 /* ms */
+#define DRIVE_H1440_SRT 4000 /* us */
+#define DRIVE_H1440_SPINUP 400 /* ms */
+#define DRIVE_H1440_SPINDOWN 3000 /* ms */
+#define DRIVE_H1440_SPINDOWN_OFFSET 10
+#define DRIVE_H1440_SELECT_DELAY 20 /* ms */
+#define DRIVE_H1440_RPS 5
+#define DRIVE_H1440_TRACKS 83
+#define DRIVE_H1440_TIMEOUT 3000 /* ms */
+#define DRIVE_H1440_INTERLEAVE_SECT 20
+
+/* Floppy drive configuration */
+#define FIFO_DEPTH 10
+#define USE_IMPLIED_SEEK 0
+#define USE_FIFO 1
+#define FIFO_THRESHOLD 10
+#define TRACK_PRECOMPENSATION 0
+
+#define SLOW_FLOPPY 0
+
+#define FD_RESET_DELAY 20 /* microseconds */
+
+/*
+ * FDC state
+ */
+static struct drive_state {
+ unsigned track;
+} drive_state[1];
+
+static struct floppy_fdc_state {
+ int in_sync;
+ int spec1; /* spec1 value last used */
+ int spec2; /* spec2 value last used */
+ int dtr;
+ unsigned char dor;
+ unsigned char version; /* FDC version code */
+ void (*fdc_outb)(unsigned char data, unsigned long port);
+ unsigned char (*fdc_inb)(unsigned long port);
+ unsigned long io_base;
+ unsigned long mmio_base;
+} fdc_state;
+
+/* Synchronization of FDC access. */
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 16
+
+static void show_floppy(void);
+static void floppy_reset(void);
+
+/*
+ * IO port operations
+ */
+static unsigned char
+ob_fdc_inb(unsigned long port)
+{
+ return inb(fdc_state.io_base + port);
+}
+
+static void
+ob_fdc_outb(unsigned char data, unsigned long port)
+{
+ outb(data, fdc_state.io_base + port);
+}
+
+/*
+ * MMIO operations
+ */
+static unsigned char
+ob_fdc_mmio_readb(unsigned long port)
+{
+ return *(unsigned char *)(fdc_state.mmio_base + port);
+}
+
+static void
+ob_fdc_mmio_writeb(unsigned char data, unsigned long port)
+{
+ *(unsigned char *)(fdc_state.mmio_base + port) = data;
+}
+
+static int set_dor(char mask, char data)
+{
+ unsigned char newdor,olddor;
+
+ olddor = fdc_state.dor;
+ newdor = (olddor & mask) | data;
+ if (newdor != olddor){
+ fdc_state.dor = newdor;
+ fdc_state.fdc_outb(newdor, FD_DOR);
+ }
+ return olddor;
+}
+
+/* waits until the fdc becomes ready */
+static int wait_til_ready(void)
+{
+ int counter, status;
+ for (counter = 0; counter < 10000; counter++) {
+ status = fdc_state.fdc_inb(FD_STATUS);
+ if (status & STATUS_READY) {
+ return status;
+ }
+ }
+ printk_debug("Getstatus times out (%x)\n", status);
+ show_floppy();
+ return -3;
+}
+
+
+/* sends a command byte to the fdc */
+static int output_byte(unsigned char byte)
+{
+ int status;
+
+ if ((status = wait_til_ready()) < 0)
+ return status;
+ if ((status & (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) == STATUS_READY){
+ fdc_state.fdc_outb(byte,FD_DATA);
+ return 0;
+ }
+ printk_debug("Unable to send byte %x to FDC_STATE. Status=%x\n",
+ byte, status);
+
+ show_floppy();
+ return -2;
+}
+
+/* gets the response from the fdc */
+static int result(unsigned char *reply_buffer, int max_replies)
+{
+ int i, status=0;
+
+ for(i=0; i < max_replies; i++) {
+ if ((status = wait_til_ready()) < 0)
+ break;
+ status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_NON_DMA;
+ if ((status & ~STATUS_BUSY) == STATUS_READY){
+ return i;
+ }
+ if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
+ reply_buffer[i] = fdc_state.fdc_inb(FD_DATA);
+ else
+ break;
+ }
+ if (i == max_replies)
+ return i;
+ printk_debug("get result error. Last status=%x Read bytes=%d\n",
+ status, i);
+ show_floppy();
+ return -1;
+}
+#define MORE_OUTPUT -2
+/* does the fdc need more output? */
+static int need_more_output(void)
+{
+ unsigned char reply_buffer[MAX_REPLIES];
+ int status;
+ if ((status = wait_til_ready()) < 0)
+ return -1;
+ if ((status & (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) == STATUS_READY)
+ return MORE_OUTPUT;
+ return result(reply_buffer, MAX_REPLIES);
+}
+
+static int output_command(unsigned char *cmd, int count)
+{
+ int i, status;
+ for(i = 0; i < count; i++) {
+ if ((status = output_byte(cmd[i])) < 0) {
+ printk_err("full command not acceppted, status =%x\n",
+ status);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int output_new_command(unsigned char *cmd, int count)
+{
+ int i, status;
+ if ((status = output_byte(cmd[0])) < 0)
+ return -1;
+ if (need_more_output() != MORE_OUTPUT)
+ return -1;
+ for(i = 1; i < count; i++) {
+ if ((status = output_byte(cmd[i])) < 0) {
+ printk_err("full new command not acceppted, status =%d\n",
+ status);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* Collect pending interrupt status */
+static unsigned char collect_interrupt(void)
+{
+ unsigned char pcn = 0xff;
+ unsigned char reply_buffer[MAX_REPLIES];
+ int nr;
+#ifdef CONFIG_DEBUG_FLOPPY
+ int i, status;
+#endif
+ nr = result(reply_buffer, MAX_REPLIES);
+ if (nr != 0) {
+ printk_debug("SENSEI\n");
+ }
+ else {
+ int max_sensei = 4;
+ do {
+ if (output_byte(FD_SENSEI) < 0)
+ break;
+ nr = result(reply_buffer, MAX_REPLIES);
+ if (nr == 2) {
+ pcn = reply_buffer[1];
+ printk_debug("SENSEI %02x %02x\n",
+ reply_buffer[0], reply_buffer[1]);
+ }
+ max_sensei--;
+ }while(((reply_buffer[0] & 0x83) != FD_DRIVE) && (nr == 2) && max_sensei);
+#ifdef CONFIG_DEBUG_FLOPPY
+ status = fdc_state.fdc_inb(FD_STATUS);
+ printk_debug("status = %x, reply_buffer=", status);
+ for(i = 0; i < nr; i++) {
+ printk_debug(" %x",
+ reply_buffer[i]);
+ }
+ printk_debug("\n");
+#else
+ fdc_state.fdc_inb(FD_STATUS);
+#endif
+ }
+
+ return pcn;
+}
+
+
+/* selects the fdc and drive, and enables the fdc's input/dma, and it's motor. */
+static void set_drive(int drive)
+{
+ int fdc = (drive >> 2) & 1;
+ int status;
+ unsigned new_dor;
+ if (drive > 3) {
+ printk_err("bad drive value\n");
+ return;
+ }
+ if (fdc != 0) {
+ printk_err("bad fdc value\n");
+ return;
+ }
+ drive &= 3;
+#if 0
+ new_dor = 8; /* Enable the controller */
+#else
+ new_dor = 0; /* Don't enable DMA on the controller */
+#endif
+ new_dor |= (1 << (drive + 4)); /* Spinup the selected drive */
+ new_dor |= drive; /* Select the drive for commands as well */
+ set_dor(0xc, new_dor);
+
+ mdelay(DRIVE_H1440_SPINUP);
+
+ status = fdc_state.fdc_inb(FD_STATUS);
+ printk_debug("set_drive status = %02x, new_dor = %02x\n",
+ status, new_dor);
+ if (status != STATUS_READY) {
+ printk_err("set_drive bad status\n");
+ }
+}
+
+
+/* Disable the motor for a given floppy drive */
+static void floppy_motor_off(int drive)
+{
+ unsigned mask;
+ printk_debug("floppy_motor_off\n");
+ /* fix the number of drives */
+ drive &= 3;
+ /* Clear the bit for the drive we care about */
+ mask = 0xff;
+ mask &= ~(1 << (drive +4));
+ /* Now clear the bit in the Digital Output Register */
+ set_dor(mask, 0);
+}
+
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static void fdc_dtr(unsigned rate)
+{
+ rate &= 3;
+ /* If data rate not already set to desired value, set it. */
+ if (fdc_state.in_sync && (rate == fdc_state.dtr))
+ return;
+
+ /* Set dtr */
+ fdc_state.fdc_outb(rate, FD_DCR);
+
+ /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+ * need a stabilization period of several milliseconds to be
+ * enforced after data rate changes before R/W operations.
+ * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+ */
+ fdc_state.dtr = rate & 3;
+ mdelay(5);
+} /* fdc_dtr */
+
+static int fdc_configure(int use_implied_seek, int use_fifo,
+ unsigned fifo_threshold, unsigned precompensation)
+{
+ unsigned config_bits;
+ unsigned char cmd[4];
+ /* 0 EIS EFIFO POLL FIFOOTHR[4] */
+
+ /* santize parameters */
+ config_bits = fifo_threshold & 0xf;
+ config_bits |= (1 << 4); /* Always disable background floppy poll */
+ config_bits |= (!use_fifo) << 5;
+ config_bits |= (!!use_implied_seek) << 6;
+
+ precompensation &= 0xff; /* pre-compensation from track 0 upwards */
+
+ cmd[0] = FD_CONFIGURE;
+ cmd[1] = 0;
+ cmd[2] = config_bits;
+ cmd[3] = precompensation;
+
+ /* Turn on FIFO */
+ if (output_new_command(cmd, 4) < 0)
+ return 0;
+ return 1;
+}
+
+#define NOMINAL_DTR 500
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps. It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types. This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
+ */
+static void fdc_specify(
+ unsigned head_load_time, unsigned head_unload_time, unsigned step_rate)
+{
+ unsigned char cmd[3];
+ unsigned long srt, hlt, hut;
+ unsigned long dtr = NOMINAL_DTR;
+ unsigned long scale_dtr = NOMINAL_DTR;
+ int hlt_max_code = 0x7f;
+ int hut_max_code = 0xf;
+
+ printk_debug("fdc_specify\n");
+
+ switch (DISK_H1440_RATE & 0x03) {
+ case 3:
+ dtr = 1000;
+ break;
+ case 1:
+ dtr = 300;
+ if (fdc_state.version >= FDC_82078) {
+ /* chose the default rate table, not the one
+ * where 1 = 2 Mbps */
+ cmd[0] = FD_DRIVESPEC;
+ cmd[1] = FD_DRIVE & 3;
+ cmd[2] = 0xc0;
+ output_new_command(cmd,3);
+ /* FIXME how do I handle errors here? */
+ }
+ break;
+ case 2:
+ dtr = 250;
+ break;
+ }
+
+
+ if (fdc_state.version >= FDC_82072) {
+ scale_dtr = dtr;
+ hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
+ hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
+ }
+
+ /* Convert step rate from microseconds to milliseconds and 4 bits */
+ srt = 16 - (step_rate*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ if (SLOW_FLOPPY) {
+ srt = srt / 4;
+ }
+ if (srt > 0xf) {
+ srt = 0xf;
+ }
+
+ hlt = (head_load_time*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ if (hlt < 0x01)
+ hlt = 0x01;
+ else if (hlt > 0x7f)
+ hlt = hlt_max_code;
+
+ hut = (head_unload_time*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ if (hut < 0x1)
+ hut = 0x1;
+ else if (hut > 0xf)
+ hut = hut_max_code;
+
+ cmd[0] = FD_SPECIFY;
+ cmd[1] = (srt << 4) | hut;
+ cmd[2] = (hlt << 1) | 1; /* Always disable DMA */
+
+ /* If these parameters did not change, just return with success */
+ if (!fdc_state.in_sync || fdc_state.spec1 != cmd[1] || fdc_state.spec2 != cmd[2]) {
+ /* Go ahead and set spec1 and spec2 */
+ output_command(cmd, 3);
+ /* FIXME how do I handle errors here... */
+ printk_info("FD_SPECIFY(%02x, %02x)\n", cmd[1], cmd[2]);
+ }
+} /* fdc_specify */
+
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
+ * or by setting the self clearing bit 7 of STATUS (newer FDCs)
+ */
+static void reset_fdc(void)
+{
+ unsigned char reply[MAX_REPLIES];
+
+ fdc_state.in_sync = 0;
+
+ /* Pseudo-DMA may intercept 'reset finished' interrupt. */
+ /* Irrelevant for systems with true DMA (i386). */
+
+ if (fdc_state.version >= FDC_82072A)
+ fdc_state.fdc_outb(0x80 | (fdc_state.dtr &3), FD_DSR);
+ else {
+ fdc_state.fdc_outb(fdc_state.dor & ~DOR_NO_RESET, FD_DOR);
+ udelay(FD_RESET_DELAY);
+ fdc_state.fdc_outb(fdc_state.dor, FD_DOR);
+ }
+ result(reply, MAX_REPLIES);
+}
+
+
+
+static void show_floppy(void)
+{
+
+ printk_debug("\n");
+ printk_debug("floppy driver state\n");
+ printk_debug("-------------------\n");
+
+ printk_debug("fdc_bytes: %02x %02x xx %02x %02x %02x xx %02x\n",
+ fdc_state.fdc_inb(FD_STATUS_A),
+ fdc_state.fdc_inb(FD_STATUS_B),
+ fdc_state.fdc_inb(FD_TDR),
+ fdc_state.fdc_inb(FD_STATUS),
+ fdc_state.fdc_inb(FD_DATA),
+ fdc_state.fdc_inb(FD_DIR));
+
+ printk_debug("status=%x\n", fdc_state.fdc_inb(FD_STATUS));
+ printk_debug("\n");
+}
+
+static void floppy_recalibrate(void)
+{
+ unsigned char cmd[2];
+ unsigned char reply[MAX_REPLIES];
+ int nr, success;
+ success = 0;
+ do {
+ printk_debug("floppy_recalibrate\n");
+ /* Send the recalibrate command to the controller.
+ * We don't have interrupts or anything we can poll
+ * so we have to guess when it is done.
+ */
+ cmd[0] = FD_RECALIBRATE;
+ cmd[1] = 0;
+ if (output_command(cmd, 2) < 0)
+ continue;
+
+ /* Sleep for the maximum time the recalibrate command
+ * can run.
+ */
+ mdelay(80*DRIVE_H1440_SRT/1000);
+
+ /* Now call FD_SENSEI to end the command
+ * and collect up the reply.
+ */
+ if (output_byte(FD_SENSEI) < 0)
+ continue;
+ nr = result(reply, MAX_REPLIES);
+
+ /* Now see if we have succeeded in our seek */
+ success =
+ /* We have the right size result */
+ (nr == 2) &&
+ /* The command didn't terminate in error */
+ ((reply[0] & ST0_INTR) == ST0_INTR_OK) &&
+ /* We finished a seek */
+ (reply[0] & ST0_SE) &&
+ /* We are at cylinder 0 */
+ (reply[1] == 0);
+ } while(!success);
+ /* Remember we are at track 0 */
+ drive_state[FD_DRIVE].track = 0;
+}
+
+
+static int floppy_seek(unsigned track)
+{
+ unsigned char cmd[3];
+ unsigned char reply[MAX_REPLIES];
+ int nr, success;
+ unsigned distance, old_track;
+
+ /* Look up the old track and see if we need to
+ * do anything.
+ */
+ old_track = drive_state[FD_DRIVE].track;
+ if (old_track == track) {
+ return 1;
+ }
+
+ /* Compute the distance we are about to move,
+ * We need to know this so we know how long to sleep...
+ */
+ distance = (old_track > track)?(old_track - track):(track - old_track);
+ distance += 1;
+
+
+ /* Send the seek command to the controller.
+ * We don't have interrupts or anything we can poll
+ * so we have to guess when it is done.
+ */
+ cmd[0] = FD_SEEK;
+ cmd[1] = FD_DRIVE;
+ cmd[2] = track;
+ if (output_command(cmd, 3) < 0)
+ return 0;
+
+ /* Sleep for the time it takes to step throuhg distance tracks.
+ */
+ mdelay(distance*DRIVE_H1440_SRT/1000);
+
+ /* Now call FD_SENSEI to end the command
+ * and collect up the reply.
+ */
+ cmd[0] = FD_SENSEI;
+ if (output_command(cmd, 1) < 0)
+ return 0;
+ nr = result(reply, MAX_REPLIES);
+
+ /* Now see if we have succeeded in our seek */
+ success =
+ /* We have the right size result */
+ (nr == 2) &&
+ /* The command didn't terminate in error */
+ ((reply[0] & ST0_INTR) == ST0_INTR_OK) &&
+ /* We finished a seek */
+ (reply[0] & ST0_SE) &&
+ /* We are at cylinder 0 */
+ (reply[1] == track);
+ if (success)
+ drive_state[FD_DRIVE].track = track;
+ else {
+ printk_debug("seek failed\n");
+ printk_debug("nr = %d\n", nr);
+ printk_debug("ST0 = %02x\n", reply[0]);
+ printk_debug("PCN = %02x\n", reply[1]);
+ printk_debug("status = %d\n", fdc_state.fdc_inb(FD_STATUS));
+ }
+ return success;
+}
+
+static int read_ok(unsigned head)
+{
+ unsigned char results[7];
+ int result_ok;
+ int nr;
+
+ /* read back the read results */
+ nr = result(results, 7);
+
+ /* Now see if they say we are o.k. */
+ result_ok = 0;
+ /* Are my result bytes o.k.? */
+ if (nr == 7) {
+ /* Are we o.k. */
+ if ((results[0] & ST0_INTR) == ST0_INTR_OK) {
+ result_ok = 1;
+ }
+ /* Or did we get just an overflow error */
+ else if (((results[0] & ST0_INTR) == ST0_INTR_ERROR) &&
+ (results[1]== ST1_OR) &&
+ (results[2] == 0)) {
+ result_ok = 1;
+ }
+ /* Verify the reply had the correct head */
+ if (((results[0] & ST0_HA) >> 2) != head) {
+ result_ok = 0;
+ }
+ /* Verify the reply had the correct drive */
+ if (((results[0] & ST0_DS) != FD_DRIVE)) {
+ result_ok = 0;
+ }
+ }
+ if (!result_ok) {
+ printk_debug("result_bytes = %d\n", nr);
+ printk_debug("ST0 = %02x\n", results[0]);
+ printk_debug("ST1 = %02x\n", results[1]);
+ printk_debug("ST2 = %02x\n", results[2]);
+ printk_debug(" C = %02x\n", results[3]);
+ printk_debug(" H = %02x\n", results[4]);
+ printk_debug(" R = %02x\n", results[5]);
+ printk_debug(" N = %02x\n", results[6]);
+ }
+ return result_ok;
+}
+
+static int floppy_read_sectors(
+ char *dest, unsigned byte_offset, unsigned length,
+ unsigned sector, unsigned head, unsigned track)
+{
+ /* MT == Multitrack */
+ /* MFM == MFM or FM Mode */
+ /* SK == Skip deleted data addres Mark */
+ /* HDS == Head number select */
+ /* DS0 == Disk Drive Select 0 */
+ /* DS1 == Disk Drive Select 1 */
+ /* C == Cylinder number 0 - 255 */
+ /* H == Head number */
+ /* R == Record */
+ /* N == The number of data bytes written in a sector */
+ /* EOT == End of Track */
+ /* GPL == Gap Length */
+ /* DTL == Data Length */
+ /* MT MFM SK 0 1 1 0 0 */
+ /* 0 0 0 0 0 HDS DS1 DS0 */
+ /* C, H, R, N, EOT, GPL, DTL */
+
+ int i, status, result_ok;
+ int max_bytes, bytes_read;
+ int ret;
+ unsigned char cmd[9];
+ unsigned end_offset;
+
+ end_offset = byte_offset + length;
+ max_bytes = 512*(DISK_H1440_SECT - sector + 1);
+
+ if (byte_offset >= max_bytes) {
+ return 0;
+ }
+ cmd[0] = FD_READ | (((DISK_H1440_HEAD ==2)?1:0) << 6);
+ cmd[1] = (head << 2) | FD_DRIVE;
+ cmd[2] = track;
+ cmd[3] = head;
+ cmd[4] = sector;
+ cmd[5] = 2; /* 2^N *128 == Sector size. Hard coded to 512 bytes */
+ cmd[6] = DISK_H1440_SECT;
+ cmd[7] = DISK_H1440_GAP;
+ cmd[8] = 0xff;
+
+ /* Output the command bytes */
+ if (output_command(cmd, 9) < 0)
+ return -1;
+
+ /* The execution stage begins when STATUS_READY&STATUS_NON_DMA is set */
+ do {
+ status = fdc_state.fdc_inb(FD_STATUS);
+ status &= STATUS_READY | STATUS_NON_DMA;
+ } while(status != (STATUS_READY|STATUS_NON_DMA));
+
+ for(i = 0; i < max_bytes; i++) {
+ unsigned char byte;
+ if ((status = wait_til_ready()) < 0) {
+ break;
+ }
+ status &= STATUS_READY|STATUS_DIR|STATUS_NON_DMA;
+ if (status != (STATUS_READY|STATUS_DIR|STATUS_NON_DMA)) {
+ break;
+ }
+ byte = fdc_state.fdc_inb(FD_DATA);
+ if ((i >= byte_offset) && (i < end_offset)) {
+ dest[i - byte_offset] = byte;
+ }
+ }
+ bytes_read = i;
+
+ /* The result stage begins when STATUS_NON_DMA is cleared */
+ while((status = fdc_state.fdc_inb(FD_STATUS)) & STATUS_NON_DMA) {
+ /* We get extra bytes in the fifo past
+ * the end of the sector and drop them on the floor.
+ * Otherwise the fifo is polluted.
+ */
+ fdc_state.fdc_inb(FD_DATA);
+ }
+ /* Did I get an error? */
+ result_ok = read_ok(head);
+ /* Did I read enough bytes? */
+ ret = -1;
+ if (result_ok && (bytes_read == max_bytes)) {
+ ret = bytes_read - byte_offset;
+ if (ret > length) {
+ ret = length;
+ }
+ }
+
+ if (ret < 0) {
+ printk_debug("ret = %d\n", ret);
+ printk_debug("bytes_read = %d\n", bytes_read);
+ printk_debug("status = %x\n", status);
+ }
+ return ret;
+}
+
+
+static int __floppy_read(char *dest, unsigned long offset, unsigned long length)
+{
+ unsigned head, track, sector, byte_offset, sector_offset;
+ int ret;
+
+ /* break the offset up into sectors and bytes */
+ byte_offset = offset % 512;
+ sector_offset = offset / 512;
+
+ /* Find the disk block we are starting with... */
+ sector = (sector_offset % DISK_H1440_SECT) + 1;
+ head = (sector_offset / DISK_H1440_SECT) % DISK_H1440_HEAD;
+ track = (sector_offset / (DISK_H1440_SECT *DISK_H1440_HEAD))% DISK_H1440_TRACK;
+
+ /* First seek to our start track */
+ if (!floppy_seek(track)) {
+ return -1;
+ }
+ /* Then read the data */
+ ret = floppy_read_sectors(dest, byte_offset, length, sector, head, track);
+ if (ret >= 0) {
+ return ret;
+ }
+ /* If we failed reset the fdc... */
+ floppy_reset();
+ return -1;
+}
+
+static int floppy_read(char *dest, unsigned long offset, unsigned long length)
+{
+ int fr_result, bytes_read;;
+
+ printk_debug("floppy_read\n");
+ bytes_read = 0;
+ do {
+ int max_errors = 3;
+ do {
+ fr_result = __floppy_read(dest + bytes_read, offset,
+ length - bytes_read);
+ if (max_errors-- == 0) {
+ return (bytes_read)?bytes_read: -1;
+ }
+ } while (fr_result <= 0);
+ offset += fr_result;
+ bytes_read += fr_result;
+ } while(bytes_read < length);
+ return bytes_read;
+}
+
+/* Determine the floppy disk controller type */
+/* This routine was written by David C. Niemi */
+static char get_fdc_version(void)
+{
+ int bytes, ret;
+ unsigned char reply_buffer[MAX_REPLIES];
+
+ ret = output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
+ if (ret < 0)
+ return FDC_NONE;
+ if ((bytes = result(reply_buffer, MAX_REPLIES)) <= 0x00)
+ return FDC_NONE; /* No FDC present ??? */
+ if ((bytes==1) && (reply_buffer[0] == 0x80)){
+ printk_info("FDC is an 8272A\n");
+ return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
+ }
+ if (bytes != 10) {
+ printk_debug("init: DUMPREGS: unexpected return of %d bytes.\n",
+ bytes);
+ return FDC_UNKNOWN;
+ }
+ if (!fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD,
+ TRACK_PRECOMPENSATION)) {
+ printk_info("FDC is an 82072\n");
+ return FDC_82072; /* 82072 doesn't know CONFIGURE */
+ }
+
+ output_byte(FD_PERPENDICULAR);
+ if (need_more_output() == MORE_OUTPUT) {
+ output_byte(0);
+ } else {
+ printk_info("FDC is an 82072A\n");
+ return FDC_82072A; /* 82072A as found on Sparcs. */
+ }
+
+ output_byte(FD_UNLOCK);
+ bytes = result(reply_buffer, MAX_REPLIES);
+ if ((bytes == 1) && (reply_buffer[0] == 0x80)){
+ printk_info("FDC is a pre-1991 82077\n");
+ return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
+ * LOCK/UNLOCK */
+ }
+ if ((bytes != 1) || (reply_buffer[0] != 0x00)) {
+ printk_debug("FDC init: UNLOCK: unexpected return of %d bytes.\n",
+ bytes);
+ return FDC_UNKNOWN;
+ }
+ output_byte(FD_PARTID);
+ bytes = result(reply_buffer, MAX_REPLIES);
+ if (bytes != 1) {
+ printk_debug("FDC init: PARTID: unexpected return of %d bytes.\n",
+ bytes);
+ return FDC_UNKNOWN;
+ }
+ if (reply_buffer[0] == 0x80) {
+ printk_info("FDC is a post-1991 82077\n");
+ return FDC_82077; /* Revised 82077AA passes all the tests */
+ }
+ switch (reply_buffer[0] >> 5) {
+ case 0x0:
+ /* Either a 82078-1 or a 82078SL running at 5Volt */
+ printk_info("FDC is an 82078.\n");
+ return FDC_82078;
+ case 0x1:
+ printk_info("FDC is a 44pin 82078\n");
+ return FDC_82078;
+ case 0x2:
+ printk_info("FDC is a S82078B\n");
+ return FDC_S82078B;
+ case 0x3:
+ printk_info("FDC is a National Semiconductor PC87306\n");
+ return FDC_87306;
+ default:
+ printk_info("FDC init: 82078 variant with unknown PARTID=%d.\n",
+ reply_buffer[0] >> 5);
+ return FDC_82078_UNKN;
+ }
+} /* get_fdc_version */
+
+
+static int floppy_init(unsigned long io_base, unsigned long mmio_base)
+{
+ printk_debug("floppy_init\n");
+ fdc_state.in_sync = 0;
+ fdc_state.spec1 = -1;
+ fdc_state.spec2 = -1;
+ fdc_state.dtr = -1;
+ fdc_state.dor = DOR_NO_RESET;
+ fdc_state.version = FDC_UNKNOWN;
+ if (mmio_base) {
+ fdc_state.fdc_inb = ob_fdc_mmio_readb;
+ fdc_state.fdc_outb = ob_fdc_mmio_writeb;
+ } else {
+ fdc_state.fdc_inb = ob_fdc_inb;
+ fdc_state.fdc_outb = ob_fdc_outb;
+ }
+ fdc_state.io_base = io_base;
+ fdc_state.mmio_base = mmio_base;
+ reset_fdc();
+ /* Try to determine the floppy controller type */
+ fdc_state.version = get_fdc_version();
+ if (fdc_state.version == FDC_NONE) {
+ return -1;
+ }
+ floppy_reset();
+ printk_info("fdc_state.version = %04x\n", fdc_state.version);
+ return 0;
+}
+
+static void floppy_reset(void)
+{
+ printk_debug("floppy_reset\n");
+ floppy_motor_off(FD_DRIVE);
+ reset_fdc();
+ fdc_dtr(DISK_H1440_RATE);
+ /* program data rate via ccr */
+ collect_interrupt();
+ fdc_configure(USE_IMPLIED_SEEK, USE_FIFO, FIFO_THRESHOLD,
+ TRACK_PRECOMPENSATION);
+ fdc_specify(DRIVE_H1440_HLT, DRIVE_H1440_HUT, DRIVE_H1440_SRT);
+ set_drive(FD_DRIVE);
+ floppy_recalibrate();
+ fdc_state.in_sync = 1;
+}
+
+static void
+ob_floppy_initialize(const char *path)
+{
+ int props[3];
+ phandle_t ph = find_dev(path);
+
+ set_property(ph, "device_type", "block", sizeof("block"));
+
+ // Set dummy reg properties
+ props[0] = __cpu_to_be32(0); props[1] = __cpu_to_be32(0); props[2] = __cpu_to_be32(0);
+ set_property(ph, "reg", (char *)&props, 3*sizeof(int));
+
+ fword("is-deblocker");
+}
+
+
+static void
+ob_floppy_open(int *idx)
+{
+ int ret = 1;
+ phandle_t ph;
+
+ fword("my-unit");
+ idx[0]=POP();
+
+ fword("my-parent");
+ fword("ihandle>phandle");
+ ph=(phandle_t)POP();
+
+ selfword("open-deblocker");
+
+ /* interpose disk-label */
+ ph = find_dev("/packages/disk-label");
+ fword("my-args");
+ PUSH_ph( ph );
+ fword("interpose");
+
+ RET ( -ret );
+}
+
+static void
+ob_floppy_close(int *idx)
+{
+ selfword("close-deblocker");
+}
+
+static void
+ob_floppy_read_blocks(int *idx)
+{
+ cell cnt = POP();
+ ucell blk = POP();
+ char *dest = (char*)POP();
+ floppy_read(dest, blk*512, cnt*512);
+ PUSH(cnt);
+}
+
+
+static void
+ob_floppy_block_size(int *idx)
+{
+ PUSH(512);
+}
+
+static void
+ob_floppy_max_transfer(int *idx)
+{
+ // Fixme
+ PUSH(18 * 512);
+}
+
+NODE_METHODS(ob_floppy) = {
+ { "open", ob_floppy_open },
+ { "close", ob_floppy_close },
+ { "read-blocks", ob_floppy_read_blocks },
+ { "block-size", ob_floppy_block_size },
+ { "max-transfer", ob_floppy_max_transfer },
+};
+
+
+int ob_floppy_init(const char *path, const char *dev_name,
+ unsigned long io_base, unsigned long mmio_base)
+{
+ char nodebuff[128];
+ phandle_t aliases;
+
+ snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name);
+ if (!mmio_base) {
+ REGISTER_NAMED_NODE(ob_floppy, nodebuff);
+ ob_floppy_initialize(nodebuff);
+ } else {
+ // Already in tree and mapped
+ REGISTER_NODE_METHODS(ob_floppy, nodebuff);
+ }
+ floppy_init(io_base, mmio_base);
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "floppy", nodebuff, strlen(nodebuff) + 1);
+
+ return 0;
+}
diff --git a/qemu/roms/openbios/drivers/floppy.h b/qemu/roms/openbios/drivers/floppy.h
new file mode 100644
index 000000000..b0f30d555
--- /dev/null
+++ b/qemu/roms/openbios/drivers/floppy.h
@@ -0,0 +1,9 @@
+#ifndef FLOPPY_SUBR_H
+#define FLOPPY_SUBR_H
+
+int floppy_init(void);
+int floppy_read(char *dest, unsigned long offset, unsigned long length);
+void floppy_fini(void);
+
+
+#endif /* FLOPPY_SUBR_H */
diff --git a/qemu/roms/openbios/drivers/fw_cfg.c b/qemu/roms/openbios/drivers/fw_cfg.c
new file mode 100644
index 000000000..402757070
--- /dev/null
+++ b/qemu/roms/openbios/drivers/fw_cfg.c
@@ -0,0 +1,76 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libopenbios/ofmem.h"
+#define NO_QEMU_PROTOS
+#include "arch/common/fw_cfg.h"
+
+#if !defined(CONFIG_SPARC64)
+static volatile uint16_t *fw_cfg_cmd;
+static volatile uint8_t *fw_cfg_data;
+
+void
+fw_cfg_read(uint16_t cmd, char *buf, unsigned int nbytes)
+{
+ unsigned int i;
+
+ *fw_cfg_cmd = cmd;
+ for (i = 0; i < nbytes; i++)
+ buf[i] = *fw_cfg_data;
+}
+#else
+// XXX depends on PCI bus location, should be removed
+void
+fw_cfg_read(uint16_t cmd, char *buf, unsigned int nbytes)
+{
+ unsigned int i;
+
+ outw(cmd, CONFIG_FW_CFG_ADDR);
+ for (i = 0; i < nbytes; i++)
+ buf[i] = inb(CONFIG_FW_CFG_ADDR + 1);
+}
+#endif
+
+uint64_t
+fw_cfg_read_i64(uint16_t cmd)
+{
+ uint64_t buf;
+
+ fw_cfg_read(cmd, (char *)&buf, sizeof(uint64_t));
+
+ return __le64_to_cpu(buf);
+}
+
+uint32_t
+fw_cfg_read_i32(uint16_t cmd)
+{
+ uint32_t buf;
+
+ fw_cfg_read(cmd, (char *)&buf, sizeof(uint32_t));
+
+ return __le32_to_cpu(buf);
+}
+
+uint16_t
+fw_cfg_read_i16(uint16_t cmd)
+{
+ uint16_t buf;
+
+ fw_cfg_read(cmd, (char *)&buf, sizeof(uint16_t));
+
+ return __le16_to_cpu(buf);
+}
+
+void
+fw_cfg_init(void)
+{
+#if defined(CONFIG_SPARC32)
+ fw_cfg_cmd = (void *)ofmem_map_io(CONFIG_FW_CFG_ADDR, 2);
+ fw_cfg_data = (uint8_t *)fw_cfg_cmd + 2;
+#elif defined(CONFIG_SPARC64)
+ // Nothing for the port version
+#elif defined(CONFIG_PPC)
+ fw_cfg_cmd = (void *)CONFIG_FW_CFG_ADDR;
+ fw_cfg_data = (void *)(CONFIG_FW_CFG_ADDR + 2);
+#endif
+}
diff --git a/qemu/roms/openbios/drivers/hdreg.h b/qemu/roms/openbios/drivers/hdreg.h
new file mode 100644
index 000000000..91e4d1ff6
--- /dev/null
+++ b/qemu/roms/openbios/drivers/hdreg.h
@@ -0,0 +1,289 @@
+/*
+ * this header holds data structures as dictated by spec
+ */
+#ifndef HDREG_H
+#define HDREG_H
+
+struct hd_driveid {
+ unsigned short config; /* lots of obsolete bit flags */
+ unsigned short cyls; /* Obsolete, "physical" cyls */
+ unsigned short reserved2; /* reserved (word 2) */
+ unsigned short heads; /* Obsolete, "physical" heads */
+ unsigned short track_bytes; /* unformatted bytes per track */
+ unsigned short sector_bytes; /* unformatted bytes per sector */
+ unsigned short sectors; /* Obsolete, "physical" sectors per track */
+ unsigned short vendor0; /* vendor unique */
+ unsigned short vendor1; /* vendor unique */
+ unsigned short vendor2; /* Retired vendor unique */
+ unsigned char serial_no[20]; /* 0 = not_specified */
+ unsigned short buf_type; /* Retired */
+ unsigned short buf_size; /* Retired, 512 byte increments
+ * 0 = not_specified
+ */
+ unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */
+ unsigned char fw_rev[8]; /* 0 = not_specified */
+ unsigned char model[40]; /* 0 = not_specified */
+ unsigned char max_multsect; /* 0=not_implemented */
+ unsigned char vendor3; /* vendor unique */
+ unsigned short dword_io; /* 0=not_implemented; 1=implemented */
+ unsigned char vendor4; /* vendor unique */
+ unsigned char capability; /* (upper byte of word 49)
+ * 3: IORDYsup
+ * 2: IORDYsw
+ * 1: LBA
+ * 0: DMA
+ */
+ unsigned short reserved50; /* reserved (word 50) */
+ unsigned char vendor5; /* Obsolete, vendor unique */
+ unsigned char tPIO; /* Obsolete, 0=slow, 1=medium, 2=fast */
+ unsigned char vendor6; /* Obsolete, vendor unique */
+ unsigned char tDMA; /* Obsolete, 0=slow, 1=medium, 2=fast */
+ unsigned short field_valid; /* (word 53)
+ * 2: ultra_ok word 88
+ * 1: eide_ok words 64-70
+ * 0: cur_ok words 54-58
+ */
+ unsigned short cur_cyls; /* Obsolete, logical cylinders */
+ unsigned short cur_heads; /* Obsolete, l heads */
+ unsigned short cur_sectors; /* Obsolete, l sectors per track */
+ unsigned short cur_capacity0; /* Obsolete, l total sectors on drive */
+ unsigned short cur_capacity1; /* Obsolete, (2 words, misaligned int) */
+ unsigned char multsect; /* current multiple sector count */
+ unsigned char multsect_valid; /* when (bit0==1) multsect is ok */
+ unsigned int lba_capacity; /* Obsolete, total number of sectors */
+ unsigned short dma_1word; /* Obsolete, single-word dma info */
+ unsigned short dma_mword; /* multiple-word dma info */
+ unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */
+ unsigned short eide_dma_min; /* min mword dma cycle time (ns) */
+ unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */
+ unsigned short eide_pio; /* min cycle time (ns), no IORDY */
+ unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */
+ unsigned short words69_70[2]; /* reserved words 69-70
+ * future command overlap and queuing
+ */
+ /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */
+ unsigned short words71_74[4]; /* reserved words 71-74
+ * for IDENTIFY PACKET DEVICE command
+ */
+ unsigned short queue_depth; /* (word 75)
+ * 15:5 reserved
+ * 4:0 Maximum queue depth -1
+ */
+ unsigned short words76_79[4]; /* reserved words 76-79 */
+ unsigned short major_rev_num; /* (word 80) */
+ unsigned short minor_rev_num; /* (word 81) */
+ unsigned short command_set_1; /* (word 82) supported
+ * 15: Obsolete
+ * 14: NOP command
+ * 13: READ_BUFFER
+ * 12: WRITE_BUFFER
+ * 11: Obsolete
+ * 10: Host Protected Area
+ * 9: DEVICE Reset
+ * 8: SERVICE Interrupt
+ * 7: Release Interrupt
+ * 6: look-ahead
+ * 5: write cache
+ * 4: PACKET Command
+ * 3: Power Management Feature Set
+ * 2: Removable Feature Set
+ * 1: Security Feature Set
+ * 0: SMART Feature Set
+ */
+ unsigned short command_set_2; /* (word 83)
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13: FLUSH CACHE EXT
+ * 12: FLUSH CACHE
+ * 11: Device Configuration Overlay
+ * 10: 48-bit Address Feature Set
+ * 9: Automatic Acoustic Management
+ * 8: SET MAX security
+ * 7: reserved 1407DT PARTIES
+ * 6: SetF sub-command Power-Up
+ * 5: Power-Up in Standby Feature Set
+ * 4: Removable Media Notification
+ * 3: APM Feature Set
+ * 2: CFA Feature Set
+ * 1: READ/WRITE DMA QUEUED
+ * 0: Download MicroCode
+ */
+ unsigned short cfsse; /* (word 84)
+ * cmd set-feature supported extensions
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:6 reserved
+ * 5: General Purpose Logging
+ * 4: Streaming Feature Set
+ * 3: Media Card Pass Through
+ * 2: Media Serial Number Valid
+ * 1: SMART selt-test supported
+ * 0: SMART error logging
+ */
+ unsigned short cfs_enable_1; /* (word 85)
+ * command set-feature enabled
+ * 15: Obsolete
+ * 14: NOP command
+ * 13: READ_BUFFER
+ * 12: WRITE_BUFFER
+ * 11: Obsolete
+ * 10: Host Protected Area
+ * 9: DEVICE Reset
+ * 8: SERVICE Interrupt
+ * 7: Release Interrupt
+ * 6: look-ahead
+ * 5: write cache
+ * 4: PACKET Command
+ * 3: Power Management Feature Set
+ * 2: Removable Feature Set
+ * 1: Security Feature Set
+ * 0: SMART Feature Set
+ */
+ unsigned short cfs_enable_2; /* (word 86)
+ * command set-feature enabled
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13: FLUSH CACHE EXT
+ * 12: FLUSH CACHE
+ * 11: Device Configuration Overlay
+ * 10: 48-bit Address Feature Set
+ * 9: Automatic Acoustic Management
+ * 8: SET MAX security
+ * 7: reserved 1407DT PARTIES
+ * 6: SetF sub-command Power-Up
+ * 5: Power-Up in Standby Feature Set
+ * 4: Removable Media Notification
+ * 3: APM Feature Set
+ * 2: CFA Feature Set
+ * 1: READ/WRITE DMA QUEUED
+ * 0: Download MicroCode
+ */
+ unsigned short csf_default; /* (word 87)
+ * command set-feature default
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:6 reserved
+ * 5: General Purpose Logging enabled
+ * 4: Valid CONFIGURE STREAM executed
+ * 3: Media Card Pass Through enabled
+ * 2: Media Serial Number Valid
+ * 1: SMART selt-test supported
+ * 0: SMART error logging
+ */
+ unsigned short dma_ultra; /* (word 88) */
+ unsigned short trseuc; /* time required for security erase */
+ unsigned short trsEuc; /* time required for enhanced erase */
+ unsigned short CurAPMvalues; /* current APM values */
+ unsigned short mprc; /* master password revision code */
+ unsigned short hw_config; /* hardware config (word 93)
+ * 15: Shall be ZERO
+ * 14: Shall be ONE
+ * 13:
+ * 12:
+ * 11:
+ * 10:
+ * 9:
+ * 8:
+ * 7:
+ * 6:
+ * 5:
+ * 4:
+ * 3:
+ * 2:
+ * 1:
+ * 0: Shall be ONE
+ */
+ unsigned short acoustic; /* (word 94)
+ * 15:8 Vendor's recommended value
+ * 7:0 current value
+ */
+ unsigned short msrqs; /* min stream request size */
+ unsigned short sxfert; /* stream transfer time */
+ unsigned short sal; /* stream access latency */
+ unsigned int spg; /* stream performance granularity */
+ unsigned long long lba_capacity_2;/* 48-bit total number of sectors */
+ unsigned short words104_125[22];/* reserved words 104-125 */
+ unsigned short last_lun; /* (word 126) */
+ unsigned short word127; /* (word 127) Feature Set
+ * Removable Media Notification
+ * 15:2 reserved
+ * 1:0 00 = not supported
+ * 01 = supported
+ * 10 = reserved
+ * 11 = reserved
+ */
+ unsigned short dlf; /* (word 128)
+ * device lock function
+ * 15:9 reserved
+ * 8 security level 1:max 0:high
+ * 7:6 reserved
+ * 5 enhanced erase
+ * 4 expire
+ * 3 frozen
+ * 2 locked
+ * 1 en/disabled
+ * 0 capability
+ */
+ unsigned short csfo; /* (word 129)
+ * current set features options
+ * 15:4 reserved
+ * 3: auto reassign
+ * 2: reverting
+ * 1: read-look-ahead
+ * 0: write cache
+ */
+ unsigned short words130_155[26];/* reserved vendor words 130-155 */
+ unsigned short word156; /* reserved vendor word 156 */
+ unsigned short words157_159[3];/* reserved vendor words 157-159 */
+ unsigned short cfa_power; /* (word 160) CFA Power Mode
+ * 15 word 160 supported
+ * 14 reserved
+ * 13
+ * 12
+ * 11:0
+ */
+ unsigned short words161_175[15];/* Reserved for CFA */
+ unsigned short words176_205[30];/* Current Media Serial Number */
+ unsigned short words206_254[49];/* reserved words 206-254 */
+ unsigned short integrity_word; /* (word 255)
+ * 15:8 Checksum
+ * 7:0 Signature
+ */
+};
+
+struct request_sense {
+#if defined(CONFIG_BIG_ENDIAN)
+ u8 valid : 1;
+ u8 error_code : 7;
+#elif defined(CONFIG_LITTLE_ENDIAN)
+ u8 error_code : 7;
+ u8 valid : 1;
+#endif
+ u8 segment_number;
+#if defined(CONFIG_BIG_ENDIAN)
+ u8 reserved1 : 2;
+ u8 ili : 1;
+ u8 reserved2 : 1;
+ u8 sense_key : 4;
+#elif defined(CONFIG_LITTLE_ENDIAN)
+ u8 sense_key : 4;
+ u8 reserved2 : 1;
+ u8 ili : 1;
+ u8 reserved1 : 2;
+#endif
+ u8 information[4];
+ u8 add_sense_len;
+ u8 command_info[4];
+ u8 asc;
+ u8 ascq;
+ u8 fruc;
+ u8 sks[3];
+ u8 asb[46];
+};
+
+struct atapi_capacity {
+ u32 lba;
+ u32 block_size;
+};
+
+#endif
diff --git a/qemu/roms/openbios/drivers/ide.c b/qemu/roms/openbios/drivers/ide.c
new file mode 100644
index 000000000..327c64a40
--- /dev/null
+++ b/qemu/roms/openbios/drivers/ide.c
@@ -0,0 +1,1685 @@
+/*
+ * OpenBIOS polled ide driver
+ *
+ * Copyright (C) 2004 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2005 Stefan Reinauer <stepan@openbios.org>
+ *
+ * Credit goes to Hale Landis for his excellent ata demo software
+ * OF node handling and some fixes by Stefan Reinauer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+#include "ide.h"
+#include "hdreg.h"
+#include "timer.h"
+
+#ifdef CONFIG_DEBUG_IDE
+#define IDE_DPRINTF(fmt, args...) \
+do { printk("IDE - %s: " fmt, __func__ , ##args); } while (0)
+#else
+#define IDE_DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+/* DECLARE data structures for the nodes. */
+DECLARE_UNNAMED_NODE( ob_ide, INSTALL_OPEN, sizeof(struct ide_drive*) );
+DECLARE_UNNAMED_NODE( ob_ide_ctrl, INSTALL_OPEN, sizeof(int));
+
+/*
+ * define to 2 for the standard 2 channels only
+ */
+#ifndef CONFIG_IDE_NUM_CHANNELS
+#define IDE_NUM_CHANNELS 4
+#else
+#define IDE_NUM_CHANNELS CONFIG_IDE_NUM_CHANNELS
+#endif
+#define IDE_MAX_CHANNELS 4
+
+#ifndef CONFIG_IDE_FIRST_UNIT
+#define FIRST_UNIT 0
+#else
+#define FIRST_UNIT CONFIG_IDE_FIRST_UNIT
+#endif
+
+#ifndef CONFIG_IDE_DEV_TYPE
+#define DEV_TYPE "ide"
+#else
+#define DEV_TYPE CONFIG_IDE_DEV_TYPE
+#endif
+
+#ifndef CONFIG_IDE_DEV_NAME
+#define DEV_NAME "ide%d"
+#else
+#define DEV_NAME CONFIG_IDE_DEV_NAME
+#endif
+
+static int current_channel = FIRST_UNIT;
+
+static struct ide_channel *channels = NULL;
+
+static inline void ide_add_channel(struct ide_channel *chan)
+{
+ chan->next = channels;
+ channels = chan;
+}
+
+static struct ide_channel *ide_seek_channel(const char *name)
+{
+ struct ide_channel *current;
+
+ current = channels;
+ while (current) {
+ if (!strcmp(current->name, name))
+ return current;
+ current = current->next;
+ }
+ return NULL;
+}
+
+/*
+ * don't be pedantic
+ */
+#undef ATA_PEDANTIC
+
+static void dump_drive(struct ide_drive *drive)
+{
+#ifdef CONFIG_DEBUG_IDE
+ printk("IDE DRIVE @%lx:\n", (unsigned long)drive);
+ printk("unit: %d\n",drive->unit);
+ printk("present: %d\n",drive->present);
+ printk("type: %d\n",drive->type);
+ printk("media: %d\n",drive->media);
+ printk("model: %s\n",drive->model);
+ printk("nr: %d\n",drive->nr);
+ printk("cyl: %d\n",drive->cyl);
+ printk("head: %d\n",drive->head);
+ printk("sect: %d\n",drive->sect);
+ printk("bs: %d\n",drive->bs);
+#endif
+}
+
+/*
+ * old style io port operations
+ */
+static unsigned char
+ob_ide_inb(struct ide_channel *chan, unsigned int port)
+{
+ return inb(chan->io_regs[port]);
+}
+
+static void
+ob_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port)
+{
+ outb(data, chan->io_regs[port]);
+}
+
+static void
+ob_ide_insw(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr, unsigned int count)
+{
+ insw(chan->io_regs[port], addr, count);
+}
+
+static void
+ob_ide_outsw(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr, unsigned int count)
+{
+ outsw(chan->io_regs[port], addr, count);
+}
+
+static inline unsigned char
+ob_ide_pio_readb(struct ide_drive *drive, unsigned int offset)
+{
+ struct ide_channel *chan = drive->channel;
+
+ return chan->obide_inb(chan, offset);
+}
+
+static inline void
+ob_ide_pio_writeb(struct ide_drive *drive, unsigned int offset,
+ unsigned char data)
+{
+ struct ide_channel *chan = drive->channel;
+
+ chan->obide_outb(chan, data, offset);
+}
+
+static inline void
+ob_ide_pio_insw(struct ide_drive *drive, unsigned int offset,
+ unsigned char *addr, unsigned int len)
+{
+ struct ide_channel *chan = drive->channel;
+
+ if (len & 1) {
+ IDE_DPRINTF("%d: command not word aligned\n", drive->nr);
+ return;
+ }
+
+ chan->obide_insw(chan, offset, addr, len / 2);
+}
+
+static inline void
+ob_ide_pio_outsw(struct ide_drive *drive, unsigned int offset,
+ unsigned char *addr, unsigned int len)
+{
+ struct ide_channel *chan = drive->channel;
+
+ if (len & 1) {
+ IDE_DPRINTF("%d: command not word aligned\n", drive->nr);
+ return;
+ }
+
+ chan->obide_outsw(chan, offset, addr, len / 2);
+}
+
+static void
+ob_ide_400ns_delay(struct ide_drive *drive)
+{
+ (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS);
+ (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS);
+ (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS);
+ (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS);
+
+ udelay(1);
+}
+
+static void
+ob_ide_error(struct ide_drive *drive, unsigned char stat, const char *msg)
+{
+#ifdef CONFIG_DEBUG_IDE
+ struct ide_channel *chan = drive->channel;
+ unsigned char err;
+#endif
+
+ if (!stat)
+ stat = ob_ide_pio_readb(drive, IDEREG_STATUS);
+
+ IDE_DPRINTF("ob_ide_error drive<%d>: %s:\n", drive->nr, msg);
+ IDE_DPRINTF(" cmd=%x, stat=%x", chan->ata_cmd.command, stat);
+
+ if ((stat & (BUSY_STAT | ERR_STAT)) == ERR_STAT) {
+#ifdef CONFIG_DEBUG_IDE
+ err =
+#endif
+ ob_ide_pio_readb(drive, IDEREG_ERROR);
+ IDE_DPRINTF(", err=%x", err);
+ }
+ IDE_DPRINTF("\n");
+
+#ifdef CONFIG_DEBUG_IDE
+ /*
+ * see if sense is valid and dump that
+ */
+ if (chan->ata_cmd.command == WIN_PACKET) {
+ struct atapi_command *cmd = &chan->atapi_cmd;
+ unsigned char old_cdb = cmd->cdb[0];
+
+ if (cmd->cdb[0] == ATAPI_REQ_SENSE) {
+ old_cdb = cmd->old_cdb;
+
+ IDE_DPRINTF(" atapi opcode=%02x", old_cdb);
+ } else {
+ int i;
+
+ IDE_DPRINTF(" cdb: ");
+ for (i = 0; i < sizeof(cmd->cdb); i++)
+ IDE_DPRINTF("%02x ", cmd->cdb[i]);
+ }
+ if (cmd->sense_valid)
+ IDE_DPRINTF(", sense: %02x/%02x/%02x",
+ cmd->sense.sense_key, cmd->sense.asc,
+ cmd->sense.ascq);
+ else
+ IDE_DPRINTF(", no sense");
+ IDE_DPRINTF("\n");
+ }
+#endif
+}
+
+/*
+ * wait for 'stat' to be set. returns 1 if failed, 0 if succesful
+ */
+static int
+ob_ide_wait_stat(struct ide_drive *drive, unsigned char ok_stat,
+ unsigned char bad_stat, unsigned char *ret_stat)
+{
+ unsigned char stat;
+ int i;
+
+ ob_ide_400ns_delay(drive);
+
+ for (i = 0; i < 5000; i++) {
+ stat = ob_ide_pio_readb(drive, IDEREG_STATUS);
+ if (!(stat & BUSY_STAT))
+ break;
+
+ udelay(1000);
+ }
+
+ if (ret_stat)
+ *ret_stat = stat;
+
+ if (stat & bad_stat)
+ return 1;
+
+ if ((stat & ok_stat) || !ok_stat)
+ return 0;
+
+ return 1;
+}
+
+static int
+ob_ide_select_drive(struct ide_drive *drive)
+{
+ struct ide_channel *chan = drive->channel;
+ unsigned char control = IDEHEAD_DEV0;
+
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) {
+ IDE_DPRINTF("select_drive: timed out\n");
+ return 1;
+ }
+
+ /*
+ * don't select drive if already active. Note: we always
+ * wait for BUSY clear
+ */
+ if (drive->unit == chan->selected)
+ return 0;
+
+ if (drive->unit)
+ control = IDEHEAD_DEV1;
+
+ ob_ide_pio_writeb(drive, IDEREG_CURRENT, control);
+ ob_ide_400ns_delay(drive);
+
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) {
+ IDE_DPRINTF("select_drive: timed out\n");
+ return 1;
+ }
+
+ chan->selected = drive->unit;
+ return 0;
+}
+
+static void
+ob_ide_write_tasklet(struct ide_drive *drive, struct ata_command *cmd)
+{
+ ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[1]);
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[3]);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[7]);
+ ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[8]);
+ ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[9]);
+
+ ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[0]);
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[2]);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[4]);
+ ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[5]);
+ ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[6]);
+
+ if (drive->unit)
+ cmd->device_head |= IDEHEAD_DEV1;
+
+ ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head);
+
+ ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command);
+ ob_ide_400ns_delay(drive);
+}
+
+static void
+ob_ide_write_registers(struct ide_drive *drive, struct ata_command *cmd)
+{
+ /*
+ * we are _always_ polled
+ */
+ ob_ide_pio_writeb(drive, IDEREG_CONTROL, cmd->control | IDECON_NIEN);
+
+ ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->feature);
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->nsector);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->sector);
+ ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->lcyl);
+ ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->hcyl);
+
+ if (drive->unit)
+ cmd->device_head |= IDEHEAD_DEV1;
+
+ ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head);
+
+ ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command);
+ ob_ide_400ns_delay(drive);
+}
+
+/*
+ * execute command with "pio non data" protocol
+ */
+#if 0
+static int
+ob_ide_pio_non_data(struct ide_drive *drive, struct ata_command *cmd)
+{
+ if (ob_ide_select_drive(drive))
+ return 1;
+
+ ob_ide_write_registers(drive, cmd);
+
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL))
+ return 1;
+
+ return 0;
+}
+#endif
+
+/*
+ * execute given command with a pio data-in phase.
+ */
+static int
+ob_ide_pio_data_in(struct ide_drive *drive, struct ata_command *cmd)
+{
+ unsigned char stat;
+ unsigned int bytes, timeout;
+
+ if (ob_ide_select_drive(drive))
+ return 1;
+
+ /*
+ * ATA must set ready and seek stat, ATAPI need only clear busy
+ */
+ timeout = 0;
+ do {
+ stat = ob_ide_pio_readb(drive, IDEREG_STATUS);
+
+ if (drive->type == ide_type_ata) {
+ /*
+ * this is BIOS code, don't be too pedantic
+ */
+#ifdef ATA_PEDANTIC
+ if ((stat & (BUSY_STAT | READY_STAT | SEEK_STAT)) ==
+ (READY_STAT | SEEK_STAT))
+ break;
+#else
+ if ((stat & (BUSY_STAT | READY_STAT)) == READY_STAT)
+ break;
+#endif
+ } else {
+ if (!(stat & BUSY_STAT))
+ break;
+ }
+ ob_ide_400ns_delay(drive);
+ } while (timeout++ < 1000);
+
+ if (timeout >= 1000) {
+ ob_ide_error(drive, stat, "drive timed out");
+ cmd->stat = stat;
+ return 1;
+ }
+
+ ob_ide_write_registers(drive, cmd);
+
+ /*
+ * now read the data
+ */
+ bytes = cmd->buflen;
+ do {
+ unsigned count = cmd->buflen;
+
+ if (count > drive->bs)
+ count = drive->bs;
+
+ /* delay 100ms for ATAPI? */
+
+ /*
+ * wait for BUSY clear
+ */
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) {
+ ob_ide_error(drive, stat, "timed out waiting for BUSY clear");
+ cmd->stat = stat;
+ break;
+ }
+
+ /*
+ * transfer the data
+ */
+ if ((stat & (BUSY_STAT | DRQ_STAT)) == DRQ_STAT) {
+ ob_ide_pio_insw(drive, IDEREG_DATA, cmd->buffer, count);
+ cmd->bytes -= count;
+ cmd->buffer += count;
+ bytes -= count;
+
+ ob_ide_400ns_delay(drive);
+ }
+
+ if (stat & (BUSY_STAT | WRERR_STAT | ERR_STAT)) {
+ cmd->stat = stat;
+ break;
+ }
+
+ if (!(stat & DRQ_STAT)) {
+ cmd->stat = stat;
+ break;
+ }
+ } while (bytes);
+
+ if (bytes)
+ IDE_DPRINTF("bytes=%d, stat=%x\n", bytes, stat);
+
+ return bytes ? 1 : 0;
+}
+
+/*
+ * execute ata command with pio packet protocol
+ */
+static int
+ob_ide_pio_packet(struct ide_drive *drive, struct atapi_command *cmd)
+{
+ unsigned char stat, reason, lcyl, hcyl;
+ struct ata_command *acmd = &drive->channel->ata_cmd;
+ unsigned char *buffer;
+ unsigned int bytes;
+
+ if (ob_ide_select_drive(drive))
+ return 1;
+
+ if (cmd->buflen && cmd->data_direction == atapi_ddir_none)
+ IDE_DPRINTF("non-zero buflen but no data direction\n");
+
+ memset(acmd, 0, sizeof(*acmd));
+ acmd->lcyl = cmd->buflen & 0xff;
+ acmd->hcyl = (cmd->buflen >> 8) & 0xff;
+ acmd->command = WIN_PACKET;
+ ob_ide_write_registers(drive, acmd);
+
+ /*
+ * BUSY must be set, _or_ DRQ | ERR
+ */
+ stat = ob_ide_pio_readb(drive, IDEREG_ASTATUS);
+ if ((stat & BUSY_STAT) == 0) {
+ if (!(stat & (DRQ_STAT | ERR_STAT))) {
+ ob_ide_error(drive, stat, "bad stat in atapi cmd");
+ cmd->stat = stat;
+ return 1;
+ }
+ }
+
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) {
+ ob_ide_error(drive, stat, "timeout, ATAPI BUSY clear");
+ cmd->stat = stat;
+ return 1;
+ }
+
+ if ((stat & (BUSY_STAT | DRQ_STAT | ERR_STAT)) != DRQ_STAT) {
+ /*
+ * if command isn't request sense, then we have a problem. if
+ * we are doing a sense, ERR_STAT == CHECK_CONDITION
+ */
+ if (cmd->cdb[0] != ATAPI_REQ_SENSE) {
+ IDE_DPRINTF("odd, drive didn't want to transfer %x\n",
+ stat);
+ return 1;
+ }
+ }
+
+ /*
+ * transfer cdb
+ */
+ ob_ide_pio_outsw(drive, IDEREG_DATA, cmd->cdb,sizeof(cmd->cdb));
+ ob_ide_400ns_delay(drive);
+
+ /*
+ * ok, cdb was sent to drive, now do data transfer (if any)
+ */
+ bytes = cmd->buflen;
+ buffer = cmd->buffer;
+ do {
+ unsigned int bc;
+
+ if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) {
+ ob_ide_error(drive, stat, "busy not clear after cdb");
+ cmd->stat = stat;
+ break;
+ }
+
+ /*
+ * transfer complete!
+ */
+ if ((stat & (BUSY_STAT | DRQ_STAT)) == 0)
+ break;
+
+ if ((stat & (BUSY_STAT | DRQ_STAT)) != DRQ_STAT)
+ break;
+
+ reason = ob_ide_pio_readb(drive, IDEREG_NSECTOR);
+ lcyl = ob_ide_pio_readb(drive, IDEREG_LCYL);
+ hcyl = ob_ide_pio_readb(drive, IDEREG_HCYL);
+
+ /*
+ * check if the drive wants to transfer data in the same
+ * direction as we do...
+ */
+ if ((reason & IREASON_CD) && cmd->data_direction != atapi_ddir_read) {
+ ob_ide_error(drive, stat, "atapi, bad transfer ddir");
+ break;
+ }
+
+ bc = (hcyl << 8) | lcyl;
+ if (!bc)
+ break;
+
+ if (bc > bytes)
+ bc = bytes;
+
+ if (cmd->data_direction == atapi_ddir_read)
+ ob_ide_pio_insw(drive, IDEREG_DATA, buffer, bc);
+ else
+ ob_ide_pio_outsw(drive, IDEREG_DATA, buffer, bc);
+
+ bytes -= bc;
+ buffer += bc;
+
+ ob_ide_400ns_delay(drive);
+ } while (bytes);
+
+ if (cmd->data_direction != atapi_ddir_none)
+ (void) ob_ide_wait_stat(drive, 0, BUSY_STAT, &stat);
+
+ if (bytes)
+ IDE_DPRINTF("cdb failed, bytes=%d, stat=%x\n", bytes, stat);
+
+ return (stat & ERR_STAT) || bytes;
+}
+
+/*
+ * execute a packet command, with retries if appropriate
+ */
+static int
+ob_ide_atapi_packet(struct ide_drive *drive, struct atapi_command *cmd)
+{
+ int retries = 5, ret;
+
+ if (drive->type != ide_type_atapi)
+ return 1;
+ if (cmd->buflen > 0xffff)
+ return 1;
+
+ /*
+ * retry loop
+ */
+ do {
+ ret = ob_ide_pio_packet(drive, cmd);
+ if (!ret)
+ break;
+
+ /*
+ * request sense failed, bummer
+ */
+ if (cmd->cdb[0] == ATAPI_REQ_SENSE)
+ break;
+
+ if (ob_ide_atapi_request_sense(drive))
+ break;
+
+ /*
+ * we know sense is valid. retry if the drive isn't ready,
+ * otherwise don't bother.
+ */
+ if (cmd->sense.sense_key != ATAPI_SENSE_NOT_READY)
+ break;
+ /*
+ * ... except 'medium not present'
+ */
+ if (cmd->sense.asc == 0x3a)
+ break;
+
+ udelay(1000000);
+ } while (retries--);
+
+ if (ret)
+ ob_ide_error(drive, 0, "atapi command");
+
+ return ret;
+}
+
+static int
+ob_ide_atapi_request_sense(struct ide_drive *drive)
+{
+ struct atapi_command *cmd = &drive->channel->atapi_cmd;
+ unsigned char old_cdb;
+
+ /*
+ * save old cdb for debug error
+ */
+ old_cdb = cmd->cdb[0];
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->cdb[0] = ATAPI_REQ_SENSE;
+ cmd->cdb[4] = 18;
+ cmd->buffer = (unsigned char *) &cmd->sense;
+ cmd->buflen = 18;
+ cmd->data_direction = atapi_ddir_read;
+ cmd->old_cdb = old_cdb;
+
+ if (ob_ide_atapi_packet(drive, cmd))
+ return 1;
+
+ cmd->sense_valid = 1;
+ return 0;
+}
+
+/*
+ * make sure drive is ready and media loaded
+ */
+static int
+ob_ide_atapi_drive_ready(struct ide_drive *drive)
+{
+ struct atapi_command *cmd = &drive->channel->atapi_cmd;
+ struct atapi_capacity cap;
+
+ IDE_DPRINTF("ob_ide_atapi_drive_ready\n");
+
+ /*
+ * Test Unit Ready is like a ping
+ */
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->cdb[0] = ATAPI_TUR;
+
+ if (ob_ide_atapi_packet(drive, cmd)) {
+ IDE_DPRINTF("%d: TUR failed\n", drive->nr);
+ return 1;
+ }
+
+ /*
+ * don't force load of tray (bit 2 in byte 4 of cdb), it's
+ * annoying and we don't want to deal with errors from drives
+ * that cannot do it
+ */
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->cdb[0] = ATAPI_START_STOP_UNIT;
+ cmd->cdb[4] = 0x01;
+
+ if (ob_ide_atapi_packet(drive, cmd)) {
+ IDE_DPRINTF("%d: START_STOP unit failed\n", drive->nr);
+ return 1;
+ }
+
+ /*
+ * finally, get capacity and block size
+ */
+ memset(cmd, 0, sizeof(*cmd));
+ memset(&cap, 0, sizeof(cap));
+
+ cmd->cdb[0] = ATAPI_READ_CAPACITY;
+ cmd->buffer = (unsigned char *) &cap;
+ cmd->buflen = sizeof(cap);
+ cmd->data_direction = atapi_ddir_read;
+
+ if (ob_ide_atapi_packet(drive, cmd)) {
+ drive->sectors = 0x1fffff;
+ drive->bs = 2048;
+ return 1;
+ }
+
+ drive->sectors = __be32_to_cpu(cap.lba) + 1;
+ drive->bs = __be32_to_cpu(cap.block_size);
+ return 0;
+}
+
+/*
+ * read from an atapi device, using READ_10
+ */
+static int
+ob_ide_read_atapi(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ struct atapi_command *cmd = &drive->channel->atapi_cmd;
+
+ if (ob_ide_atapi_drive_ready(drive))
+ return 1;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ /*
+ * READ_10 should work on generally any atapi device
+ */
+ cmd->cdb[0] = ATAPI_READ_10;
+ cmd->cdb[2] = (block >> 24) & 0xff;
+ cmd->cdb[3] = (block >> 16) & 0xff;
+ cmd->cdb[4] = (block >> 8) & 0xff;
+ cmd->cdb[5] = block & 0xff;
+ cmd->cdb[7] = (sectors >> 8) & 0xff;
+ cmd->cdb[8] = sectors & 0xff;
+
+ cmd->buffer = buf;
+ cmd->buflen = sectors * 2048;
+ cmd->data_direction = atapi_ddir_read;
+
+ return ob_ide_atapi_packet(drive, cmd);
+}
+
+static int
+ob_ide_read_ata_chs(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ struct ata_command *cmd = &drive->channel->ata_cmd;
+ unsigned int track = (block / drive->sect);
+ unsigned int sect = (block % drive->sect) + 1;
+ unsigned int head = (track % drive->head);
+ unsigned int cyl = (track / drive->head);
+
+ /*
+ * fill in chs command to read from disk at given location
+ */
+ cmd->buffer = buf;
+ cmd->buflen = sectors * 512;
+
+ cmd->nsector = sectors & 0xff;
+ cmd->sector = sect;
+ cmd->lcyl = cyl;
+ cmd->hcyl = cyl >> 8;
+ cmd->device_head = head;
+
+ cmd->command = WIN_READ;
+
+ return ob_ide_pio_data_in(drive, cmd);
+}
+
+static int
+ob_ide_read_ata_lba28(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ struct ata_command *cmd = &drive->channel->ata_cmd;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ /*
+ * fill in 28-bit lba command to read from disk at given location
+ */
+ cmd->buffer = buf;
+ cmd->buflen = sectors * 512;
+
+ cmd->nsector = sectors;
+ cmd->sector = block;
+ cmd->lcyl = block >>= 8;
+ cmd->hcyl = block >>= 8;
+ cmd->device_head = ((block >> 8) & 0x0f);
+ cmd->device_head |= (1 << 6);
+
+ cmd->command = WIN_READ;
+
+ return ob_ide_pio_data_in(drive, cmd);
+}
+
+static int
+ob_ide_read_ata_lba48(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ struct ata_command *cmd = &drive->channel->ata_cmd;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ cmd->buffer = buf;
+ cmd->buflen = sectors * 512;
+
+ /*
+ * we are using tasklet addressing here
+ */
+ cmd->task[2] = sectors;
+ cmd->task[3] = sectors >> 8;
+ cmd->task[4] = block;
+ cmd->task[5] = block >> 8;
+ cmd->task[6] = block >> 16;
+ cmd->task[7] = block >> 24;
+ cmd->task[8] = (u64) block >> 32;
+ cmd->task[9] = (u64) block >> 40;
+
+ cmd->command = WIN_READ_EXT;
+
+ ob_ide_write_tasklet(drive, cmd);
+
+ return ob_ide_pio_data_in(drive, cmd);
+}
+/*
+ * read 'sectors' sectors from ata device
+ */
+static int
+ob_ide_read_ata(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ unsigned long long end_block = block + sectors;
+ const int need_lba48 = (end_block > (1ULL << 28)) || (sectors > 255);
+
+ if (end_block > drive->sectors)
+ return 1;
+ if (need_lba48 && drive->addressing != ide_lba48)
+ return 1;
+
+ /*
+ * use lba48 if we have to, otherwise use the faster lba28
+ */
+ if (need_lba48)
+ return ob_ide_read_ata_lba48(drive, block, buf, sectors);
+ else if (drive->addressing != ide_chs)
+ return ob_ide_read_ata_lba28(drive, block, buf, sectors);
+
+ return ob_ide_read_ata_chs(drive, block, buf, sectors);
+}
+
+static int
+ob_ide_read_sectors(struct ide_drive *drive, unsigned long long block,
+ unsigned char *buf, unsigned int sectors)
+{
+ if (!sectors)
+ return 1;
+ if (block + sectors > drive->sectors)
+ return 1;
+
+ IDE_DPRINTF("ob_ide_read_sectors: block=%lu sectors=%u\n",
+ (unsigned long) block, sectors);
+
+ if (drive->type == ide_type_ata)
+ return ob_ide_read_ata(drive, block, buf, sectors);
+ else
+ return ob_ide_read_atapi(drive, block, buf, sectors);
+}
+
+/*
+ * byte swap the string if necessay, and strip leading/trailing blanks
+ */
+static void
+ob_ide_fixup_string(unsigned char *s, unsigned int len)
+{
+ unsigned char *p = s, *end = &s[len & ~1];
+
+ /*
+ * if big endian arch, byte swap the string
+ */
+#ifdef CONFIG_BIG_ENDIAN
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = __le16_to_cpu(*pp);
+ }
+#endif
+
+ while (s != end && *s == ' ')
+ ++s;
+ while (s != end && *s)
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ while (p != end)
+ *p++ = '\0';
+}
+
+/*
+ * it's big endian, we need to swap (if on little endian) the items we use
+ */
+static int
+ob_ide_fixup_id(struct hd_driveid *id)
+{
+ ob_ide_fixup_string(id->model, 40);
+ id->config = __le16_to_cpu(id->config);
+ id->lba_capacity = __le32_to_cpu(id->lba_capacity);
+ id->cyls = __le16_to_cpu(id->cyls);
+ id->heads = __le16_to_cpu(id->heads);
+ id->sectors = __le16_to_cpu(id->sectors);
+ id->command_set_2 = __le16_to_cpu(id->command_set_2);
+ id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2);
+
+ return 0;
+}
+
+static int
+ob_ide_identify_drive(struct ide_drive *drive)
+{
+ struct ata_command *cmd = &drive->channel->ata_cmd;
+ struct hd_driveid id;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->buffer = (unsigned char *) &id;
+ cmd->buflen = 512;
+
+ if (drive->type == ide_type_ata)
+ cmd->command = WIN_IDENTIFY;
+ else if (drive->type == ide_type_atapi)
+ cmd->command = WIN_IDENTIFY_PACKET;
+ else {
+ IDE_DPRINTF("%s: called with bad device type %d\n",
+ __FUNCTION__, drive->type);
+ return 1;
+ }
+
+ if (ob_ide_pio_data_in(drive, cmd))
+ return 1;
+
+ ob_ide_fixup_id(&id);
+
+ if (drive->type == ide_type_atapi) {
+ drive->media = (id.config >> 8) & 0x1f;
+ drive->sectors = 0x7fffffff;
+ drive->bs = 2048;
+ drive->max_sectors = 31;
+ } else {
+ drive->media = ide_media_disk;
+ drive->sectors = id.lba_capacity;
+ drive->bs = 512;
+ drive->max_sectors = 255;
+
+#ifdef CONFIG_IDE_LBA48
+ if ((id.command_set_2 & 0x0400) && (id.cfs_enable_2 & 0x0400)) {
+ drive->addressing = ide_lba48;
+ drive->max_sectors = 65535;
+ } else
+#endif
+ if (id.capability & 2)
+ drive->addressing = ide_lba28;
+ else {
+ drive->addressing = ide_chs;
+ }
+
+ /* only set these in chs mode? */
+ drive->cyl = id.cyls;
+ drive->head = id.heads;
+ drive->sect = id.sectors;
+ }
+
+ strncpy(drive->model, (char*)id.model, sizeof(id.model));
+ drive->model[40] = '\0';
+ return 0;
+}
+
+/*
+ * identify type of devices on channel. must have already been probed.
+ */
+static void
+ob_ide_identify_drives(struct ide_channel *chan)
+{
+ struct ide_drive *drive;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ drive = &chan->drives[i];
+
+ if (!drive->present)
+ continue;
+
+ ob_ide_identify_drive(drive);
+ }
+}
+
+/*
+ * software reset (ATA-4, section 8.3)
+ */
+static void
+ob_ide_software_reset(struct ide_drive *drive)
+{
+ struct ide_channel *chan = drive->channel;
+
+ ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN | IDECON_SRST);
+ ob_ide_400ns_delay(drive);
+ ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN);
+ ob_ide_400ns_delay(drive);
+
+ /*
+ * if master is present, wait for BUSY clear
+ */
+ if (chan->drives[0].present)
+ ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL);
+
+ /*
+ * if slave is present, wait until it allows register access
+ */
+ if (chan->drives[1].present) {
+ unsigned char sectorn, sectorc;
+ int timeout = 1000;
+
+ do {
+ /*
+ * select it
+ */
+ ob_ide_pio_writeb(drive, IDEREG_CURRENT, IDEHEAD_DEV1);
+ ob_ide_400ns_delay(drive);
+
+ sectorn = ob_ide_pio_readb(drive, IDEREG_SECTOR);
+ sectorc = ob_ide_pio_readb(drive, IDEREG_NSECTOR);
+
+ if (sectorc == 0x01 && sectorn == 0x01)
+ break;
+
+ } while (--timeout);
+ }
+
+ /*
+ * reset done, reselect original device
+ */
+ drive->channel->selected = -1;
+ ob_ide_select_drive(drive);
+}
+
+/*
+ * this serves as both a device check, and also to verify that the drives
+ * we initially "found" are really there
+ */
+static void
+ob_ide_device_type_check(struct ide_drive *drive)
+{
+ unsigned char sc, sn, cl, ch, st;
+
+ if (ob_ide_select_drive(drive))
+ return;
+
+ sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR);
+ sn = ob_ide_pio_readb(drive, IDEREG_SECTOR);
+
+ if (sc == 0x01 && sn == 0x01) {
+ /*
+ * read device signature
+ */
+ cl = ob_ide_pio_readb(drive, IDEREG_LCYL);
+ ch = ob_ide_pio_readb(drive, IDEREG_HCYL);
+ st = ob_ide_pio_readb(drive, IDEREG_STATUS);
+ if (cl == 0x14 && ch == 0xeb)
+ drive->type = ide_type_atapi;
+ else if (cl == 0x00 && ch == 0x00 && st != 0x00)
+ drive->type = ide_type_ata;
+ else
+ drive->present = 0;
+ } else
+ drive->present = 0;
+}
+
+/*
+ * pure magic
+ */
+static void
+ob_ide_device_check(struct ide_drive *drive)
+{
+ unsigned char sc, sn;
+
+ /*
+ * non-existing io port should return 0xff, don't probe this
+ * channel at all then
+ */
+ if (ob_ide_pio_readb(drive, IDEREG_STATUS) == 0xff) {
+ drive->channel->present = 0;
+ return;
+ }
+
+ if (ob_ide_select_drive(drive))
+ return;
+
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa);
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0xaa);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0x55);
+ ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55);
+ ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa);
+
+ sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR);
+ sn = ob_ide_pio_readb(drive, IDEREG_SECTOR);
+
+ /*
+ * we _think_ the device is there, we will make sure later
+ */
+ if (sc == 0x55 && sn == 0xaa) {
+ drive->present = 1;
+ drive->type = ide_type_unknown;
+ }
+}
+
+/*
+ * probe the legacy ide ports and find attached devices.
+ */
+static void
+ob_ide_probe(struct ide_channel *chan)
+{
+ struct ide_drive *drive;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ drive = &chan->drives[i];
+
+ ob_ide_device_check(drive);
+
+ /*
+ * no point in continuing
+ */
+ if (!chan->present)
+ break;
+
+ if (!drive->present)
+ continue;
+
+ /*
+ * select and reset device
+ */
+ if (ob_ide_select_drive(drive))
+ continue;
+
+ ob_ide_software_reset(drive);
+
+ ob_ide_device_type_check(drive);
+ }
+}
+
+/*
+ * The following functions are interfacing with OpenBIOS. They
+ * are device node methods. Thus they have to do proper stack handling.
+ *
+ */
+
+/*
+ * 255 sectors for ata lba28, 65535 for lba48, and 31 sectors for atapi
+ */
+static void
+ob_ide_max_transfer(int *idx)
+{
+ struct ide_drive *drive = *(struct ide_drive **)idx;
+
+ IDE_DPRINTF("max_transfer %x\n", drive->max_sectors * drive->bs);
+
+ PUSH(drive->max_sectors * drive->bs);
+}
+
+static void
+ob_ide_read_blocks(int *idx)
+{
+ cell n = POP(), cnt=n;
+ ucell blk = POP();
+ unsigned char *dest = (unsigned char *)cell2pointer(POP());
+ struct ide_drive *drive = *(struct ide_drive **)idx;
+
+ IDE_DPRINTF("ob_ide_read_blocks %lx block=%ld n=%ld\n",
+ (unsigned long)dest, (unsigned long)blk, (long)n);
+
+ while (n) {
+ int len = n;
+ if (len > drive->max_sectors)
+ len = drive->max_sectors;
+
+ if (ob_ide_read_sectors(drive, blk, dest, len)) {
+ IDE_DPRINTF("ob_ide_read_blocks: error\n");
+ RET(0);
+ }
+
+ dest += len * drive->bs;
+ n -= len;
+ blk += len;
+ }
+
+ PUSH(cnt);
+}
+
+static void
+ob_ide_block_size(int *idx)
+{
+ struct ide_drive *drive = *(struct ide_drive **)idx;
+
+ IDE_DPRINTF("ob_ide_block_size: block size %x\n", drive->bs);
+
+ PUSH(drive->bs);
+}
+
+static void
+ob_ide_initialize(int *idx)
+{
+ int props[3];
+ phandle_t ph=get_cur_dev();
+
+ push_str("block");
+ fword("device-type");
+
+ // Set dummy reg properties
+
+ set_int_property(ph, "#address-cells", 1);
+ set_int_property(ph, "#size-cells", 0);
+
+ props[0] = __cpu_to_be32(0); props[1] = __cpu_to_be32(0); props[2] = __cpu_to_be32(0);
+ set_property(ph, "reg", (char *)&props, 3*sizeof(int));
+
+ fword("is-deblocker");
+}
+
+static void
+ob_ide_open(int *idx)
+{
+ int ret=1, len;
+ phandle_t ph;
+ struct ide_drive *drive;
+ struct ide_channel *chan;
+ char *idename;
+ int unit;
+
+ fword("my-unit");
+ unit = POP();
+
+ fword("my-parent");
+ fword("ihandle>phandle");
+ ph=(phandle_t)POP();
+ idename=get_property(ph, "name", &len);
+
+ chan = ide_seek_channel(idename);
+ drive = &chan->drives[unit];
+ *(struct ide_drive **)idx = drive;
+
+ IDE_DPRINTF("opening channel %d unit %d\n", idx[1], idx[0]);
+ dump_drive(drive);
+
+ if (drive->type != ide_type_ata)
+ ret= !ob_ide_atapi_drive_ready(drive);
+
+ selfword("open-deblocker");
+
+ /* interpose disk-label */
+ ph = find_dev("/packages/disk-label");
+ fword("my-args");
+ PUSH_ph( ph );
+ fword("interpose");
+
+ RET ( -ret );
+}
+
+static void
+ob_ide_close(struct ide_drive *drive)
+{
+ selfword("close-deblocker");
+}
+
+NODE_METHODS(ob_ide) = {
+ { NULL, ob_ide_initialize },
+ { "open", ob_ide_open },
+ { "close", ob_ide_close },
+ { "read-blocks", ob_ide_read_blocks },
+ { "block-size", ob_ide_block_size },
+ { "max-transfer", ob_ide_max_transfer },
+};
+
+static void
+ob_ide_ctrl_initialize(int *idx)
+{
+ phandle_t ph=get_cur_dev();
+
+ /* set device type */
+ push_str(DEV_TYPE);
+ fword("device-type");
+
+ set_int_property(ph, "#address-cells", 1);
+ set_int_property(ph, "#size-cells", 0);
+}
+
+static void
+ob_ide_ctrl_decodeunit(int *idx)
+{
+ fword("parse-hex");
+}
+
+NODE_METHODS(ob_ide_ctrl) = {
+ { NULL, ob_ide_ctrl_initialize },
+ { "decode-unit", ob_ide_ctrl_decodeunit },
+};
+
+static void set_cd_alias(const char *path)
+{
+ phandle_t aliases;
+
+ aliases = find_dev("/aliases");
+
+ if (get_property(aliases, "cd", NULL))
+ return;
+
+ set_property(aliases, "cd", path, strlen(path) + 1);
+ set_property(aliases, "cdrom", path, strlen(path) + 1);
+}
+
+static void set_hd_alias(const char *path)
+{
+ phandle_t aliases;
+
+ aliases = find_dev("/aliases");
+
+ if (get_property(aliases, "hd", NULL))
+ return;
+
+ set_property(aliases, "hd", path, strlen(path) + 1);
+ set_property(aliases, "disk", path, strlen(path) + 1);
+}
+
+static void set_ide_alias(const char *path)
+{
+ phandle_t aliases;
+ static int ide_counter = 0;
+ char idestr[8];
+
+ aliases = find_dev("/aliases");
+
+ snprintf(idestr, sizeof(idestr), "ide%d", ide_counter++);
+ set_property(aliases, idestr, path, strlen(path) + 1);
+}
+
+int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0,
+ uint32_t io_port1, uint32_t ctl_port1)
+{
+ int i, j;
+ char nodebuff[128];
+ phandle_t dnode;
+ struct ide_channel *chan;
+ int io_ports[IDE_MAX_CHANNELS];
+ int ctl_ports[IDE_MAX_CHANNELS];
+ u32 props[6];
+
+ io_ports[0] = io_port0;
+ ctl_ports[0] = ctl_port0;
+ io_ports[1] = io_port1;
+ ctl_ports[1] = ctl_port1;
+
+ for (i = 0; i < IDE_NUM_CHANNELS; i++, current_channel++) {
+
+ chan = malloc(sizeof(struct ide_channel));
+
+ snprintf(chan->name, sizeof(chan->name),
+ DEV_NAME, current_channel);
+
+ chan->mmio = 0;
+
+ for (j = 0; j < 8; j++)
+ chan->io_regs[j] = io_ports[i] + j;
+
+ chan->io_regs[8] = ctl_ports[i];
+ chan->io_regs[9] = ctl_ports[i] + 1;
+
+ chan->obide_inb = ob_ide_inb;
+ chan->obide_insw = ob_ide_insw;
+ chan->obide_outb = ob_ide_outb;
+ chan->obide_outsw = ob_ide_outsw;
+
+ chan->selected = -1;
+
+ /*
+ * assume it's there, if not io port dead check will clear
+ */
+ chan->present = 1;
+
+ for (j = 0; j < 2; j++) {
+ chan->drives[j].present = 0;
+ chan->drives[j].unit = j;
+ chan->drives[j].channel = chan;
+ /* init with a decent value */
+ chan->drives[j].bs = 512;
+
+ chan->drives[j].nr = i * 2 + j;
+ }
+
+ ide_add_channel(chan);
+
+ ob_ide_probe(chan);
+
+ if (!chan->present)
+ continue;
+
+ ob_ide_identify_drives(chan);
+
+ snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path,
+ current_channel);
+ REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff);
+
+ dnode = find_dev(nodebuff);
+
+#if !defined(CONFIG_PPC) && !defined(CONFIG_SPARC64)
+ props[0]=14; props[1]=0;
+ set_property(dnode, "interrupts",
+ (char *)&props, 2*sizeof(props[0]));
+#endif
+
+ props[0] = __cpu_to_be32(chan->io_regs[0]);
+ props[1] = __cpu_to_be32(1); props[2] = __cpu_to_be32(8);
+ props[3] = __cpu_to_be32(chan->io_regs[8]);
+ props[4] = __cpu_to_be32(1); props[5] = __cpu_to_be32(2);
+ set_property(dnode, "reg", (char *)&props, 6*sizeof(props[0]));
+
+ IDE_DPRINTF(DEV_NAME": [io ports 0x%x-0x%x,0x%x]\n",
+ current_channel, chan->io_regs[0],
+ chan->io_regs[0] + 7, chan->io_regs[8]);
+
+ for (j = 0; j < 2; j++) {
+ struct ide_drive *drive = &chan->drives[j];
+ const char *media = "UNKNOWN";
+
+ if (!drive->present)
+ continue;
+
+ IDE_DPRINTF(" drive%d [ATA%s ", j,
+ drive->type == ide_type_atapi ? "PI" : "");
+ switch (drive->media) {
+ case ide_media_floppy:
+ media = "floppy";
+ break;
+ case ide_media_cdrom:
+ media = "cdrom";
+ break;
+ case ide_media_optical:
+ media = "mo";
+ break;
+ case ide_media_disk:
+ media = "disk";
+ break;
+ }
+ IDE_DPRINTF("%s]: %s\n", media, drive->model);
+ snprintf(nodebuff, sizeof(nodebuff),
+ "%s/" DEV_NAME "/%s", path, current_channel,
+ media);
+ REGISTER_NAMED_NODE(ob_ide, nodebuff);
+ dnode=find_dev(nodebuff);
+ set_int_property(dnode, "reg", j);
+
+ /* create aliases */
+
+ set_ide_alias(nodebuff);
+ if (drive->media == ide_media_cdrom)
+ set_cd_alias(nodebuff);
+ if (drive->media == ide_media_disk)
+ set_hd_alias(nodebuff);
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_DRIVER_MACIO)
+static unsigned char
+macio_ide_inb(struct ide_channel *chan, unsigned int port)
+{
+ return in_8((unsigned char*)(chan->mmio + (port << 4)));
+}
+
+static void
+macio_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port)
+{
+ out_8((unsigned char*)(chan->mmio + (port << 4)), data);
+}
+
+static void
+macio_ide_insw(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr, unsigned int count)
+{
+ _insw((uint16_t*)(chan->mmio + (port << 4)), addr, count);
+}
+
+static void
+macio_ide_outsw(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr, unsigned int count)
+{
+ _outsw((uint16_t*)(chan->mmio + (port << 4)), addr, count);
+}
+
+#define MACIO_IDE_OFFSET 0x00020000
+#define MACIO_IDE_SIZE 0x00001000
+
+int macio_ide_init(const char *path, uint32_t addr, int nb_channels)
+{
+ int i, j;
+ char nodebuff[128];
+ phandle_t dnode;
+ 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));
+
+ snprintf(chan->name, sizeof(chan->name),
+ DEV_NAME, current_channel);
+
+ chan->mmio = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE;
+
+ chan->obide_inb = macio_ide_inb;
+ chan->obide_insw = macio_ide_insw;
+ chan->obide_outb = macio_ide_outb;
+ chan->obide_outsw = macio_ide_outsw;
+
+ chan->selected = -1;
+
+ /*
+ * assume it's there, if not io port dead check will clear
+ */
+ chan->present = 1;
+
+ for (j = 0; j < 2; j++) {
+ chan->drives[j].present = 0;
+ chan->drives[j].unit = j;
+ chan->drives[j].channel = chan;
+ /* init with a decent value */
+ chan->drives[j].bs = 512;
+
+ chan->drives[j].nr = i * 2 + j;
+ }
+
+ ob_ide_probe(chan);
+
+ if (!chan->present) {
+ free(chan);
+ continue;
+ }
+
+ ide_add_channel(chan);
+
+ ob_ide_identify_drives(chan);
+
+ snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path,
+ current_channel);
+ REGISTER_NAMED_NODE(ob_ide_ctrl, nodebuff);
+
+ dnode = find_dev(nodebuff);
+
+ set_property(dnode, "compatible", (is_oldworld() ?
+ "heathrow-ata" : "keylargo-ata"), 13);
+
+ props[0] = 0x00000526;
+ props[1] = 0x00000085;
+ props[2] = 0x00000025;
+ props[3] = 0x00000025;
+ props[4] = 0x00000025;
+ props[5] = 0x00000000;
+ props[6] = 0x00000000;
+ props[7] = 0x00000000;
+ OLDWORLD(set_property(dnode, "AAPL,pio-timing",
+ (char *)&props, 8*sizeof(props[0])));
+
+ /* The first interrupt entry is the ide interrupt, the second
+ the dbdma interrupt */
+ switch (i) {
+ case 0:
+ props[0] = 0x0000000d;
+ props[2] = 0x00000002;
+ break;
+ case 1:
+ props[0] = 0x0000000e;
+ props[2] = 0x00000003;
+ break;
+ case 2:
+ props[0] = 0x0000000f;
+ props[2] = 0x00000004;
+ break;
+ default:
+ props[0] = 0x00000000;
+ props[2] = 0x00000000;
+ break;
+ }
+ props[1] = 0x00000000; /* XXX level triggered on real hw */
+ props[3] = 0x00000000;
+ NEWWORLD(set_property(dnode, "interrupts",
+ (char *)&props, 4*sizeof(props[0])));
+ NEWWORLD(set_int_property(dnode, "#interrupt-cells", 2));
+
+ props[1] = props[2];
+ OLDWORLD(set_property(dnode, "AAPL,interrupts",
+ (char *)&props, 2*sizeof(props[0])));
+
+ props[0] = MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE;
+ props[1] = MACIO_IDE_SIZE;
+ props[2] = 0x00008b00 + i * 0x0200;
+ props[3] = 0x0200;
+ set_property(dnode, "reg", (char *)&props, 4*sizeof(props[0]));
+
+ props[0] = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE;
+ props[1] = addr + 0x00008b00 + i * 0x0200;
+ OLDWORLD(set_property(dnode, "AAPL,address",
+ (char *)&props, 2*sizeof(props[0])));
+
+ props[0] = 0;
+ OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props,
+ 1 * sizeof(props[0])));
+ IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n",
+ current_channel, chan->mmio);
+
+ for (j = 0; j < 2; j++) {
+ struct ide_drive *drive = &chan->drives[j];
+ const char *media = "UNKNOWN";
+
+ if (!drive->present)
+ continue;
+
+ IDE_DPRINTF(" drive%d [ATA%s ", j,
+ drive->type == ide_type_atapi ? "PI" : "");
+ switch (drive->media) {
+ case ide_media_floppy:
+ media = "floppy";
+ break;
+ case ide_media_cdrom:
+ media = "cdrom";
+ break;
+ case ide_media_optical:
+ media = "mo";
+ break;
+ case ide_media_disk:
+ media = "disk";
+ break;
+ }
+ IDE_DPRINTF("%s]: %s\n", media, drive->model);
+ snprintf(nodebuff, sizeof(nodebuff),
+ "%s/" DEV_NAME "/%s", path, current_channel,
+ media);
+ REGISTER_NAMED_NODE(ob_ide, nodebuff);
+ dnode = find_dev(nodebuff);
+ set_int_property(dnode, "reg", j);
+
+ /* create aliases */
+
+ set_ide_alias(nodebuff);
+ if (drive->media == ide_media_cdrom)
+ set_cd_alias(nodebuff);
+ if (drive->media == ide_media_disk)
+ set_hd_alias(nodebuff);
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_DRIVER_MACIO */
diff --git a/qemu/roms/openbios/drivers/ide.h b/qemu/roms/openbios/drivers/ide.h
new file mode 100644
index 000000000..d6c4b9f5d
--- /dev/null
+++ b/qemu/roms/openbios/drivers/ide.h
@@ -0,0 +1,213 @@
+#ifndef IDE_H
+#define IDE_H
+
+#include "hdreg.h"
+
+/*
+ * legacy ide ports
+ */
+#define IDEREG_DATA 0x00
+#define IDEREG_ERROR 0x01
+#define IDEREG_FEATURE IDEREG_ERROR
+#define IDEREG_NSECTOR 0x02
+#define IDEREG_SECTOR 0x03
+#define IDEREG_LCYL 0x04
+#define IDEREG_HCYL 0x05
+#define IDEREG_CURRENT 0x06
+#define IDEREG_STATUS 0x07
+#define IDEREG_COMMAND IDEREG_STATUS
+#define IDEREG_CONTROL 0x08
+#define IDEREG_ASTATUS IDEREG_CONTROL
+
+/*
+ * device control bits
+ */
+#define IDECON_NIEN 0x02
+#define IDECON_SRST 0x04
+
+/*
+ * device head bits
+ */
+#define IDEHEAD_LBA 0x40
+#define IDEHEAD_DEV0 0x00
+#define IDEHEAD_DEV1 0x10
+
+/*
+ * status bytes
+ */
+#define ERR_STAT 0x01
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+#define IREASON_CD 0x01
+#define IREASON_IO 0x02
+
+/*
+ * ATA opcodes
+ */
+#define WIN_READ 0x20
+#define WIN_READ_EXT 0x24
+#define WIN_IDENTIFY 0xEC
+#define WIN_PACKET 0xA0
+#define WIN_IDENTIFY_PACKET 0xA1
+
+/*
+ * ATAPI opcodes
+ */
+#define ATAPI_TUR 0x00
+#define ATAPI_READ_10 0x28
+#define ATAPI_REQ_SENSE 0x03
+#define ATAPI_START_STOP_UNIT 0x1b
+#define ATAPI_READ_CAPACITY 0x25
+
+/*
+ * atapi sense keys
+ */
+#define ATAPI_SENSE_NOT_READY 0x02
+
+/*
+ * supported device types
+ */
+enum {
+ ide_type_unknown,
+ ide_type_ata,
+ ide_type_atapi,
+};
+
+enum {
+ ide_media_floppy = 0x00,
+ ide_media_cdrom = 0x05,
+ ide_media_optical = 0x07,
+ ide_media_disk = 0x20,
+};
+
+/*
+ * drive addressing
+ */
+enum {
+ ide_chs = 1,
+ ide_lba28,
+ ide_lba48,
+};
+
+/*
+ * simple ata command that works for everything (except 48-bit lba commands)
+ */
+struct ata_command {
+ unsigned char *buffer;
+ unsigned int buflen;
+
+ /*
+ * data register
+ */
+ unsigned char data;
+ unsigned char feature;
+ unsigned char nsector;
+ unsigned char sector;
+ unsigned char lcyl;
+ unsigned char hcyl;
+ unsigned char device_head;
+ unsigned char command;
+ unsigned char control;
+
+ /*
+ * or tasklet, just for lba48 for now (above could be scrapped)
+ */
+ unsigned char task[10];
+
+ /*
+ * output
+ */
+ unsigned char stat;
+ unsigned int bytes;
+};
+
+struct atapi_command {
+ unsigned char cdb[12];
+ unsigned char *buffer;
+ unsigned int buflen;
+ unsigned char data_direction;
+
+ unsigned char stat;
+ unsigned char sense_valid;
+ struct request_sense sense;
+ unsigned char old_cdb;
+};
+
+struct ide_channel;
+
+struct ide_drive {
+ char unit; /* 0: master, 1: slave */
+ char present; /* there or not */
+ char type; /* ata or atapi */
+ char media; /* disk, cdrom, etc */
+ char addressing; /* chs/lba28/lba48 */
+
+ char model[41]; /* name */
+ int nr;
+
+ unsigned long sectors;
+
+ unsigned int max_sectors;
+
+ /*
+ * for legacy chs crap
+ */
+ unsigned int cyl;
+ unsigned int head;
+ unsigned int sect;
+
+ unsigned int bs; /* block size */
+
+ struct ide_channel *channel;
+};
+
+struct ide_channel {
+
+ char name[32];
+ struct ide_channel *next;
+
+ /*
+ * either mmio or io_regs is set to indicate mmio or not
+ */
+ unsigned long mmio;
+ int io_regs[10];
+
+ /*
+ * can be set to a mmio hook, default it legacy outb/inb
+ */
+ void (*obide_outb)(struct ide_channel *chan,
+ unsigned char addr, unsigned int port);
+ unsigned char (*obide_inb)(struct ide_channel *chan,
+ unsigned int port);
+ void (*obide_insw)(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr,
+ unsigned int count);
+ void (*obide_outsw)(struct ide_channel *chan,
+ unsigned int port, unsigned char *addr,
+ unsigned int count);
+
+ struct ide_drive drives[2];
+ char selected;
+ char present;
+
+ /*
+ * only one can be busy per channel
+ */
+ struct ata_command ata_cmd;
+ struct atapi_command atapi_cmd;
+
+};
+
+enum {
+ atapi_ddir_none,
+ atapi_ddir_read,
+ atapi_ddir_write,
+};
+
+static int ob_ide_atapi_request_sense(struct ide_drive *drive);
+
+#endif
diff --git a/qemu/roms/openbios/drivers/iommu.c b/qemu/roms/openbios/drivers/iommu.c
new file mode 100644
index 000000000..cd9a64bb6
--- /dev/null
+++ b/qemu/roms/openbios/drivers/iommu.c
@@ -0,0 +1,207 @@
+/**
+ ** Proll (PROM replacement)
+ ** iommu.c: Functions for DVMA management.
+ ** Copyright 1999 Pete Zaitcev
+ ** This code is licensed under GNU General Public License.
+ **/
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libopenbios/ofmem.h"
+#include "drivers/drivers.h"
+#include "iommu.h"
+#include "arch/sparc32/ofmem_sparc32.h"
+
+#ifdef CONFIG_DEBUG_IOMMU
+#define DPRINTF(fmt, args...) \
+ do { printk(fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+/*
+ * IOMMU parameters
+ */
+struct iommu {
+ struct iommu_regs *regs;
+ unsigned int *page_table;
+ unsigned long plow; /* Base bus address */
+};
+
+static struct iommu ciommu;
+
+static void
+iommu_invalidate(struct iommu_regs *iregs)
+{
+ iregs->tlbflush = 0;
+}
+
+/*
+ * XXX This is a problematic interface. We alloc _memory_ which is uncached.
+ * So if we ever reuse allocations somebody is going to get uncached pages.
+ * Returned address is always aligned by page.
+ * BTW, we were not going to give away anonymous storage, were we not?
+ */
+void *
+dvma_alloc(int size, unsigned int *pphys)
+{
+ void *va;
+ unsigned int pa, ba;
+ unsigned int npages;
+ unsigned int mva, mpa;
+ unsigned int i;
+ unsigned int *iopte;
+ struct iommu *t = &ciommu;
+ int ret;
+
+ npages = (size + (PAGE_SIZE-1)) / PAGE_SIZE;
+ ret = ofmem_posix_memalign(&va, npages * PAGE_SIZE, PAGE_SIZE);
+ if (ret != 0)
+ return NULL;
+
+ ba = (unsigned int)mem_alloc(&cdvmem, npages * PAGE_SIZE, PAGE_SIZE);
+ if (ba == 0)
+ return NULL;
+
+ pa = (unsigned int)va2pa((unsigned long)va);
+
+ /*
+ * Change page attributes in MMU to uncached.
+ */
+ mva = (unsigned int) va;
+ mpa = (unsigned int) pa;
+ ofmem_arch_map_pages(mpa, mva, npages * PAGE_SIZE, ofmem_arch_io_translation_mode(mpa));
+
+ /*
+ * Map into IOMMU page table.
+ */
+ mpa = (unsigned int) pa;
+ iopte = &t->page_table[(ba - t->plow) / PAGE_SIZE];
+ for (i = 0; i < npages; i++) {
+ *iopte++ = MKIOPTE(mpa);
+ mpa += PAGE_SIZE;
+ }
+
+ *pphys = ba;
+
+ return va;
+}
+
+/*
+ * Initialize IOMMU
+ * This looks like initialization of CPU MMU but
+ * the routine is higher in food chain.
+ */
+static struct iommu_regs *
+iommu_init(struct iommu *t, uint64_t base)
+{
+ unsigned int *ptab;
+ int ptsize;
+#ifdef CONFIG_DEBUG_IOMMU
+ unsigned int impl, vers;
+#endif
+ unsigned int tmp;
+ struct iommu_regs *regs;
+ int ret;
+ unsigned long vasize;
+
+ regs = (struct iommu_regs *)ofmem_map_io(base, IOMMU_REGS);
+ if (regs == NULL) {
+ DPRINTF("Cannot map IOMMU\n");
+ for (;;) { }
+ }
+ t->regs = regs;
+#ifdef CONFIG_DEBUG_IOMMU
+ impl = (regs->control & IOMMU_CTRL_IMPL) >> 28;
+ vers = (regs->control & IOMMU_CTRL_VERS) >> 24;
+#endif
+
+ tmp = regs->control;
+ tmp &= ~(IOMMU_CTRL_RNGE);
+
+ tmp |= (IOMMU_RNGE_32MB | IOMMU_CTRL_ENAB);
+ t->plow = 0xfe000000; /* End - 32 MB */
+ /* Size of VA region that we manage */
+ vasize = 0x2000000; /* 32 MB */
+
+ regs->control = tmp;
+ iommu_invalidate(regs);
+
+ /* Allocate IOMMU page table */
+ /* Tremendous alignment causes great waste... */
+ ptsize = (vasize / PAGE_SIZE) * sizeof(int);
+ ret = ofmem_posix_memalign((void *)&ptab, ptsize, ptsize);
+ if (ret != 0) {
+ DPRINTF("Cannot allocate IOMMU table [0x%x]\n", ptsize);
+ for (;;) { }
+ }
+ t->page_table = ptab;
+
+ /* flush_cache_all(); */
+ /** flush_tlb_all(); **/
+ tmp = (unsigned int)va2pa((unsigned long)ptab);
+ regs->base = tmp >> 4;
+ iommu_invalidate(regs);
+
+ DPRINTF("IOMMU: impl %d vers %d page table at 0x%p (pa 0x%x) of size %d bytes\n",
+ impl, vers, t->page_table, tmp, ptsize);
+
+ mem_init(&cdvmem, (char*)t->plow, (char *)0xfffff000);
+ return regs;
+}
+
+/* ( addr.lo addr.hi size -- virt ) */
+
+static void
+ob_iommu_map_in(void)
+{
+ phys_addr_t phys;
+ ucell size, virt;
+
+ size = POP();
+ phys = POP();
+ phys = (phys << 32) + POP();
+
+ virt = ofmem_map_io(phys, size);
+
+ PUSH(virt);
+}
+
+/* ( virt size ) */
+
+static void
+ob_iommu_map_out(void)
+{
+ ucell size = POP();
+ ucell virt = POP();
+
+ ofmem_release_io(virt, size);
+}
+
+void
+ob_init_iommu(uint64_t base)
+{
+ struct iommu_regs *regs;
+
+ regs = iommu_init(&ciommu, base);
+
+ push_str("/iommu");
+ fword("find-device");
+ PUSH((unsigned long)regs);
+ fword("encode-int");
+ push_str("address");
+ fword("property");
+
+ PUSH(base >> 32);
+ fword("encode-int");
+ PUSH(base & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(IOMMU_REGS);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ bind_func("map-in", ob_iommu_map_in);
+ bind_func("map-out", ob_iommu_map_out);
+}
diff --git a/qemu/roms/openbios/drivers/iommu.h b/qemu/roms/openbios/drivers/iommu.h
new file mode 100644
index 000000000..59e3387ca
--- /dev/null
+++ b/qemu/roms/openbios/drivers/iommu.h
@@ -0,0 +1,102 @@
+/* iommu.h: Definitions for the sun4m IOMMU.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Adapted for Proll by Pete Zaitcev in 1999 (== made worse than original).
+ */
+
+/* #include <asm/page.h> */
+
+/* The iommu handles all virtual to physical address translations
+ * that occur between the SBUS and physical memory. Access by
+ * the cpu to IO registers and similar go over the mbus so are
+ * translated by the on chip SRMMU. The iommu and the srmmu do
+ * not need to have the same translations at all, in fact most
+ * of the time the translations they handle are a disjunct set.
+ * Basically the iommu handles all dvma sbus activity.
+ */
+
+/* The IOMMU registers occupy three pages in IO space. */
+struct iommu_regs {
+ /* First page */
+ volatile unsigned long control; /* IOMMU control */
+ volatile unsigned long base; /* Physical base of iopte page table */
+ volatile unsigned long _unused1[3];
+ volatile unsigned long tlbflush; /* write only */
+ volatile unsigned long pageflush; /* write only */
+ volatile unsigned long _unused2[1017];
+ /* Second page */
+ volatile unsigned long afsr; /* Async-fault status register */
+ volatile unsigned long afar; /* Async-fault physical address */
+ volatile unsigned long _unused3[2];
+ volatile unsigned long sbuscfg0; /* SBUS configuration registers, per-slot */
+ volatile unsigned long sbuscfg1;
+ volatile unsigned long sbuscfg2;
+ volatile unsigned long sbuscfg3;
+ volatile unsigned long mfsr; /* Memory-fault status register */
+ volatile unsigned long mfar; /* Memory-fault physical address */
+ volatile unsigned long _unused4[1014];
+ /* Third page */
+ volatile unsigned long mid; /* IOMMU module-id */
+};
+
+#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
+#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
+#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */
+#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */
+#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */
+#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */
+#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */
+#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */
+#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */
+#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */
+#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */
+#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */
+
+#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */
+#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after transaction */
+#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than 12.8 us. */
+#define IOMMU_AFSR_BE 0x10000000 /* Write access received error acknowledge */
+#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */
+#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */
+#define IOMMU_AFSR_RESV 0x00f00000 /* Reserver, forced to 0x8 by hardware */
+#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */
+#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */
+#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */
+
+#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */
+#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */
+#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */
+#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses
+ produced by this device as pure
+ physical. */
+
+#define IOMMU_MFSR_ERR 0x80000000 /* One or more of PERR1 or PERR0 */
+#define IOMMU_MFSR_S 0x01000000 /* Sparc was in supervisor mode */
+#define IOMMU_MFSR_CPU 0x00800000 /* CPU transaction caused parity error */
+#define IOMMU_MFSR_ME 0x00080000 /* Multiple parity errors occurred */
+#define IOMMU_MFSR_PERR 0x00006000 /* high bit indicates parity error occurred
+ on the even word of the access, low bit
+ indicated odd word caused the parity error */
+#define IOMMU_MFSR_BM 0x00001000 /* Error occurred while in boot mode */
+#define IOMMU_MFSR_C 0x00000800 /* Address causing error was marked cacheable */
+#define IOMMU_MFSR_RTYP 0x000000f0 /* Memory request transaction type */
+
+#define IOMMU_MID_SBAE 0x001f0000 /* SBus arbitration enable */
+#define IOMMU_MID_SE 0x00100000 /* Enables SCSI/ETHERNET arbitration */
+#define IOMMU_MID_SB3 0x00080000 /* Enable SBUS device 3 arbitration */
+#define IOMMU_MID_SB2 0x00040000 /* Enable SBUS device 2 arbitration */
+#define IOMMU_MID_SB1 0x00020000 /* Enable SBUS device 1 arbitration */
+#define IOMMU_MID_SB0 0x00010000 /* Enable SBUS device 0 arbitration */
+#define IOMMU_MID_MID 0x0000000f /* Module-id, hardcoded to 0x8 */
+
+/* The format of an iopte in the page tables */
+#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */
+#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */
+#define IOPTE_WRITE 0x00000004 /* Writeable */
+#define IOPTE_VALID 0x00000002 /* IOPTE is valid */
+#define IOPTE_WAZ 0x00000001 /* Write as zeros */
+
+#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
+#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
+
+#define IOMMU_REGS 0x300
diff --git a/qemu/roms/openbios/drivers/kbd.c b/qemu/roms/openbios/drivers/kbd.c
new file mode 100644
index 000000000..43070d877
--- /dev/null
+++ b/qemu/roms/openbios/drivers/kbd.c
@@ -0,0 +1,116 @@
+/*
+ * <kbd.c>
+ *
+ * Open Hack'Ware BIOS generic keyboard input translation.
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libc/string.h"
+#include "asm/types.h"
+#include "kbd.h"
+
+//#define DEBUG_KBD
+#ifdef DEBUG_KBD
+#define KBD_DPRINTF(fmt, args...) \
+do { printk("KBD - %s: " fmt, __func__ , ##args); } while (0)
+#else
+#define KBD_DPRINTF(fmt, args...) do { } while (0)
+#endif
+
+int kbd_set_keymap (kbd_t *kbd, int nb_keys, const keymap_t *keymap, const char **sequences)
+{
+ kbd->nb_keys = nb_keys;
+ kbd->keymap = keymap;
+ kbd->sequences = sequences;
+
+ return 0;
+}
+
+int kbd_translate_key (kbd_t *kbd, int keycode, int up_down, char *sequence)
+{
+ const keymap_t *keyt;
+ int mod_state, key, type;
+ int ret;
+
+ ret = -1;
+ /* Get key table */
+ if (keycode < kbd->nb_keys) {
+ keyt = &kbd->keymap[keycode];
+ /* Get modifier state */
+ mod_state = (kbd->mod_state | (kbd->mod_state >> 8)) & 0xFF;
+ /* Adjust with lock */
+ if (keyt->lck_shift >= 0) {
+ if ((kbd->mod_state >> (16 + keyt->lck_shift)) & 0x01) {
+ KBD_DPRINTF("adjust with lock %02x => %02x (%d %08x)\n",
+ mod_state,
+ mod_state ^ ((kbd->mod_state >>
+ (16 + keyt->lck_shift)) &
+ 0x01),
+ keyt->lck_shift, kbd->mod_state);
+ }
+ mod_state ^= (kbd->mod_state >> (16 + keyt->lck_shift)) & 0x01;
+ }
+ key = keyt->trans[mod_state];
+ type = key & 0xFF000000;
+ key &= ~0xFF000000;
+ switch (type) {
+ case KBD_TYPE_REGULAR:
+ if (!up_down) {
+ /* We don't care about up events on "normal" keys */
+ *sequence = key;
+ ret = 1;
+ }
+ break;
+ case KBD_TYPE_SEQUENCE:
+ if (!up_down) {
+ /* We don't care about up events on "normal" keys */
+ ret = strlen(kbd->sequences[key]);
+ memcpy(sequence, kbd->sequences[key], ret);
+ }
+ break;
+ case KBD_TYPE_LOCK:
+ if (!up_down) {
+ kbd->mod_state ^= key;
+ ret = -2;
+ KBD_DPRINTF("Change modifier type %d key %04x %s => %08x\n",
+ type, key, up_down ? "up" : "down",
+ kbd->mod_state);
+ }
+ break;
+ case KBD_TYPE_LMOD:
+ case KBD_TYPE_RMOD:
+ if (up_down)
+ kbd->mod_state &= ~key;
+ else
+ kbd->mod_state |= key;
+ KBD_DPRINTF("Change modifier type %d key %04x %s => %08x\n",
+ type, key, up_down ? "up" : "down", kbd->mod_state);
+ ret = -2; /* The caller may know the key was a modifier */
+ break;
+ default:
+ KBD_DPRINTF("Unknown key: keycode=%02x mod_state=%02x (%08x)\n",
+ keycode, mod_state, kbd->mod_state);
+ break;
+ }
+ } else {
+ KBD_DPRINTF("Unmanaged key: keycode=%02x mod_state %08x\n",
+ keycode, kbd->mod_state);
+ }
+
+ return ret;
+}
diff --git a/qemu/roms/openbios/drivers/kbd.h b/qemu/roms/openbios/drivers/kbd.h
new file mode 100644
index 000000000..8a7d0d476
--- /dev/null
+++ b/qemu/roms/openbios/drivers/kbd.h
@@ -0,0 +1,108 @@
+/*
+ * <kbd.h>
+ *
+ * Open Hack'Ware BIOS generic keyboard management definitions.
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#if !defined (__OHW_KBD_H__)
+#define __OHW_KBD_H__
+typedef struct kbd_t kbd_t;
+typedef struct keymap_t keymap_t;
+struct kbd_t {
+ uint32_t mod_state;
+ /* Modifier state
+ * 0x00 kk ll rr
+ * | | | |
+ * Not used for now -+ | | |
+ * Locks ---------------+ | |
+ * Left modifiers ---------+ |
+ * Right modifiers -----------+
+ */
+ int nb_keys;
+ const keymap_t *keymap;
+ const char **sequences;
+};
+
+/* Modifiers */
+typedef enum {
+ KBD_MOD_SHIFT = 0x01,
+ KBD_MOD_CTRL = 0x02,
+ KBD_MOD_ALT = 0x04,
+ KBD_MOD_CMD = 0x08,
+ KBD_MOD_OPT = 0x10,
+} kbd_modifiers;
+
+/* Locks */
+typedef enum {
+ KBD_LCK_CAPS = 0x01,
+ KBD_LCK_NUM = 0x02,
+ KBD_LCK_SCROLL = 0x04,
+} kbd_locks;
+
+/* Lock shifts */
+typedef enum {
+ KBD_SH_NONE = -1,
+ KBD_SH_CAPS = 0,
+ KBD_SH_NUML = 1,
+ KBD_SH_SCRL = 2,
+} kbd_lck_shifts;
+
+enum {
+ KBD_TYPE_REGULAR = 0 << 24,
+ KBD_TYPE_LMOD = 1 << 24,
+ KBD_TYPE_RMOD = 2 << 24,
+ KBD_TYPE_LOCK = 3 << 24,
+ KBD_TYPE_SEQUENCE = 4 << 24,
+};
+
+#define KBD_SEQUENCE(sequence) (KBD_TYPE_SEQUENCE | (sequence))
+
+#define KBD_MOD_MAP(mod) \
+KBD_SH_NONE, { (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \
+ (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \
+ (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), \
+ (mod), (mod), (mod), (mod), (mod), (mod), (mod), (mod), }
+#define KBD_MOD_MAP_LSHIFT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_SHIFT)
+#define KBD_MOD_MAP_RSHIFT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_SHIFT << 8))
+#define KBD_MOD_MAP_LCTRL KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_CTRL)
+#define KBD_MOD_MAP_RCTRL KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_CTRL << 8))
+#define KBD_MOD_MAP_LALT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_ALT)
+#define KBD_MOD_MAP_RALT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_ALT << 8))
+#define KBD_MOD_MAP_LCMD KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_CMD)
+#define KBD_MOD_MAP_RCMD KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_CMD << 8))
+#define KBD_MOD_MAP_LOPT KBD_MOD_MAP(KBD_TYPE_LMOD | KBD_MOD_OPT)
+#define KBD_MOD_MAP_ROPT KBD_MOD_MAP(KBD_TYPE_RMOD | (KBD_MOD_OPT << 8))
+#define KBD_MOD_MAP_CAPS KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_CAPS << 16))
+#define KBD_MOD_MAP_NUML KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_NUML << 16))
+#define KBD_MOD_MAP_SCROLL KBD_MOD_MAP(KBD_TYPE_LOCK | (KBD_LCK_SCRL << 16))
+#define KBD_MAP_NONE KBD_MOD_MAP(-1)
+
+/* Keymap definition */
+struct keymap_t {
+ /* Set the lock which applies to this key (if any) */
+ int lck_shift;
+ /* Key translations */
+ uint32_t trans[32];
+};
+
+void *kbd_new (int len);
+int kbd_set_keymap (kbd_t *kbd, int nb_keys, const keymap_t *keymap,
+ const char **sequences);
+int kbd_translate_key (kbd_t *kbd, int keycode, int up_down, char *sequence);
+
+#endif /* !defined (__OHW_KBD_H__) */
diff --git a/qemu/roms/openbios/drivers/macio.c b/qemu/roms/openbios/drivers/macio.c
new file mode 100644
index 000000000..f54bc86dc
--- /dev/null
+++ b/qemu/roms/openbios/drivers/macio.c
@@ -0,0 +1,281 @@
+/*
+ * derived from mol/mol.c,
+ * Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "arch/common/nvram.h"
+#include "packages/nvram.h"
+#include "libopenbios/bindings.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+#include "macio.h"
+#include "cuda.h"
+#include "escc.h"
+#include "drivers/pci.h"
+
+#define OW_IO_NVRAM_SIZE 0x00020000
+#define OW_IO_NVRAM_OFFSET 0x00060000
+#define OW_IO_NVRAM_SHIFT 4
+
+#define NW_IO_NVRAM_SIZE 0x00004000
+#define NW_IO_NVRAM_OFFSET 0xfff04000
+
+#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 >> shift;
+ else
+ return NW_IO_NVRAM_SIZE >> shift;
+}
+
+static unsigned long macio_nvram_offset(void)
+{
+ unsigned long r;
+
+ /* Hypervisor tells us where NVRAM lies */
+ r = fw_cfg_read_i32(FW_CFG_PPC_NVRAM_ADDR);
+ if (r)
+ return r;
+
+ /* Fall back to hardcoded addresses */
+ if (is_oldworld())
+ return OW_IO_NVRAM_OFFSET;
+
+ return NW_IO_NVRAM_OFFSET;
+}
+
+static unsigned long macio_nvram_size(void)
+{
+ if (is_oldworld())
+ return OW_IO_NVRAM_SIZE;
+ else
+ return NW_IO_NVRAM_SIZE;
+}
+
+void macio_nvram_init(const char *path, phys_addr_t addr)
+{
+ phandle_t chosen, aliases;
+ phandle_t dnode;
+ int props[2];
+ char buf[64];
+ unsigned long nvram_size, nvram_offset;
+
+ nvram_offset = macio_nvram_offset();
+ nvram_size = macio_nvram_size();
+
+ nvram = (char*)addr + nvram_offset;
+ snprintf(buf, sizeof(buf), "%s/nvram", path);
+ nvram_init(buf);
+ dnode = find_dev(buf);
+ set_int_property(dnode, "#bytes", arch_nvram_size() );
+ props[0] = __cpu_to_be32(nvram_offset);
+ props[1] = __cpu_to_be32(nvram_size);
+ set_property(dnode, "reg", (char *)&props, sizeof(props));
+ set_property(dnode, "device_type", "nvram", 6);
+ NEWWORLD(set_property(dnode, "compatible", "nvram,flash", 12));
+
+ chosen = find_dev("/chosen");
+ push_str(buf);
+ fword("open-dev");
+ set_int_property(chosen, "nvram", POP());
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "nvram", buf, strlen(buf) + 1);
+}
+
+#ifdef DUMP_NVRAM
+static void
+dump_nvram(void)
+{
+ int i, j;
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 16; j++)
+ printk ("%02x ", nvram[(i*16+j)<<4]);
+ printk (" ");
+ for (j = 0; j < 16; j++)
+ if (isprint(nvram[(i*16+j)<<4]))
+ printk("%c", nvram[(i*16+j)<<4]);
+ else
+ printk(".");
+ printk ("\n");
+ }
+}
+#endif
+
+
+void
+macio_nvram_put(char *buf)
+{
+ int i;
+ unsigned int it_shift = macio_nvram_shift();
+
+ for (i=0; i < arch_nvram_size(); i++)
+ nvram[i << it_shift] = buf[i];
+#ifdef DUMP_NVRAM
+ printk("new nvram:\n");
+ dump_nvram();
+#endif
+}
+
+void
+macio_nvram_get(char *buf)
+{
+ int i;
+ unsigned int it_shift = macio_nvram_shift();
+
+ for (i=0; i< arch_nvram_size(); i++)
+ buf[i] = nvram[i << it_shift];
+
+#ifdef DUMP_NVRAM
+ printk("current nvram:\n");
+ dump_nvram();
+#endif
+}
+
+static void
+openpic_init(const char *path, phys_addr_t addr)
+{
+ phandle_t dnode;
+ int props[2];
+ char buf[128];
+
+ push_str(path);
+ fword("find-device");
+ fword("new-device");
+ push_str("interrupt-controller");
+ fword("device-name");
+
+ snprintf(buf, sizeof(buf), "%s/interrupt-controller", path);
+ dnode = find_dev(buf);
+ set_property(dnode, "device_type", "open-pic", 9);
+ set_property(dnode, "compatible", "chrp,open-pic", 14);
+ set_property(dnode, "built-in", "", 0);
+ props[0] = __cpu_to_be32(IO_OPENPIC_OFFSET);
+ props[1] = __cpu_to_be32(IO_OPENPIC_SIZE);
+ set_property(dnode, "reg", (char *)&props, sizeof(props));
+ set_int_property(dnode, "#interrupt-cells", 2);
+ set_int_property(dnode, "#address-cells", 0);
+ set_property(dnode, "interrupt-controller", "", 0);
+ set_int_property(dnode, "clock-frequency", 4166666);
+
+ fword("finish-device");
+}
+
+DECLARE_NODE(ob_macio, INSTALL_OPEN, sizeof(int), "Tmac-io");
+
+/* ( str len -- addr ) */
+
+static void
+ob_macio_decode_unit(void *private)
+{
+ ucell addr;
+
+ const char *arg = pop_fstr_copy();
+
+ addr = strtol(arg, NULL, 16);
+
+ free((char*)arg);
+
+ PUSH(addr);
+}
+
+/* ( addr -- str len ) */
+
+static void
+ob_macio_encode_unit(void *private)
+{
+ char buf[8];
+
+ ucell addr = POP();
+
+ snprintf(buf, sizeof(buf), "%x", addr);
+
+ push_str(buf);
+}
+
+NODE_METHODS(ob_macio) = {
+ { "decode-unit", ob_macio_decode_unit },
+ { "encode-unit", ob_macio_encode_unit },
+};
+
+static void
+ob_unin_init(void)
+{
+ phandle_t dnode;
+ int props[2];
+
+ push_str("/");
+ fword("find-device");
+ fword("new-device");
+ push_str("uni-n");
+ fword("device-name");
+
+ dnode = find_dev("/uni-n");
+ set_property(dnode, "device_type", "memory-controller", 18);
+ set_property(dnode, "compatible", "uni-north", 10);
+ set_int_property(dnode, "device-rev", 0);
+ props[0] = __cpu_to_be32(0xf8000000);
+ props[1] = __cpu_to_be32(0x1000000);
+ set_property(dnode, "reg", (char *)&props, sizeof(props));
+
+ fword("finish-device");
+}
+
+void
+ob_macio_heathrow_init(const char *path, phys_addr_t addr)
+{
+ phandle_t aliases;
+
+ REGISTER_NODE(ob_macio);
+ aliases = find_dev("/aliases");
+ set_property(aliases, "mac-io", path, strlen(path) + 1);
+
+ cuda_init(path, addr);
+ macio_nvram_init(path, addr);
+ escc_init(path, addr);
+ macio_ide_init(path, addr, 2);
+}
+
+void
+ob_macio_keylargo_init(const char *path, phys_addr_t addr)
+{
+ phandle_t aliases;
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "mac-io", path, strlen(path) + 1);
+
+ cuda_init(path, addr);
+ /* The NewWorld NVRAM is not located in the MacIO device */
+ macio_nvram_init("", 0);
+ escc_init(path, addr);
+ macio_ide_init(path, addr, 2);
+ openpic_init(path, addr);
+ ob_unin_init();
+}
diff --git a/qemu/roms/openbios/drivers/macio.h b/qemu/roms/openbios/drivers/macio.h
new file mode 100644
index 000000000..925b8812b
--- /dev/null
+++ b/qemu/roms/openbios/drivers/macio.h
@@ -0,0 +1,5 @@
+extern phandle_t pic_handle;
+
+void ob_macio_heathrow_init(const char *path, phys_addr_t addr);
+void ob_macio_keylargo_init(const char *path, phys_addr_t addr);
+void macio_nvram_init(const char *path, phys_addr_t addr);
diff --git a/qemu/roms/openbios/drivers/obio.c b/qemu/roms/openbios/drivers/obio.c
new file mode 100644
index 000000000..7c135a362
--- /dev/null
+++ b/qemu/roms/openbios/drivers/obio.c
@@ -0,0 +1,525 @@
+/*
+ * OpenBIOS Sparc OBIO driver
+ *
+ * (C) 2004 Stefan Reinauer <stepan@openbios.org>
+ * (C) 2005 Ed Schouten <ed@fxq.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+#include "arch/common/nvram.h"
+#include "libopenbios/ofmem.h"
+#include "obio.h"
+#include "escc.h"
+
+#define PROMDEV_KBD 0 /* input from keyboard */
+#define PROMDEV_SCREEN 0 /* output to screen */
+#define PROMDEV_TTYA 1 /* in/out to ttya */
+
+/* DECLARE data structures for the nodes. */
+DECLARE_UNNAMED_NODE( ob_obio, INSTALL_OPEN, sizeof(int) );
+
+void
+ob_new_obio_device(const char *name, const char *type)
+{
+ push_str("/obio");
+ fword("find-device");
+ fword("new-device");
+
+ push_str(name);
+ fword("device-name");
+
+ if (type) {
+ push_str(type);
+ fword("device-type");
+ }
+}
+
+static unsigned long
+map_reg(uint64_t base, uint64_t offset, unsigned long size, int map,
+ int phys_hi)
+{
+ PUSH(phys_hi);
+ fword("encode-int");
+ PUSH(offset);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(size);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ if (map) {
+ unsigned long addr;
+
+ addr = (unsigned long)ofmem_map_io(base + offset, size);
+
+ PUSH(addr);
+ fword("encode-int");
+ push_str("address");
+ fword("property");
+ return addr;
+ }
+ return 0;
+}
+
+unsigned long
+ob_reg(uint64_t base, uint64_t offset, unsigned long size, int map)
+{
+ return map_reg(base, offset, size, map, 0);
+}
+
+void
+ob_intr(int intr)
+{
+ PUSH(intr);
+ fword("encode-int");
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ push_str("intr");
+ fword("property");
+}
+
+void
+ob_eccmemctl_init(uint64_t base)
+{
+ uint32_t version, *regs;
+ const char *mc_type;
+
+ push_str("/");
+ fword("find-device");
+ fword("new-device");
+
+ push_str("eccmemctl");
+ fword("device-name");
+
+ PUSH(0x20);
+ fword("encode-int");
+ push_str("width");
+ fword("property");
+
+ regs = (uint32_t *)map_reg(ECC_BASE, 0, ECC_SIZE, 1, ECC_BASE >> 32);
+
+ version = regs[0];
+ switch (version) {
+ case 0x00000000:
+ mc_type = "MCC";
+ break;
+ case 0x10000000:
+ mc_type = "EMC";
+ break;
+ default:
+ case 0x20000000:
+ mc_type = "SMC";
+ break;
+ }
+ push_str(mc_type);
+ fword("encode-string");
+ push_str("mc-type");
+ fword("property");
+
+ fword("finish-device");
+}
+
+static unsigned char *nvram;
+
+#define NVRAM_OB_START (0)
+#define NVRAM_OB_SIZE ((NVRAM_IDPROM - NVRAM_OB_START) & ~15)
+
+void
+arch_nvram_get(char *data)
+{
+ memcpy(data, &nvram[NVRAM_OB_START], NVRAM_OB_SIZE);
+}
+
+void
+arch_nvram_put(char *data)
+{
+ memcpy(&nvram[NVRAM_OB_START], data, NVRAM_OB_SIZE);
+}
+
+int
+arch_nvram_size(void)
+{
+ return NVRAM_OB_SIZE;
+}
+
+void
+ss5_init(uint64_t base)
+{
+ ob_new_obio_device("slavioconfig", NULL);
+
+ ob_reg(base, SLAVIO_SCONFIG, SCONFIG_REGS, 0);
+
+ fword("finish-device");
+}
+
+static void
+ob_nvram_init(uint64_t base, uint64_t offset)
+{
+ ob_new_obio_device("eeprom", NULL);
+
+ nvram = (unsigned char *)ob_reg(base, offset, NVRAM_SIZE, 1);
+
+ PUSH((unsigned long)nvram);
+ fword("encode-int");
+ push_str("address");
+ fword("property");
+
+ push_str("mk48t08");
+ fword("model");
+
+ fword("finish-device");
+
+ // Add /idprom
+ push_str("/");
+ fword("find-device");
+
+ PUSH((long)&nvram[NVRAM_IDPROM]);
+ PUSH(32);
+ fword("encode-bytes");
+ push_str("idprom");
+ fword("property");
+}
+
+static void
+ob_fd_init(uint64_t base, uint64_t offset, int intr)
+{
+ unsigned long addr;
+
+ ob_new_obio_device("SUNW,fdtwo", "block");
+
+ addr = ob_reg(base, offset, FD_REGS, 1);
+
+ ob_intr(intr);
+
+ fword("is-deblocker");
+
+ ob_floppy_init("/obio", "SUNW,fdtwo", 0, addr);
+
+ fword("finish-device");
+}
+
+static void
+ob_auxio_init(uint64_t base, uint64_t offset)
+{
+ ob_new_obio_device("auxio", NULL);
+
+ ob_reg(base, offset, AUXIO_REGS, 1);
+
+ fword("finish-device");
+}
+
+volatile unsigned char *power_reg;
+volatile unsigned int *reset_reg;
+
+static void
+sparc32_reset_all(void)
+{
+ *reset_reg = 1;
+}
+
+// AUX 2 (Software Powerdown Control) and reset
+static void
+ob_aux2_reset_init(uint64_t base, uint64_t offset, int intr)
+{
+ ob_new_obio_device("power", NULL);
+
+ power_reg = (void *)ob_reg(base, offset, AUXIO2_REGS, 1);
+
+ // Not in device tree
+ reset_reg = (unsigned int *)ofmem_map_io(base + (uint64_t)SLAVIO_RESET, RESET_REGS);
+
+ bind_func("sparc32-reset-all", sparc32_reset_all);
+ push_str("' sparc32-reset-all to reset-all");
+ fword("eval");
+
+ ob_intr(intr);
+
+ fword("finish-device");
+}
+
+volatile struct sun4m_timer_regs *counter_regs;
+
+static void
+ob_counter_init(uint64_t base, unsigned long offset, int ncpu)
+{
+ int i;
+
+ ob_new_obio_device("counter", NULL);
+
+ for (i = 0; i < ncpu; i++) {
+ PUSH(0);
+ fword("encode-int");
+ if (i != 0) fword("encode+");
+ PUSH(offset + (i * PAGE_SIZE));
+ fword("encode-int");
+ fword("encode+");
+ PUSH(COUNTER_REGS);
+ fword("encode-int");
+ fword("encode+");
+ }
+
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(offset + 0x10000);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(COUNTER_REGS);
+ fword("encode-int");
+ fword("encode+");
+
+ push_str("reg");
+ fword("property");
+
+
+ counter_regs = (struct sun4m_timer_regs *)ofmem_map_io(base + (uint64_t)offset, sizeof(*counter_regs));
+ counter_regs->cfg = 0xfffffffe;
+ counter_regs->l10_timer_limit = 0;
+ counter_regs->cpu_timers[0].l14_timer_limit = 0x9c4000; /* see comment in obio.h */
+ counter_regs->cpu_timers[0].cntrl = 1;
+
+ for (i = 0; i < ncpu; i++) {
+ PUSH((unsigned long)&counter_regs->cpu_timers[i]);
+ fword("encode-int");
+ if (i != 0)
+ fword("encode+");
+ }
+ PUSH((unsigned long)&counter_regs->l10_timer_limit);
+ fword("encode-int");
+ fword("encode+");
+ push_str("address");
+ fword("property");
+
+ fword("finish-device");
+}
+
+static volatile struct sun4m_intregs *intregs;
+
+static void
+ob_interrupt_init(uint64_t base, unsigned long offset, int ncpu)
+{
+ int i;
+
+ ob_new_obio_device("interrupt", NULL);
+
+ for (i = 0; i < ncpu; i++) {
+ PUSH(0);
+ fword("encode-int");
+ if (i != 0) fword("encode+");
+ PUSH(offset + (i * PAGE_SIZE));
+ fword("encode-int");
+ fword("encode+");
+ PUSH(INTERRUPT_REGS);
+ fword("encode-int");
+ fword("encode+");
+ }
+
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(offset + 0x10000);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(INTERRUPT_REGS);
+ fword("encode-int");
+ fword("encode+");
+
+ push_str("reg");
+ fword("property");
+
+ intregs = (struct sun4m_intregs *)ofmem_map_io(base | (uint64_t)offset, sizeof(*intregs));
+ intregs->clear = ~SUN4M_INT_MASKALL;
+ intregs->cpu_intregs[0].clear = ~0x17fff;
+
+ for (i = 0; i < ncpu; i++) {
+ PUSH((unsigned long)&intregs->cpu_intregs[i]);
+ fword("encode-int");
+ if (i != 0)
+ fword("encode+");
+ }
+ PUSH((unsigned long)&intregs->tbt);
+ fword("encode-int");
+ fword("encode+");
+ push_str("address");
+ fword("property");
+
+ fword("finish-device");
+}
+
+/* SMP CPU boot structure */
+struct smp_cfg {
+ uint32_t smp_ctx;
+ uint32_t smp_ctxtbl;
+ uint32_t smp_entry;
+ uint32_t valid;
+};
+
+static struct smp_cfg *smp_header;
+
+int
+start_cpu(unsigned int pc, unsigned int context_ptr, unsigned int context, int cpu)
+{
+ if (!cpu)
+ return -1;
+
+ cpu &= 7;
+
+ smp_header->smp_entry = pc;
+ smp_header->smp_ctxtbl = context_ptr;
+ smp_header->smp_ctx = context;
+ smp_header->valid = cpu;
+
+ intregs->cpu_intregs[cpu].set = SUN4M_SOFT_INT(14);
+
+ return 0;
+}
+
+static void
+ob_smp_init(unsigned long mem_size)
+{
+ // See arch/sparc32/entry.S for memory layout
+ smp_header = (struct smp_cfg *)ofmem_map_io((uint64_t)(mem_size - 0x100),
+ sizeof(struct smp_cfg));
+}
+
+static void
+ob_obio_open(__attribute__((unused))int *idx)
+{
+ int ret=1;
+ RET ( -ret );
+}
+
+static void
+ob_obio_close(__attribute__((unused))int *idx)
+{
+ selfword("close-deblocker");
+}
+
+static void
+ob_obio_initialize(__attribute__((unused))int *idx)
+{
+ push_str("/");
+ fword("find-device");
+ fword("new-device");
+
+ push_str("obio");
+ fword("device-name");
+
+ push_str("hierarchical");
+ fword("device-type");
+
+ PUSH(2);
+ fword("encode-int");
+ push_str("#address-cells");
+ fword("property");
+
+ PUSH(1);
+ fword("encode-int");
+ push_str("#size-cells");
+ fword("property");
+
+ fword("finish-device");
+}
+
+static void
+ob_set_obio_ranges(uint64_t base)
+{
+ push_str("/obio");
+ fword("find-device");
+ PUSH(0);
+ fword("encode-int");
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(base >> 32);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(base & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(SLAVIO_SIZE);
+ fword("encode-int");
+ fword("encode+");
+ push_str("ranges");
+ fword("property");
+}
+
+static void
+ob_obio_decodeunit(__attribute__((unused)) int *idx)
+{
+ fword("decode-unit-sbus");
+}
+
+
+static void
+ob_obio_encodeunit(__attribute__((unused)) int *idx)
+{
+ fword("encode-unit-sbus");
+}
+
+NODE_METHODS(ob_obio) = {
+ { NULL, ob_obio_initialize },
+ { "open", ob_obio_open },
+ { "close", ob_obio_close },
+ { "encode-unit", ob_obio_encodeunit },
+ { "decode-unit", ob_obio_decodeunit },
+};
+
+
+int
+ob_obio_init(uint64_t slavio_base, unsigned long fd_offset,
+ unsigned long counter_offset, unsigned long intr_offset,
+ int intr_ncpu, unsigned long aux1_offset, unsigned long aux2_offset,
+ unsigned long mem_size)
+{
+
+ // All devices were integrated to NCR89C105, see
+ // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+
+ //printk("Initializing OBIO devices...\n");
+#if 0 // XXX
+ REGISTER_NAMED_NODE(ob_obio, "/obio");
+ device_end();
+#endif
+ ob_set_obio_ranges(slavio_base);
+
+ // Zilog Z8530 serial ports, see http://www.zilog.com
+ // Must be before zs@0,0 or Linux won't boot
+ ob_zs_init(slavio_base, SLAVIO_ZS1, ZS_INTR, 0, 0);
+
+ ob_zs_init(slavio_base, SLAVIO_ZS, ZS_INTR, 1, 1);
+
+ // M48T08 NVRAM, see http://www.st.com
+ ob_nvram_init(slavio_base, SLAVIO_NVRAM);
+
+ // 82078 FDC
+ if (fd_offset != (unsigned long) -1)
+ ob_fd_init(slavio_base, fd_offset, FD_INTR);
+
+ ob_auxio_init(slavio_base, aux1_offset);
+
+ if (aux2_offset != (unsigned long) -1)
+ ob_aux2_reset_init(slavio_base, aux2_offset, AUXIO2_INTR);
+
+ ob_counter_init(slavio_base, counter_offset, intr_ncpu);
+
+ ob_interrupt_init(slavio_base, intr_offset, intr_ncpu);
+
+ ob_smp_init(mem_size);
+
+ return 0;
+}
diff --git a/qemu/roms/openbios/drivers/obio.h b/qemu/roms/openbios/drivers/obio.h
new file mode 100644
index 000000000..49c3040c4
--- /dev/null
+++ b/qemu/roms/openbios/drivers/obio.h
@@ -0,0 +1,165 @@
+/* Addresses, interrupt numbers, register sizes */
+
+#define SLAVIO_ZS 0x00000000ULL
+#define SLAVIO_ZS1 0x00100000ULL
+#define ZS_INTR 0x2c
+
+#define SLAVIO_NVRAM 0x00200000ULL
+#define NVRAM_SIZE 0x2000
+#define NVRAM_IDPROM 0x1fd8
+
+#define SLAVIO_FD 0x00400000ULL
+#define FD_REGS 15
+#define FD_INTR 0x2b
+
+#define SLAVIO_SCONFIG 0x00800000ULL
+#define SCONFIG_REGS 1
+
+#define AUXIO_REGS 1
+
+#define AUXIO2_REGS 1
+#define AUXIO2_INTR 0x22
+
+#define SLAVIO_COUNTER 0x00d00000ULL
+#define COUNTER_REGS 0x10
+
+#define SLAVIO_INTERRUPT 0x00e00000ULL
+#define INTERRUPT_REGS 0x10
+
+#define SLAVIO_RESET 0x00f00000ULL
+#define RESET_REGS 1
+
+#define ECC_BASE 0xf00000000ULL
+#define ECC_SIZE 0x20
+
+#define SLAVIO_SIZE 0x01000000
+
+#define SUN4M_NCPUS 16
+
+#define CFG_ADDR 0xd00000510ULL
+#define CFG_SIZE 3
+
+/* linux/include/asm-sparc/timer.h */
+
+/* A sun4m has two blocks of registers which are probably of the same
+ * structure. LSI Logic's L64851 is told to _decrement_ from the limit
+ * value. Aurora behaves similarly but its limit value is compacted in
+ * other fashion (it's wider). Documented fields are defined here.
+ */
+
+/* As with the interrupt register, we have two classes of timer registers
+ * which are per-cpu and master. Per-cpu timers only hit that cpu and are
+ * only level 14 ticks, master timer hits all cpus and is level 10.
+ */
+
+#define SUN4M_PRM_CNT_L 0x80000000
+#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00
+
+struct sun4m_timer_percpu_info {
+ __volatile__ unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */
+ __volatile__ unsigned int l14_cur_count;
+
+ /* This register appears to be write only and/or inaccessible
+ * on Uni-Processor sun4m machines.
+ */
+ __volatile__ unsigned int l14_limit_noclear; /* Data access error is here */
+
+ __volatile__ unsigned int cntrl; /* =1 after POST on Aurora */
+ __volatile__ unsigned char space[PAGE_SIZE - 16];
+};
+
+struct sun4m_timer_regs {
+ struct sun4m_timer_percpu_info cpu_timers[SUN4M_NCPUS];
+ volatile unsigned int l10_timer_limit;
+ volatile unsigned int l10_cur_count;
+
+ /* Again, this appears to be write only and/or inaccessible
+ * on uni-processor sun4m machines.
+ */
+ volatile unsigned int l10_limit_noclear;
+
+ /* This register too, it must be magic. */
+ volatile unsigned int foobar;
+
+ volatile unsigned int cfg; /* equals zero at boot time... */
+};
+
+/*
+ * Registers of hardware timer in sun4m.
+ */
+struct sun4m_timer_percpu {
+ volatile unsigned int l14_timer_limit; /* Initial value is 0x009c4000 = 10ms period*/
+ volatile unsigned int l14_cur_count;
+};
+
+struct sun4m_timer_global {
+ volatile unsigned int l10_timer_limit;
+ volatile unsigned int l10_cur_count;
+};
+
+/* linux/include/asm-sparc/irq.h */
+
+/* These registers are used for sending/receiving irqs from/to
+ * different cpu's.
+ */
+struct sun4m_intreg_percpu {
+ unsigned int tbt; /* Interrupts still pending for this cpu. */
+
+ /* These next two registers are WRITE-ONLY and are only
+ * "on bit" sensitive, "off bits" written have NO affect.
+ */
+ unsigned int clear; /* Clear this cpus irqs here. */
+ unsigned int set; /* Set this cpus irqs here. */
+ unsigned char space[PAGE_SIZE - 12];
+};
+
+/*
+ * djhr
+ * Actually the clear and set fields in this struct are misleading..
+ * according to the SLAVIO manual (and the same applies for the SEC)
+ * the clear field clears bits in the mask which will ENABLE that IRQ
+ * the set field sets bits in the mask to DISABLE the IRQ.
+ *
+ * Also the undirected_xx address in the SLAVIO is defined as
+ * RESERVED and write only..
+ *
+ * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
+ * sun4m machines, for MP the layout makes more sense.
+ */
+struct sun4m_intregs {
+ struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS];
+ unsigned int tbt; /* IRQ's that are still pending. */
+ unsigned int irqs; /* Master IRQ bits. */
+
+ /* Again, like the above, two these registers are WRITE-ONLY. */
+ unsigned int clear; /* Clear master IRQ's by setting bits here. */
+ unsigned int set; /* Set master IRQ's by setting bits here. */
+
+ /* This register is both READ and WRITE. */
+ unsigned int undirected_target; /* Which cpu gets undirected irqs. */
+};
+
+/* Dave Redman (djhr@tadpole.co.uk)
+ * The sun4m interrupt registers.
+ */
+#define SUN4M_INT_ENABLE 0x80000000
+#define SUN4M_INT_E14 0x00000080
+#define SUN4M_INT_E10 0x00080000
+
+#define SUN4M_HARD_INT(x) (0x000000001 << (x))
+#define SUN4M_SOFT_INT(x) (0x000010000 << (x))
+
+#define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */
+#define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */
+#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */
+#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */
+#define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */
+#define SUN4M_INT_MODULE 0x00200000 /* module interrupt */
+#define SUN4M_INT_VIDEO 0x00100000 /* onboard video */
+#define SUN4M_INT_REALTIME 0x00080000 /* system timer */
+#define SUN4M_INT_SCSI 0x00040000 /* onboard scsi */
+#define SUN4M_INT_AUDIO 0x00020000 /* audio/isdn */
+#define SUN4M_INT_ETHERNET 0x00010000 /* onboard ethernet */
+#define SUN4M_INT_SERIAL 0x00008000 /* serial ports */
+#define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */
+#define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */
diff --git a/qemu/roms/openbios/drivers/pc_kbd.c b/qemu/roms/openbios/drivers/pc_kbd.c
new file mode 100644
index 000000000..49218f82e
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pc_kbd.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2003, 2004 Stefan Reinauer
+ *
+ * See the file "COPYING" for further information about
+ * the copyright and warranty status of this work.
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "drivers/drivers.h"
+#include "libc/vsprintf.h"
+
+/* ******************************************************************
+ * simple polling video/keyboard console functions
+ * ****************************************************************** */
+
+#define SER_SIZE 8
+
+/*
+ * keyboard driver
+ */
+
+static const char normal[] = {
+ 0x0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-',
+ '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o',
+ 'p', '[', ']', 0xa, 0x0, 'a', 's', 'd', 'f', 'g', 'h', 'j',
+ 'k', 'l', ';', 0x27, 0x60, 0x0, 0x5c, 'z', 'x', 'c', 'v', 'b',
+ 'n', 'm', ',', '.', '/', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '0', 0x7f
+};
+
+static const char shifted[] = {
+ 0x0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_',
+ '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O',
+ 'P', '{', '}', 0xa, 0x0, 'A', 'S', 'D', 'F', 'G', 'H', 'J',
+ 'K', 'L', ':', 0x22, '~', 0x0, '|', 'Z', 'X', 'C', 'V', 'B',
+ 'N', 'M', '<', '>', '?', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '7', '8',
+ '9', 0x0, '4', '5', '6', 0x0, '1', '2', '3', '0', 0x7f
+};
+
+static int key_ext;
+static int key_lshift = 0, key_rshift = 0, key_caps = 0;
+
+static char last_key;
+
+static void pc_kbd_cmd(unsigned char cmd, unsigned char val)
+{
+ outb(cmd, 0x60);
+ /* wait until keyboard controller accepts cmds: */
+ while (inb(0x64) & 2);
+ outb(val, 0x60);
+ while (inb(0x64) & 2);
+}
+
+static void pc_kbd_controller_cmd(unsigned char cmd, unsigned char val)
+{
+ outb(cmd, 0x64);
+ /* wait until keyboard controller accepts cmds: */
+ while (inb(0x64) & 2);
+ outb(val, 0x60);
+ while (inb(0x64) & 2);
+}
+
+static char pc_kbd_poll(void)
+{
+ unsigned int c;
+ if (inb(0x64) & 1) {
+ c = inb(0x60);
+ switch (c) {
+ case 0xe0:
+ key_ext = 1;
+ return 0;
+ case 0x2a:
+ key_lshift = 1;
+ return 0;
+ case 0x36:
+ key_rshift = 1;
+ return 0;
+ case 0xaa:
+ key_lshift = 0;
+ return 0;
+ case 0xb6:
+ key_rshift = 0;
+ return 0;
+ case 0x3a:
+ if (key_caps) {
+ key_caps = 0;
+ pc_kbd_cmd(0xed, 0);
+ } else {
+ key_caps = 1;
+ pc_kbd_cmd(0xed, 4); /* set caps led */
+ }
+ return 0;
+ }
+
+ if (key_ext) {
+ // void printk(const char *format, ...);
+ printk("extended keycode: %x\n", c);
+
+ key_ext = 0;
+ return 0;
+ }
+
+ if (c & 0x80) /* unhandled key release */
+ return 0;
+
+ if (key_lshift || key_rshift)
+ return key_caps ? normal[c] : shifted[c];
+ else
+ return key_caps ? shifted[c] : normal[c];
+ }
+ return 0;
+}
+
+int pc_kbd_dataready(void)
+{
+ if (last_key)
+ return 1;
+
+ last_key = pc_kbd_poll();
+
+ return (last_key != 0);
+}
+
+unsigned char pc_kbd_readdata(void)
+{
+ char tmp;
+ while (!pc_kbd_dataready());
+ tmp = last_key;
+ last_key = 0;
+ return tmp;
+}
+
+/* ( addr len -- actual ) */
+static void
+pc_kbd_read(void)
+{
+ unsigned char *addr;
+ int len;
+
+ len = POP();
+ addr = (unsigned char *)POP();
+
+ if (len != 1)
+ printk("pc_kbd_read: bad len, addr %lx len %x\n", (unsigned long)addr, len);
+
+ if (pc_kbd_dataready()) {
+ *addr = pc_kbd_readdata();
+ PUSH(1);
+ } else {
+ PUSH(0);
+ }
+}
+
+static void
+pc_kbd_close(void)
+{
+}
+
+static void
+pc_kbd_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 );
+}
+
+DECLARE_UNNAMED_NODE(pc_kbd, INSTALL_OPEN, sizeof(unsigned long));
+
+NODE_METHODS(pc_kbd) = {
+ { "open", pc_kbd_open },
+ { "close", pc_kbd_close },
+ { "read", pc_kbd_read },
+};
+
+void
+ob_pc_kbd_init(const char *path, const char *dev_name, uint64_t base,
+ uint64_t offset, int intr)
+{
+ phandle_t chosen, aliases;
+ char nodebuff[128];
+
+ snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name);
+ REGISTER_NAMED_NODE(pc_kbd, nodebuff);
+
+ push_str(nodebuff);
+ fword("find-device");
+
+ push_str(dev_name);
+ fword("device-name");
+
+ push_str("serial");
+ fword("device-type");
+
+ PUSH(-1);
+ fword("encode-int");
+ push_str("keyboard");
+ fword("property");
+
+ PUSH((base + offset) >> 32);
+ fword("encode-int");
+ PUSH((base + offset) & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(SER_SIZE);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ PUSH(offset);
+ fword("encode-int");
+ push_str("address");
+ fword("property");
+
+ chosen = find_dev("/chosen");
+ push_str(nodebuff);
+ fword("open-dev");
+ set_int_property(chosen, "keyboard", POP());
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1);
+
+ pc_kbd_controller_cmd(0x60, 0x40); // Write mode command, translated mode
+}
diff --git a/qemu/roms/openbios/drivers/pc_serial.c b/qemu/roms/openbios/drivers/pc_serial.c
new file mode 100644
index 000000000..a638e1f99
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pc_serial.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2003, 2004 Stefan Reinauer
+ *
+ * See the file "COPYING" for further information about
+ * the copyright and warranty status of this work.
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "drivers/drivers.h"
+#include "libc/vsprintf.h"
+
+/* ******************************************************************
+ * serial console functions
+ * ****************************************************************** */
+
+#define SER_SIZE 8
+
+#define RBR(x) x==2?0x2f8:0x3f8
+#define THR(x) x==2?0x2f8:0x3f8
+#define IER(x) x==2?0x2f9:0x3f9
+#define IIR(x) x==2?0x2fa:0x3fa
+#define LCR(x) x==2?0x2fb:0x3fb
+#define MCR(x) x==2?0x2fc:0x3fc
+#define LSR(x) x==2?0x2fd:0x3fd
+#define MSR(x) x==2?0x2fe:0x3fe
+#define SCR(x) x==2?0x2ff:0x3ff
+#define DLL(x) x==2?0x2f8:0x3f8
+#define DLM(x) x==2?0x2f9:0x3f9
+
+int uart_charav(int port)
+{
+ return ((inb(LSR(port)) & 1) != 0);
+}
+
+char uart_getchar(int port)
+{
+ while (!uart_charav(port));
+ return ((char) inb(RBR(port)) & 0177);
+}
+
+static void uart_port_putchar(int port, unsigned char c)
+{
+ if (c == '\n')
+ uart_port_putchar(port, '\r');
+ while (!(inb(LSR(port)) & 0x20));
+ outb(c, THR(port));
+}
+
+static void uart_init_line(int port, unsigned long baud)
+{
+ int i, baudconst;
+
+ switch (baud) {
+ case 115200:
+ baudconst = 1;
+ break;
+ case 57600:
+ baudconst = 2;
+ break;
+ case 38400:
+ baudconst = 3;
+ break;
+ case 19200:
+ baudconst = 6;
+ break;
+ case 9600:
+ default:
+ baudconst = 12;
+ break;
+ }
+
+ outb(0x87, LCR(port));
+ outb(0x00, DLM(port));
+ outb(baudconst, DLL(port));
+ outb(0x07, LCR(port));
+ outb(0x0f, MCR(port));
+
+ for (i = 10; i > 0; i--) {
+ if (inb(LSR(port)) == (unsigned int) 0)
+ break;
+ inb(RBR(port));
+ }
+}
+
+#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
+int uart_init(int port, unsigned long speed)
+{
+ uart_init_line(port, speed);
+ return -1;
+}
+
+void uart_putchar(int c)
+{
+ uart_port_putchar(CONFIG_SERIAL_PORT, (unsigned char) (c & 0xff));
+}
+#endif
+
+/* ( addr len -- actual ) */
+static void
+pc_serial_read(unsigned long *address)
+{
+ char *addr;
+ int len;
+
+ len = POP();
+ addr = (char *)POP();
+
+ if (len != 1)
+ printk("pc_serial_read: bad len, addr %lx len %x\n", (unsigned long)addr, len);
+
+ if (uart_charav(*address)) {
+ *addr = (char)uart_getchar(*address);
+ PUSH(1);
+ } else {
+ PUSH(0);
+ }
+}
+
+/* ( addr len -- actual ) */
+static void
+pc_serial_write(unsigned long *address)
+{
+ unsigned char *addr;
+ int i, len;
+
+ len = POP();
+ addr = (unsigned char *)POP();
+
+ for (i = 0; i < len; i++) {
+ uart_port_putchar(*address, addr[i]);
+ }
+ PUSH(len);
+}
+
+static void
+pc_serial_close(void)
+{
+}
+
+static void
+pc_serial_open(unsigned long *address)
+{
+ 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 },
+ { "write", pc_serial_write },
+};
+
+void
+ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base,
+ uint64_t offset, int intr)
+{
+ phandle_t aliases;
+ char nodebuff[128];
+
+ snprintf(nodebuff, sizeof(nodebuff), "%s/%s", path, dev_name);
+ REGISTER_NAMED_NODE(pc_serial, nodebuff);
+
+ push_str(nodebuff);
+ fword("find-device");
+
+ PUSH(offset);
+ PUSH(find_package_method("init", get_cur_dev()));
+ fword("execute");
+
+ push_str("serial");
+ fword("device-type");
+
+ PUSH((base + offset) >> 32);
+ fword("encode-int");
+ PUSH((base + offset) & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(SER_SIZE);
+ fword("encode-int");
+ fword("encode+");
+ 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);
+#endif
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "ttya", nodebuff, strlen(nodebuff) + 1);
+}
diff --git a/qemu/roms/openbios/drivers/pci.c b/qemu/roms/openbios/drivers/pci.c
new file mode 100644
index 000000000..366f4a17f
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pci.c
@@ -0,0 +1,1569 @@
+/*
+ * OpenBIOS pci driver
+ *
+ * This driver is compliant to the
+ * PCI bus binding to IEEE 1275-1994 Rev 2.1
+ *
+ * (C) 2004 Stefan Reinauer <stepan@openbios.org>
+ * (C) 2005 Ed Schouten <ed@fxq.nl>
+ *
+ * Some parts from OpenHackWare-0.4, Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "libopenbios/ofmem.h"
+#include "kernel/kernel.h"
+#include "drivers/pci.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+
+#include "drivers/drivers.h"
+#include "drivers/vga.h"
+#include "packages/video.h"
+#include "libopenbios/video.h"
+#include "timer.h"
+#include "pci.h"
+#include "pci_database.h"
+#ifdef CONFIG_DRIVER_MACIO
+#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__)
+#else
+# define PCI_DPRINTF(format, ...) do { } while (0)
+#endif
+
+#define set_bool_property(ph, name) set_property(ph, name, NULL, 0);
+
+/* DECLARE data structures for the nodes. */
+
+DECLARE_UNNAMED_NODE( ob_pci_bus_node, INSTALL_OPEN, 2*sizeof(int) );
+DECLARE_UNNAMED_NODE( ob_pci_simple_node, INSTALL_OPEN, 2*sizeof(int) );
+DECLARE_UNNAMED_NODE( ob_pci_empty_node, 0, 2*sizeof(int) );
+
+const pci_arch_t *arch;
+
+#define IS_NOT_RELOCATABLE 0x80000000
+#define IS_PREFETCHABLE 0x40000000
+#define IS_ALIASED 0x20000000
+
+enum {
+ CONFIGURATION_SPACE = 0,
+ IO_SPACE = 1,
+ MEMORY_SPACE_32 = 2,
+ MEMORY_SPACE_64 = 3,
+};
+
+static int encode_int32_cells(int num_cells, u32 *prop, ucell val)
+{
+ int i = 0;
+
+ /* hi ... lo */
+ for (i=0; i < num_cells; ++i) {
+ prop[num_cells - i - 1] = val;
+ val >>= 16;
+ val >>= 16;
+ }
+
+ return num_cells;
+}
+
+static inline int pci_encode_phys_addr(u32 *phys, int flags, int space_code,
+ pci_addr dev, uint8_t reg, uint64_t addr)
+{
+
+ /* phys.hi */
+
+ phys[0] = flags | (space_code << 24) | dev | reg;
+
+ /* phys.mid */
+
+ phys[1] = addr >> 32;
+
+ /* phys.lo */
+
+ phys[2] = addr;
+
+ return 3;
+}
+
+static inline int pci_encode_size(u32 *prop, uint64_t size)
+{
+ return encode_int32_cells(2, prop, size);
+}
+
+static int host_address_cells(void)
+{
+ return get_int_property(find_dev("/"), "#address-cells", NULL);
+}
+
+static int host_encode_phys_addr(u32 *prop, ucell addr)
+{
+ return encode_int32_cells(host_address_cells(), prop, addr);
+}
+
+static int host_size_cells(void)
+{
+ return get_int_property(find_dev("/"), "#size-cells", NULL);
+}
+
+/*
+static int parent_address_cells(void)
+{
+ phandle_t parent_ph = ih_to_phandle(my_parent());
+ return get_int_property(parent_ph, "#address-cells", NULL);
+}
+
+static int parent_size_cells(void)
+{
+ phandle_t parent_ph = ih_to_phandle(my_parent());
+ return get_int_property(parent_ph, "#size-cells", NULL);
+}
+*/
+
+#if defined(CONFIG_DEBUG_PCI)
+static void dump_reg_property(const char* description, int nreg, u32 *reg)
+{
+ int i;
+ printk("%s reg", description);
+ for (i=0; i < nreg; ++i) {
+ printk(" %08X", reg[i]);
+ }
+ printk("\n");
+}
+#endif
+
+static unsigned long pci_bus_addr_to_host_addr(uint32_t ba)
+{
+ return arch->host_pci_base + (unsigned long)ba;
+}
+
+static void
+ob_pci_open(int *idx)
+{
+ int ret=1;
+ RET ( -ret );
+}
+
+static void
+ob_pci_close(int *idx)
+{
+}
+
+static void
+ob_pci_initialize(int *idx)
+{
+}
+
+/* ( str len -- phys.lo phys.mid phys.hi ) */
+
+static void
+ob_pci_decode_unit(int *idx)
+{
+ ucell hi, mid, lo;
+ const char *arg = pop_fstr_copy();
+ int dev, fn, reg, ss, n, p, t;
+ int bus = 0; /* no information */
+ char *ptr;
+
+ PCI_DPRINTF("ob_pci_decode_unit idx=%p\n", idx);
+
+ fn = 0;
+ reg = 0;
+ n = 0;
+ p = 0;
+ t = 0;
+
+ ptr = (char*)arg;
+ if (*ptr == 'n') {
+ n = IS_NOT_RELOCATABLE;
+ ptr++;
+ }
+ if (*ptr == 'i') {
+ ss = IO_SPACE;
+ ptr++;
+ if (*ptr == 't') {
+ t = IS_ALIASED;
+ ptr++;
+ }
+
+ /* DD,F,RR,NNNNNNNN */
+
+ dev = strtol(ptr, &ptr, 16);
+ ptr++;
+ fn = strtol(ptr, &ptr, 16);
+ ptr++;
+ reg = strtol(ptr, &ptr, 16);
+ ptr++;
+ lo = strtol(ptr, &ptr, 16);
+ mid = 0;
+
+ } else if (*ptr == 'm') {
+ ss = MEMORY_SPACE_32;
+ ptr++;
+ if (*ptr == 't') {
+ t = IS_ALIASED;
+ ptr++;
+ }
+ if (*ptr == 'p') {
+ p = IS_PREFETCHABLE;
+ ptr++;
+ }
+
+ /* DD,F,RR,NNNNNNNN */
+
+ dev = strtol(ptr, &ptr, 16);
+ ptr++;
+ fn = strtol(ptr, &ptr, 16);
+ ptr++;
+ reg = strtol(ptr, &ptr, 16);
+ ptr++;
+ lo = strtol(ptr, &ptr, 16);
+ mid = 0;
+
+ } else if (*ptr == 'x') {
+ unsigned long long addr64;
+ ss = MEMORY_SPACE_64;
+ ptr++;
+ if (*ptr == 'p') {
+ p = IS_PREFETCHABLE;
+ ptr++;
+ }
+
+ /* DD,F,RR,NNNNNNNNNNNNNNNN */
+
+ dev = strtol(ptr, &ptr, 16);
+ ptr++;
+ fn = strtol(ptr, &ptr, 16);
+ ptr++;
+ reg = strtol(ptr, &ptr, 16);
+ ptr++;
+ addr64 = strtoll(ptr, &ptr, 16);
+ lo = (ucell)addr64;
+ mid = addr64 >> 32;
+
+ } else {
+ ss = CONFIGURATION_SPACE;
+ /* "DD" or "DD,FF" */
+ dev = strtol(ptr, &ptr, 16);
+ if (*ptr == ',') {
+ ptr++;
+ fn = strtol(ptr, NULL, 16);
+ }
+ lo = 0;
+ mid = 0;
+ }
+ free((char*)arg);
+
+ hi = n | p | t | (ss << 24) | (bus << 16) | (dev << 11) | (fn << 8) | reg;
+
+ PUSH(lo);
+ PUSH(mid);
+ PUSH(hi);
+
+ PCI_DPRINTF("ob_pci_decode_unit idx=%p addr="
+ FMT_ucellx " " FMT_ucellx " " FMT_ucellx "\n",
+ idx, lo, mid, hi);
+}
+
+/* ( phys.lo phy.mid phys.hi -- str len ) */
+
+static void
+ob_pci_encode_unit(int *idx)
+{
+ char buf[28];
+ cell hi = POP();
+ cell mid = POP();
+ cell lo = POP();
+ int n, p, t, ss, dev, fn, reg;
+
+ n = hi & IS_NOT_RELOCATABLE;
+ p = hi & IS_PREFETCHABLE;
+ t = hi & IS_ALIASED;
+ ss = (hi >> 24) & 0x03;
+
+ dev = (hi >> 11) & 0x1F;
+ fn = (hi >> 8) & 0x07;
+ reg = hi & 0xFF;
+
+ switch(ss) {
+ case CONFIGURATION_SPACE:
+
+ if (fn == 0) /* DD */
+ snprintf(buf, sizeof(buf), "%x", dev);
+ else /* DD,F */
+ snprintf(buf, sizeof(buf), "%x,%x", dev, fn);
+ break;
+
+ case IO_SPACE:
+
+ /* [n]i[t]DD,F,RR,NNNNNNNN */
+ snprintf(buf, sizeof(buf), "%si%s%x,%x,%x," FMT_ucellx,
+ n ? "n" : "", /* relocatable */
+ t ? "t" : "", /* aliased */
+ dev, fn, reg, t ? lo & 0x03FF : lo);
+ break;
+
+ case MEMORY_SPACE_32:
+
+ /* [n]m[t][p]DD,F,RR,NNNNNNNN */
+ snprintf(buf, sizeof(buf), "%sm%s%s%x,%x,%x," FMT_ucellx,
+ n ? "n" : "", /* relocatable */
+ t ? "t" : "", /* aliased */
+ p ? "p" : "", /* prefetchable */
+ dev, fn, reg, lo );
+ break;
+
+ case MEMORY_SPACE_64:
+
+ /* [n]x[p]DD,F,RR,NNNNNNNNNNNNNNNN */
+ snprintf(buf, sizeof(buf), "%sx%s%x,%x,%x,%llx",
+ n ? "n" : "", /* relocatable */
+ p ? "p" : "", /* prefetchable */
+ dev, fn, reg, ((long long)mid << 32) | (long long)lo);
+ break;
+ }
+ push_str(buf);
+
+ PCI_DPRINTF("ob_pci_encode_unit space=%d dev=%d fn=%d buf=%s\n",
+ ss, dev, fn, buf);
+}
+
+/* ( pci-addr.lo pci-addr.hi size -- virt ) */
+
+static void
+ob_pci_map_in(int *idx)
+{
+ phys_addr_t phys;
+ uint32_t ba;
+ ucell size, virt;
+
+ PCI_DPRINTF("ob_pci_bar_map_in idx=%p\n", idx);
+
+ size = POP();
+ POP();
+ ba = POP();
+
+ phys = pci_bus_addr_to_host_addr(ba);
+
+#if defined(CONFIG_OFMEM)
+ ofmem_claim_phys(phys, size, 0);
+
+#if defined(CONFIG_PPC)
+ /* For some reason PPC gets upset when virt != phys for map-in... */
+ virt = ofmem_claim_virt(phys, size, 0);
+#else
+ virt = ofmem_claim_virt(-1, size, size);
+#endif
+
+ ofmem_map(phys, virt, size, ofmem_arch_io_translation_mode(phys));
+
+#else
+ virt = size; /* Keep compiler quiet */
+ virt = phys;
+#endif
+
+ PUSH(virt);
+}
+
+NODE_METHODS(ob_pci_bus_node) = {
+ { NULL, ob_pci_initialize },
+ { "open", ob_pci_open },
+ { "close", ob_pci_close },
+ { "decode-unit", ob_pci_decode_unit },
+ { "encode-unit", ob_pci_encode_unit },
+ { "pci-map-in", ob_pci_map_in },
+};
+
+NODE_METHODS(ob_pci_simple_node) = {
+ { NULL, ob_pci_initialize },
+ { "open", ob_pci_open },
+ { "close", ob_pci_close },
+};
+
+NODE_METHODS(ob_pci_empty_node) = {
+ { NULL, ob_pci_initialize }
+};
+
+static void pci_set_bus_range(const pci_config_t *config)
+{
+ phandle_t dev = find_dev(config->path);
+ u32 props[2];
+
+ props[0] = config->secondary_bus;
+ props[1] = config->subordinate_bus;
+
+ PCI_DPRINTF("setting bus range for %s PCI device, "
+ "package handle " FMT_ucellx " "
+ "bus primary=%d secondary=%d subordinate=%d\n",
+ config->path,
+ dev,
+ config->primary_bus,
+ config->secondary_bus,
+ config->subordinate_bus);
+
+
+ set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0]));
+}
+
+static void pci_host_set_reg(phandle_t phandle)
+{
+ phandle_t dev = phandle;
+
+ /* at most 2 integers for address and size */
+ u32 props[4];
+ int ncells = 0;
+
+ ncells += encode_int32_cells(host_address_cells(), props + ncells,
+ arch->cfg_base);
+
+ ncells += encode_int32_cells(host_size_cells(), props + ncells,
+ arch->cfg_len);
+
+ set_property(dev, "reg", (char *)props, ncells * sizeof(props[0]));
+
+#if defined(CONFIG_DEBUG_PCI)
+ dump_reg_property("pci_host_set_reg", 4, props);
+#endif
+}
+
+/* child-phys : parent-phys : size */
+/* 3 cells for PCI : 2 cells for 64bit parent : 2 cells for PCI */
+
+static void pci_host_set_ranges(const pci_config_t *config)
+{
+ phandle_t dev = get_cur_dev();
+ u32 props[32];
+ int ncells;
+
+ ncells = 0;
+ /* first encode PCI configuration space */
+ {
+ ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE,
+ config->dev, 0, 0);
+ ncells += host_encode_phys_addr(props + ncells, arch->cfg_addr);
+ ncells += pci_encode_size(props + ncells, arch->cfg_len);
+ }
+
+ if (arch->io_base) {
+ ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE,
+ config->dev, 0, 0);
+ ncells += host_encode_phys_addr(props + ncells, arch->io_base);
+ ncells += pci_encode_size(props + ncells, arch->io_len);
+ }
+ if (arch->rbase) {
+ ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32,
+ config->dev, 0, 0);
+ ncells += host_encode_phys_addr(props + ncells, arch->rbase);
+ ncells += pci_encode_size(props + ncells, arch->rlen);
+ }
+ if (arch->pci_mem_base) {
+ ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32,
+ config->dev, 0, arch->pci_mem_base);
+ ncells += host_encode_phys_addr(props + ncells, arch->host_pci_base +
+ arch->pci_mem_base);
+ ncells += pci_encode_size(props + ncells, arch->mem_len);
+ }
+ set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0]));
+}
+
+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);
+
+ return 0;
+}
+
+static int sabre_configure(phandle_t dev)
+{
+ uint32_t props[28];
+
+ props[0] = 0xc0000000;
+ props[1] = 0x20000000;
+ set_property(dev, "virtual-dma", (char *)props, 2 * sizeof(props[0]));
+ props[0] = 1;
+ set_property(dev, "#virtual-dma-size-cells", (char *)props,
+ 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;
+ props[3] = 0x000007e5;
+ set_property(dev, "interrupts", (char *)props, 4 * sizeof(props[0]));
+ props[0] = 0x0000001f;
+ set_property(dev, "upa-portid", (char *)props, 1 * sizeof(props[0]));
+ return 0;
+}
+
+int sabre_config_cb(const pci_config_t *config)
+{
+ host_config_cb(config);
+
+ return sabre_configure(get_cur_dev());
+}
+
+int bridge_config_cb(const pci_config_t *config)
+{
+ phandle_t aliases;
+
+ aliases = find_dev("/aliases");
+ set_property(aliases, "bridge", config->path, strlen(config->path) + 1);
+
+ return 0;
+}
+
+int ide_config_cb2 (const pci_config_t *config)
+{
+ ob_ide_init(config->path,
+ config->assigned[0] & ~0x0000000F,
+ (config->assigned[1] & ~0x0000000F) + 2,
+ config->assigned[2] & ~0x0000000F,
+ (config->assigned[3] & ~0x0000000F) + 2);
+ return 0;
+}
+
+int eth_config_cb (const pci_config_t *config)
+{
+ phandle_t ph = get_cur_dev();
+
+ set_property(ph, "network-type", "ethernet", 9);
+ set_property(ph, "removable", "network", 8);
+ set_property(ph, "category", "net", 4);
+
+ return 0;
+}
+
+static inline void pci_decode_pci_addr(pci_addr addr, int *flags,
+ int *space_code, uint32_t *mask)
+{
+ *flags = 0;
+
+ if (addr & 0x01) {
+ *space_code = IO_SPACE;
+ *mask = 0x00000001;
+ } else {
+ if (addr & 0x04) {
+ *space_code = MEMORY_SPACE_64;
+ *flags |= IS_NOT_RELOCATABLE; /* XXX: why not relocatable? */
+ } else {
+ *space_code = MEMORY_SPACE_32;
+ }
+
+ if (addr & 0x08) {
+ *flags |= IS_PREFETCHABLE;
+ }
+
+ *mask = 0x0000000F;
+ }
+}
+
+/*
+ * "Designing PCI Cards and Drivers for Power Macintosh Computers", p. 454
+ *
+ * "AAPL,address" provides an array of 32-bit logical addresses
+ * Nth entry corresponding to Nth "assigned-address" base address entry.
+ */
+
+static void pci_set_AAPL_address(const pci_config_t *config)
+{
+ phandle_t dev = get_cur_dev();
+ cell props[7];
+ int ncells, i;
+
+ ncells = 0;
+ for (i = 0; i < 6; i++) {
+ if (!config->assigned[i] || !config->sizes[i])
+ continue;
+ props[ncells++] = config->assigned[i] & ~0x0000000F;
+ }
+ if (ncells)
+ set_property(dev, "AAPL,address", (char *)props,
+ ncells * sizeof(cell));
+}
+
+static void pci_set_assigned_addresses(phandle_t phandle,
+ const pci_config_t *config, int num_bars)
+{
+ phandle_t dev = phandle;
+ u32 props[32];
+ int ncells;
+ int i;
+ uint32_t mask;
+ int flags, space_code;
+
+ ncells = 0;
+ for (i = 0; i < num_bars; 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);
+
+ ncells += pci_encode_phys_addr(props + ncells,
+ flags, space_code, config->dev,
+ PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)),
+ config->assigned[i] & ~mask);
+
+ props[ncells++] = 0x00000000;
+ props[ncells++] = config->sizes[i];
+ }
+ if (ncells)
+ set_property(dev, "assigned-addresses", (char *)props,
+ ncells * sizeof(props[0]));
+}
+
+/* call after writing "reg" property to update config->path */
+static void ob_pci_reload_device_path(phandle_t phandle, pci_config_t *config)
+{
+ /* since "name" and "reg" are now assigned
+ we need to reload current node name */
+
+ PUSH(phandle);
+ fword("get-package-path");
+ char *new_path = pop_fstr_copy();
+ if (new_path) {
+ if (0 != strcmp(config->path, new_path)) {
+ PCI_DPRINTF("\n=== CHANGED === package path old=%s new=%s\n",
+ config->path, new_path);
+ strncpy(config->path, new_path, sizeof(config->path));
+ config->path[sizeof(config->path)-1] = '\0';
+ }
+ free(new_path);
+ } else {
+ PCI_DPRINTF("\n=== package path old=%s new=NULL\n", config->path);
+ }
+}
+
+static void pci_set_reg(phandle_t phandle,
+ pci_config_t *config, int num_bars)
+{
+ phandle_t dev = phandle;
+ u32 props[38];
+ int ncells;
+ int i;
+ uint32_t mask;
+ int space_code, flags;
+
+ ncells = 0;
+
+ /* first (addr, size) pair is the beginning of configuration address space */
+ ncells += pci_encode_phys_addr(props + ncells, 0, CONFIGURATION_SPACE,
+ config->dev, 0, 0);
+
+ ncells += pci_encode_size(props + ncells, 0);
+
+ for (i = 0; i < num_bars; i++) {
+ /* consider only bars with non-zero region size */
+ if (!config->sizes[i])
+ continue;
+
+ pci_decode_pci_addr(config->regions[i],
+ &flags, &space_code, &mask);
+
+ ncells += pci_encode_phys_addr(props + ncells,
+ flags, space_code, config->dev,
+ PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)),
+ config->regions[i] & ~mask);
+
+ /* set size */
+ ncells += pci_encode_size(props + ncells, config->sizes[i]);
+ }
+
+ set_property(dev, "reg", (char *)props, ncells * sizeof(props[0]));
+ ob_pci_reload_device_path(dev, config);
+
+#if defined(CONFIG_DEBUG_PCI)
+ dump_reg_property("pci_set_reg", ncells, props);
+#endif
+}
+
+
+static void pci_set_ranges(const pci_config_t *config)
+{
+ phandle_t dev = get_cur_dev();
+ u32 props[32];
+ int ncells;
+ int i;
+ uint32_t mask;
+ int flags;
+ int space_code;
+
+ ncells = 0;
+ for (i = 0; i < 6; i++) {
+ if (!config->assigned[i] || !config->sizes[i])
+ continue;
+
+ /* child address */
+
+ props[ncells++] = 0x00000000;
+
+ /* parent address */
+
+ pci_decode_pci_addr(config->assigned[i],
+ &flags, &space_code, &mask);
+ ncells += pci_encode_phys_addr(props + ncells, flags, space_code,
+ config->dev, 0x10 + i * 4,
+ config->assigned[i] & ~mask);
+
+ /* size */
+
+ props[ncells++] = config->sizes[i];
+ }
+ set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0]));
+}
+
+int macio_heathrow_config_cb (const pci_config_t *config)
+{
+ pci_set_ranges(config);
+
+#ifdef CONFIG_DRIVER_MACIO
+ ob_macio_heathrow_init(config->path, config->assigned[0] & ~0x0000000F);
+#endif
+ return 0;
+}
+
+int macio_keylargo_config_cb (const pci_config_t *config)
+{
+ pci_set_ranges(config);
+
+#ifdef CONFIG_DRIVER_MACIO
+ ob_macio_keylargo_init(config->path, config->assigned[0] & ~0x0000000F);
+#endif
+ return 0;
+}
+
+int vga_config_cb (const pci_config_t *config)
+{
+ unsigned long rom;
+ uint32_t rom_size, size;
+ phandle_t ph;
+
+ if (config->assigned[0] != 0x00000000) {
+ setup_video();
+
+ rom = pci_bus_addr_to_host_addr(config->assigned[1] & ~0x0000000F);
+ rom_size = config->sizes[1];
+
+ ph = get_cur_dev();
+
+ if (rom_size >= 8) {
+ const char *p;
+
+ p = (const char *)rom;
+ if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') {
+ size = *(uint32_t*)(p + 4);
+ set_property(ph, "driver,AAPL,MacOS,PowerPC", p + 8, size);
+ }
+ }
+
+ /* Currently we don't read FCode from the hardware but execute it directly */
+ feval("['] vga-driver-fcode 2 cells + 1 byte-load");
+
+#ifdef CONFIG_MOL
+ /* Install special words for Mac On Linux */
+ molvideo_init();
+#endif
+
+ }
+
+ return 0;
+}
+
+int ebus_config_cb(const pci_config_t *config)
+{
+#ifdef CONFIG_DRIVER_EBUS
+ phandle_t dev = get_cur_dev();
+ uint32_t props[12];
+ int ncells;
+ int i;
+ uint32_t mask;
+ int flags, space_code;
+
+ props[0] = 0x14;
+ props[1] = 0x3f8;
+ 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]));
+
+ /* 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]));
+
+ /* Build eeprom node */
+ fword("new-device");
+ PUSH(0x14);
+ fword("encode-int");
+ PUSH(0x2000);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(0x2000);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ push_str("mk48t59");
+ fword("model");
+
+ push_str("eeprom");
+ fword("device-name");
+ fword("finish-device");
+
+#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", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x3f8ULL, 0);
+#endif
+#ifdef CONFIG_DRIVER_PC_KBD
+ ob_pc_kbd_init(config->path, "kb_ps2", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x60ULL, 0);
+#endif
+#endif
+ return 0;
+}
+
+int i82378_config_cb(const pci_config_t *config)
+{
+#ifdef CONFIG_DRIVER_PC_SERIAL
+ ob_pc_serial_init(config->path, "serial", arch->io_base, 0x3f8ULL, 0);
+#endif
+#ifdef CONFIG_DRIVER_PC_KBD
+ ob_pc_kbd_init(config->path, "8042", arch->io_base, 0x60ULL, 0);
+#endif
+#ifdef CONFIG_DRIVER_IDE
+ ob_ide_init(config->path, 0x1f0, 0x3f6, 0x170, 0x376);
+#endif
+
+ 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)
+{
+ /* cannot use get_cur_dev() path resolution since "name" and "reg"
+ properties are being changed */
+ phandle_t dev=phandle;
+ int status,id;
+ uint16_t vendor_id, device_id;
+ uint8_t rev;
+ uint8_t class_prog;
+ uint32_t class_code;
+
+ vendor_id = pci_config_read16(addr, PCI_VENDOR_ID);
+ device_id = pci_config_read16(addr, PCI_DEVICE_ID);
+ rev = pci_config_read8(addr, PCI_REVISION_ID);
+ class_prog = pci_config_read8(addr, PCI_CLASS_PROG);
+ class_code = pci_config_read16(addr, PCI_CLASS_DEVICE);
+
+ if (pci_dev) {
+ /**/
+ if (pci_dev->name) {
+ push_str(pci_dev->name);
+ fword("encode-string");
+ push_str("name");
+ fword("property");
+ } else {
+ char path[256];
+ snprintf(path, sizeof(path),
+ "pci%x,%x", vendor_id, device_id);
+ push_str(path);
+ fword("encode-string");
+ push_str("name");
+ fword("property");
+ }
+ } else {
+ PCI_DPRINTF("*** missing pci_dev\n");
+ }
+
+ /* create properties as described in 2.5 */
+
+ set_int_property(dev, "vendor-id", vendor_id);
+ set_int_property(dev, "device-id", device_id);
+ set_int_property(dev, "revision-id", rev);
+ set_int_property(dev, "class-code", class_code << 8 | class_prog);
+
+ if (config->irq_pin) {
+ OLDWORLD(set_int_property(dev, "AAPL,interrupts",
+ config->irq_line));
+#if defined(CONFIG_SPARC64)
+ set_int_property(dev, "interrupts", config->irq_pin);
+#else
+ NEWWORLD(set_int_property(dev, "interrupts", config->irq_pin));
+#endif
+ }
+
+ set_int_property(dev, "min-grant", pci_config_read8(addr, PCI_MIN_GNT));
+ set_int_property(dev, "max-latency", pci_config_read8(addr, PCI_MAX_LAT));
+
+ status=pci_config_read16(addr, PCI_STATUS);
+
+ set_int_property(dev, "devsel-speed",
+ (status&PCI_STATUS_DEVSEL_MASK)>>10);
+
+ if(status&PCI_STATUS_FAST_BACK)
+ set_bool_property(dev, "fast-back-to-back");
+ if(status&PCI_STATUS_66MHZ)
+ set_bool_property(dev, "66mhz-capable");
+ if(status&PCI_STATUS_UDF)
+ set_bool_property(dev, "udf-supported");
+
+ id=pci_config_read16(addr, PCI_SUBSYSTEM_VENDOR_ID);
+ if(id)
+ set_int_property(dev, "subsystem-vendor-id", id);
+ id=pci_config_read16(addr, PCI_SUBSYSTEM_ID);
+ if(id)
+ set_int_property(dev, "subsystem-id", id);
+
+ set_int_property(dev, "cache-line-size",
+ pci_config_read16(addr, PCI_CACHE_LINE_SIZE));
+
+ if (pci_dev) {
+ if (pci_dev->type) {
+ push_str(pci_dev->type);
+ fword("encode-string");
+ push_str("device_type");
+ fword("property");
+ }
+ if (pci_dev->model) {
+ push_str(pci_dev->model);
+ fword("encode-string");
+ push_str("model");
+ fword("property");
+ }
+ if (pci_dev->compat)
+ set_property(dev, "compatible",
+ pci_dev->compat, pci_compat_len(pci_dev));
+
+ if (pci_dev->acells)
+ set_int_property(dev, "#address-cells",
+ pci_dev->acells);
+ if (pci_dev->scells)
+ set_int_property(dev, "#size-cells",
+ pci_dev->scells);
+ if (pci_dev->icells)
+ set_int_property(dev, "#interrupt-cells",
+ pci_dev->icells);
+ }
+
+ pci_set_assigned_addresses(phandle, config, num_bars);
+ OLDWORLD(pci_set_AAPL_address(config));
+
+ PCI_DPRINTF("\n");
+}
+
+#ifdef CONFIG_XBOX
+static char pci_xbox_blacklisted (int bus, int devnum, int fn)
+{
+ /*
+ * The Xbox MCPX chipset is a derivative of the nForce 1
+ * chipset. It almost has the same bus layout; some devices
+ * cannot be used, because they have been removed.
+ */
+
+ /*
+ * Devices 00:00.1 and 00:00.2 used to be memory controllers on
+ * the nForce chipset, but on the Xbox, using them will lockup
+ * the chipset.
+ */
+ if ((bus == 0) && (devnum == 0) && ((fn == 1) || (fn == 2)))
+ return 1;
+
+ /*
+ * Bus 1 only contains a VGA controller at 01:00.0. When you try
+ * to probe beyond that device, you only get garbage, which
+ * could cause lockups.
+ */
+ if ((bus == 1) && ((devnum != 0) || (fn != 0)))
+ return 1;
+
+ /*
+ * Bus 2 used to contain the AGP controller, but the Xbox MCPX
+ * doesn't have one. Probing it can cause lockups.
+ */
+ if (bus >= 2)
+ return 1;
+
+ /*
+ * The device is not blacklisted.
+ */
+ return 0;
+}
+#endif
+
+static void ob_pci_configure_bar(pci_addr addr, pci_config_t *config,
+ int reg, int config_addr,
+ uint32_t *p_omask,
+ unsigned long *mem_base,
+ unsigned long *io_base)
+{
+ uint32_t smask, amask, size, reloc, min_align;
+ unsigned long base;
+
+ config->assigned[reg] = 0x00000000;
+ config->sizes[reg] = 0x00000000;
+
+ if ((*p_omask & 0x0000000f) == 0x4) {
+ /* 64 bits memory mapping */
+ PCI_DPRINTF("Skipping 64 bit BARs for %s\n", config->path);
+ return;
+ }
+
+ config->regions[reg] = pci_config_read32(addr, config_addr);
+
+ /* get region size */
+
+ pci_config_write32(addr, config_addr, 0xffffffff);
+ smask = pci_config_read32(addr, config_addr);
+ if (smask == 0x00000000 || smask == 0xffffffff)
+ return;
+
+ if (smask & 0x00000001 && reg != 6) {
+ /* I/O space */
+ base = *io_base;
+ min_align = 1 << 7;
+ amask = 0x00000001;
+ } else {
+ /* Memory Space */
+ base = *mem_base;
+ min_align = 1 << 16;
+ amask = 0x0000000F;
+ if (reg == 6) {
+ smask |= 1; /* ROM */
+ }
+ }
+ *p_omask = smask & amask;
+ smask &= ~amask;
+ size = (~smask) + 1;
+ config->sizes[reg] = size;
+ reloc = base;
+ if (size < min_align)
+ size = min_align;
+ reloc = (reloc + size -1) & ~(size - 1);
+ if (*io_base == base) {
+ PCI_DPRINTF("changing io_base from 0x%lx to 0x%x\n",
+ *io_base, reloc + size);
+ *io_base = reloc + size;
+ } else {
+ PCI_DPRINTF("changing mem_base from 0x%lx to 0x%x\n",
+ *mem_base, reloc + size);
+ *mem_base = reloc + size;
+ }
+ PCI_DPRINTF("Configuring BARs for %s: reloc 0x%x omask 0x%x "
+ "io_base 0x%lx mem_base 0x%lx size 0x%x\n",
+ config->path, reloc, *p_omask, *io_base, *mem_base, size);
+ pci_config_write32(addr, config_addr, reloc | *p_omask);
+ config->assigned[reg] = reloc | *p_omask;
+}
+
+static void ob_pci_configure_irq(pci_addr addr, pci_config_t *config)
+{
+ uint8_t irq_pin, irq_line;
+
+ irq_pin = pci_config_read8(addr, PCI_INTERRUPT_PIN);
+ if (irq_pin) {
+ config->irq_pin = irq_pin;
+ irq_pin = (((config->dev >> 11) & 0x1F) + irq_pin - 1) & 3;
+ irq_line = arch->irqs[irq_pin];
+ pci_config_write8(addr, PCI_INTERRUPT_LINE, irq_line);
+ config->irq_line = irq_line;
+ } else
+ config->irq_line = -1;
+}
+
+static void
+ob_pci_configure(pci_addr addr, pci_config_t *config, int num_regs, int rom_bar,
+ unsigned long *mem_base, unsigned long *io_base)
+
+{
+ uint32_t omask;
+ uint16_t cmd;
+ int reg;
+ pci_addr config_addr;
+
+ ob_pci_configure_irq(addr, config);
+
+ omask = 0x00000000;
+ for (reg = 0; reg < num_regs; ++reg) {
+ config_addr = PCI_BASE_ADDR_0 + reg * 4;
+
+ ob_pci_configure_bar(addr, config, reg, config_addr,
+ &omask, mem_base,
+ io_base);
+ }
+
+ if (rom_bar) {
+ config_addr = rom_bar;
+ ob_pci_configure_bar(addr, config, reg, config_addr,
+ &omask, mem_base, io_base);
+ }
+ cmd = pci_config_read16(addr, PCI_COMMAND);
+ cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_config_write16(addr, PCI_COMMAND, cmd);
+}
+
+static void ob_configure_pci_device(const char* parent_path,
+ int *bus_num, unsigned long *mem_base, unsigned long *io_base,
+ int bus, int devnum, int fn, int *p_is_multi);
+
+static void ob_scan_pci_bus(int *bus_num, unsigned long *mem_base,
+ unsigned long *io_base, const char *path,
+ int bus)
+{
+ int devnum, fn, is_multi;
+
+ PCI_DPRINTF("\nScanning bus %d at %s...\n", bus, path);
+
+ for (devnum = 0; devnum < 32; devnum++) {
+ is_multi = 0;
+ for (fn = 0; fn==0 || (is_multi && fn<8); fn++) {
+ ob_configure_pci_device(path, bus_num, mem_base, io_base,
+ bus, devnum, fn, &is_multi);
+
+ }
+ }
+}
+
+static void ob_configure_pci_bridge(pci_addr addr,
+ int *bus_num, unsigned long *mem_base,
+ unsigned long *io_base,
+ int primary_bus, pci_config_t *config)
+{
+ config->primary_bus = primary_bus;
+ pci_config_write8(addr, PCI_PRIMARY_BUS, config->primary_bus);
+
+ config->secondary_bus = *bus_num;
+ pci_config_write8(addr, PCI_SECONDARY_BUS, config->secondary_bus);
+
+ config->subordinate_bus = 0xff;
+ pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus);
+
+ PCI_DPRINTF("scanning new pci bus %u under bridge %s\n",
+ config->secondary_bus, config->path);
+
+ /* make pci bridge parent device, prepare for recursion */
+
+ ob_scan_pci_bus(bus_num, mem_base, io_base,
+ config->path, config->secondary_bus);
+
+ /* bus scan updates *bus_num to last revealed pci bus number */
+ config->subordinate_bus = *bus_num;
+ pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus);
+
+ PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d\n",
+ config->path, config->primary_bus, config->secondary_bus,
+ config->subordinate_bus);
+
+ pci_set_bus_range(config);
+}
+
+static int ob_pci_read_identification(int bus, int devnum, int fn,
+ int *p_vid, int *p_did,
+ uint8_t *p_class, uint8_t *p_subclass)
+{
+ int vid, did;
+ uint32_t ccode;
+ pci_addr addr;
+
+#ifdef CONFIG_XBOX
+ if (pci_xbox_blacklisted (bus, devnum, fn))
+ return;
+#endif
+ addr = PCI_ADDR(bus, devnum, fn);
+ vid = pci_config_read16(addr, PCI_VENDOR_ID);
+ did = pci_config_read16(addr, PCI_DEVICE_ID);
+
+ if (vid==0xffff || vid==0) {
+ return 0;
+ }
+
+ if (p_vid) {
+ *p_vid = vid;
+ }
+
+ if (p_did) {
+ *p_did = did;
+ }
+
+ ccode = pci_config_read16(addr, PCI_CLASS_DEVICE);
+
+ if (p_class) {
+ *p_class = ccode >> 8;
+ }
+
+ if (p_subclass) {
+ *p_subclass = ccode;
+ }
+
+ return 1;
+}
+
+static void ob_configure_pci_device(const char* parent_path,
+ int *bus_num, unsigned long *mem_base, unsigned long *io_base,
+ int bus, int devnum, int fn, int *p_is_multi)
+{
+ int vid, did;
+ unsigned int htype;
+ pci_addr addr;
+ pci_config_t config = {};
+ const pci_dev_t *pci_dev;
+ uint8_t class, subclass, iface;
+ int num_bars, rom_bar;
+
+ phandle_t phandle = 0;
+ int is_host_bridge = 0;
+
+ if (!ob_pci_read_identification(bus, devnum, fn, &vid, &did, &class, &subclass)) {
+ return;
+ }
+
+ addr = PCI_ADDR(bus, devnum, fn);
+ iface = pci_config_read8(addr, PCI_CLASS_PROG);
+
+ pci_dev = pci_find_device(class, subclass, iface,
+ vid, did);
+
+ PCI_DPRINTF("%x:%x.%x - %x:%x - ", bus, devnum, fn,
+ vid, did);
+
+ htype = pci_config_read8(addr, PCI_HEADER_TYPE);
+
+ if (fn == 0) {
+ if (p_is_multi) {
+ *p_is_multi = htype & 0x80;
+ }
+ }
+
+ /* stop adding host bridge accessible from it's primary bus
+ PCI host bridge is to be added by host code
+ */
+ if (class == PCI_BASE_CLASS_BRIDGE &&
+ subclass == PCI_SUBCLASS_BRIDGE_HOST) {
+ is_host_bridge = 1;
+ }
+
+ if (is_host_bridge) {
+ /* reuse device tree node */
+ PCI_DPRINTF("host bridge found - ");
+ snprintf(config.path, sizeof(config.path),
+ "%s", parent_path);
+ } else if (pci_dev == NULL || pci_dev->name == NULL) {
+ snprintf(config.path, sizeof(config.path),
+ "%s/pci%x,%x", parent_path, vid, did);
+ }
+ else {
+ snprintf(config.path, sizeof(config.path),
+ "%s/%s", parent_path, pci_dev->name);
+ }
+
+ PCI_DPRINTF("%s - ", config.path);
+
+ config.dev = addr & 0x00FFFFFF;
+
+ switch (class) {
+ case PCI_BASE_CLASS_BRIDGE:
+ if (subclass != PCI_SUBCLASS_BRIDGE_HOST) {
+ REGISTER_NAMED_NODE_PHANDLE(ob_pci_bus_node, config.path, phandle);
+ }
+ break;
+ case PCI_CLASS_DISPLAY:
+ REGISTER_NAMED_NODE_PHANDLE(ob_pci_empty_node, config.path, phandle);
+ break;
+ default:
+ REGISTER_NAMED_NODE_PHANDLE(ob_pci_simple_node, config.path, phandle);
+ break;
+ }
+
+ if (is_host_bridge) {
+ phandle = find_dev(config.path);
+
+ if (get_property(phandle, "vendor-id", NULL)) {
+ PCI_DPRINTF("host bridge already configured\n");
+ return;
+ }
+ }
+
+ activate_dev(phandle);
+
+ if (htype & PCI_HEADER_TYPE_BRIDGE) {
+ num_bars = 2;
+ rom_bar = PCI_ROM_ADDRESS1;
+ } else {
+ num_bars = 6;
+ rom_bar = PCI_ROM_ADDRESS;
+ }
+
+ ob_pci_configure(addr, &config, num_bars, rom_bar,
+ mem_base, io_base);
+
+ ob_pci_add_properties(phandle, addr, pci_dev, &config, num_bars);
+
+ if (!is_host_bridge) {
+ pci_set_reg(phandle, &config, num_bars);
+ }
+
+ /* call device-specific configuration callback */
+ if (pci_dev && pci_dev->config_cb) {
+ //activate_device(config.path);
+ pci_dev->config_cb(&config);
+ }
+
+ /* device is configured so we may move it out of scope */
+ device_end();
+
+ /* scan bus behind bridge device */
+ //if (htype & PCI_HEADER_TYPE_BRIDGE && class == PCI_BASE_CLASS_BRIDGE) {
+ if ( class == PCI_BASE_CLASS_BRIDGE &&
+ ( subclass == PCI_SUBCLASS_BRIDGE_PCI ||
+ subclass == PCI_SUBCLASS_BRIDGE_HOST ) ) {
+
+ if (subclass == PCI_SUBCLASS_BRIDGE_PCI) {
+ /* reserve next pci bus number for this PCI bridge */
+ ++(*bus_num);
+ }
+
+ ob_configure_pci_bridge(addr, bus_num, mem_base, io_base, bus, &config);
+ }
+}
+
+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;
+ uint8_t class, subclass;
+ unsigned long mem_base, io_base;
+
+ pci_config_t config = {}; /* host bridge */
+ phandle_t phandle_host = 0;
+
+ PCI_DPRINTF("Initializing PCI host bridge...\n");
+
+ activate_device("/");
+
+ /* Find all PCI bridges */
+
+ mem_base = arch->pci_mem_base;
+ /* I/O ports under 0x400 are used by devices mapped at fixed
+ location. */
+ io_base = 0x400;
+
+ bus = 0;
+
+ for (devnum = 0; devnum < 32; devnum++) {
+ /* scan only fn 0 */
+ fn = 0;
+
+ if (!ob_pci_read_identification(bus, devnum, fn,
+ 0, 0, &class, &subclass)) {
+ continue;
+ }
+
+ if (class != PCI_BASE_CLASS_BRIDGE || subclass != PCI_SUBCLASS_BRIDGE_HOST) {
+ continue;
+ }
+
+ /* create root node for host PCI bridge */
+
+ /* configure */
+ snprintf(config.path, sizeof(config.path), "/pci");
+
+ REGISTER_NAMED_NODE_PHANDLE(ob_pci_bus_node, config.path, phandle_host);
+
+ pci_host_set_reg(phandle_host);
+
+ /* update device path after changing "reg" property */
+ ob_pci_reload_device_path(phandle_host, &config);
+
+ ob_configure_pci_device(config.path, &bus, &mem_base, &io_base,
+ bus, devnum, fn, 0);
+
+ /* we expect single host PCI bridge
+ but this may be machine-specific */
+ 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();
+
+ return 0;
+}
diff --git a/qemu/roms/openbios/drivers/pci.fs b/qemu/roms/openbios/drivers/pci.fs
new file mode 100644
index 000000000..563b652a4
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pci.fs
@@ -0,0 +1,92 @@
+[IFDEF] CONFIG_DRIVER_PCI
+
+: pci-addr-encode ( addr.lo addr.mi addr.hi )
+ rot >r swap >r
+ encode-int
+ r> encode-int encode+
+ r> encode-int encode+
+ ;
+
+: pci-len-encode ( len.lo len.hi )
+ encode-int
+ rot encode-int encode+
+ ;
+
+\ Get region offset for BAR reg
+: pci-bar-offset@ ( bar-reg -- off.lo off.hi -1 | 0 )
+ " reg" active-package get-package-property 0= if
+ begin
+ decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi )
+ ff and 5 pick = if
+ >r >r 3drop r> r>
+ -1 exit
+ else
+ 2drop
+ then
+ \ Drop the size as we don't need it
+ decode-int drop decode-int drop
+ dup 0=
+ until
+ 3drop
+ 0 exit
+ else
+ 0
+ then
+ ;
+
+\ Get region size for BAR reg
+: pci-bar-size@ ( bar-reg -- size )
+ " reg" active-package get-package-property 0= if
+ begin
+ decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi )
+ ff and 5 pick = if
+ 2drop decode-int drop
+ decode-int
+ >r 3drop r>
+ exit
+ else
+ 2drop decode-int drop
+ decode-int drop
+ then
+ dup 0=
+ until
+ 3drop
+ 0 \ default size of 0 if BAR not found
+ then
+ ;
+
+\ Get base address for configured BAR reg
+: pci-bar-base@ ( bar-reg -- addr.lo addr.hi -1 | 0 )
+ " assigned-addresses" active-package get-package-property 0= if
+ begin
+ decode-phys \ ( reg prop prop-len phys.lo phys.mid phys.hi )
+ ff and 5 pick = if
+ >r >r 3drop r> r>
+ -1 exit
+ else
+ 2drop
+ then
+ \ Drop the size as we don't need it
+ decode-int drop decode-int drop
+ dup 0=
+ until
+ 3drop
+ 0 exit
+ else
+ 0
+ then
+ ;
+
+\ Get PCI bus address and size for configured BAR reg
+: pci-bar>pci-region ( bar-reg -- addr.lo addr.hi size )
+ dup
+ >r pci-bar-offset@ if
+ swap r@ pci-bar-base@ if
+ swap d+
+ then
+ swap r@ pci-bar-size@
+ then
+ r> drop
+ ;
+
+[THEN]
diff --git a/qemu/roms/openbios/drivers/pci.h b/qemu/roms/openbios/drivers/pci.h
new file mode 100644
index 000000000..84a2b2cf6
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pci.h
@@ -0,0 +1,87 @@
+#ifndef PCI_H
+#define PCI_H
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_DEVICE_ID 0x02
+
+#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 */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features
+ [obsolete] */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_DISPLAY 0x03
+#define PCI_CLASS_PROG 0x09
+#define PCI_CLASS_DEVICE 0x0a
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e
+#define PCI_HEADER_TYPE_NORMAL 0x00
+#define PCI_HEADER_TYPE_BRIDGE 0x01
+#define PCI_HEADER_TYPE_CARDBUS 0x02
+#define PCI_PRIMARY_BUS 0x18
+#define PCI_SECONDARY_BUS 0x19
+#define PCI_SUBORDINATE_BUS 0x1A
+#define PCI_BASE_ADDR_0 0x10
+#define PCI_BASE_ADDR_1 0x14
+#define PCI_BASE_ADDR_2 0x18
+#define PCI_BASE_ADDR_3 0x1c
+#define PCI_BASE_ADDR_4 0x20
+#define PCI_BASE_ADDR_5 0x24
+
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~0x7ffUL)
+#define PCI_ROM_ADDRESS1 0x38 /* ROM_ADDRESS in bridge header */
+
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+typedef struct {
+ u16 signature;
+ u8 reserved[0x16];
+ u16 dptr;
+} rom_header_t;
+
+typedef struct {
+ u32 signature;
+ u16 vendor;
+ u16 device;
+ u16 reserved_1;
+ u16 dlen;
+ u8 drevision;
+ u8 class_hi;
+ u16 class_lo;
+ u16 ilen;
+ u16 irevision;
+ u8 type;
+ u8 indicator;
+ u16 reserved_2;
+} pci_data_t;
+
+
+#include "asm/pci.h"
+
+#endif /* PCI_H */
diff --git a/qemu/roms/openbios/drivers/pci_database.c b/qemu/roms/openbios/drivers/pci_database.c
new file mode 100644
index 000000000..0070a78ba
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pci_database.c
@@ -0,0 +1,1275 @@
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "drivers/pci.h"
+#include "libc/vsprintf.h"
+
+#include "pci_database.h"
+
+/* PCI devices database */
+
+typedef struct pci_class_t pci_class_t;
+typedef struct pci_subclass_t pci_subclass_t;
+typedef struct pci_iface_t pci_iface_t;
+
+struct pci_iface_t {
+ uint8_t iface;
+ const char *name;
+ const char *type;
+ const pci_dev_t *devices;
+ int (*config_cb)(const pci_config_t *config);
+ const void *private;
+};
+
+struct pci_subclass_t {
+ uint8_t subclass;
+ const char *name;
+ const char *type;
+ const pci_dev_t *devices;
+ const pci_iface_t *iface;
+ int (*config_cb)(const pci_config_t *config);
+ const void *private;
+};
+
+struct pci_class_t {
+ const char *name;
+ const char *type;
+ const pci_subclass_t *subc;
+};
+
+/* Current machine description */
+
+static const pci_subclass_t undef_subclass[] = {
+ {
+ 0xFF, NULL, NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t scsi_devices[] = {
+ {
+ /* Virtio-block controller */
+ PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_BLOCK,
+ NULL, "virtio-blk", NULL,
+ "pci1af4,1001\0pci1af4,1001\0pciclass,01018f\0",
+ 0, 0, 0,
+ NULL, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t ide_devices[] = {
+ {
+ PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646, /* CMD646 IDE controller */
+ "pci-ide", "pci-ata", NULL,
+ "pci1095,646\0pci1095,646\0pciclass,01018f\0",
+ 0, 0, 0,
+ ide_config_cb2, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t mass_subclass[] = {
+ {
+ PCI_SUBCLASS_STORAGE_SCSI, "SCSI bus controller",
+ "scsi", scsi_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_IDE, "IDE controller",
+ "ide", ide_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_FLOPPY, "Floppy disk controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_IPI, "IPI bus controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_RAID, "RAID controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_ATA, "ATA controller",
+ "ata", NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_STORAGE_OTHER, "misc mass-storage controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t eth_devices[] = {
+ {
+ PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_RTL8029,
+ NULL, "NE2000", "NE2000 PCI", NULL,
+ 0, 0, 0,
+ NULL, "ethernet",
+ },
+ {
+ /* Virtio-network controller */
+ PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_NET,
+ NULL, "virtio-net", NULL,
+ "pci1af4,1000\0pci1af4,1000\0pciclass,020000\0",
+ 0, 0, 0,
+ NULL, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t net_subclass[] = {
+ {
+ PCI_SUBCLASS_NETWORK_ETHERNET, "ethernet controller",
+ NULL, eth_devices, NULL,
+ eth_config_cb, "ethernet",
+ },
+ {
+ PCI_SUBCLASS_NETWORK_TOKEN_RING, "token ring controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_FDDI, "FDDI controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_ATM, "ATM controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_ISDN, "ISDN controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_WORDFIP, "WordFip controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_PICMG214, "PICMG 2.14 controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_NETWORK_OTHER, "misc network controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t vga_devices[] = {
+ {
+ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF,
+ NULL, "ATY", "ATY Rage128", "VGA\0",
+ 0, 0, 0,
+ NULL, NULL,
+ },
+ {
+ PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_QEMU_VGA,
+ NULL, "QEMU,VGA", "Qemu VGA", "VGA\0",
+ 0, 0, 0,
+ NULL, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const struct pci_iface_t vga_iface[] = {
+ {
+ 0x00, "VGA controller", NULL,
+ vga_devices, &vga_config_cb, NULL,
+ },
+ {
+ 0x01, "8514 compatible controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_subclass_t displ_subclass[] = {
+ {
+ PCI_SUBCLASS_DISPLAY_VGA, "display controller",
+ NULL, NULL, vga_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_DISPLAY_XGA, "XGA display controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_DISPLAY_3D, "3D display controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_DISPLAY_OTHER, "misc display controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t media_subclass[] = {
+ {
+ PCI_SUBCLASS_MULTIMEDIA_VIDEO, "video device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_MULTIMEDIA_AUDIO, "audio device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_MULTIMEDIA_PHONE, "computer telephony device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_MULTIMEDIA_OTHER, "misc multimedia device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t mem_subclass[] = {
+ {
+ PCI_SUBCLASS_MEMORY_RAM, "RAM controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_MEMORY_FLASH, "flash controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+
+static const pci_dev_t hbrg_devices[] = {
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_U3_AGP, NULL,
+ "pci", "AAPL,UniNorth", "u3-agp\0",
+ 3, 2, 1,
+ host_config_cb, NULL,
+ },
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_AGP, NULL,
+ "pci", "AAPL,UniNorth", "uni-north\0",
+ 3, 2, 1,
+ host_config_cb, NULL,
+ },
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_PCI, NULL,
+ "pci", "AAPL,UniNorth", "uni-north\0",
+ 3, 2, 1,
+ host_config_cb, NULL,
+ },
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_I_PCI, NULL,
+ "pci", "AAPL,UniNorth", "uni-north\0",
+ 3, 2, 1,
+ NULL, NULL
+ },
+ {
+ PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_MPC106, "pci",
+ "pci", "MOT,MPC106", "grackle\0",
+ 3, 2, 1,
+ host_config_cb, NULL
+ },
+ {
+ PCI_VENDOR_ID_MOTOROLA, PCI_DEVICE_ID_MOTOROLA_RAVEN, NULL,
+ "pci", "PREP Host PCI Bridge - Motorola Raven", NULL,
+ 3, 2, 1,
+ host_config_cb, NULL,
+ },
+ {
+ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SABRE, NULL,
+ "pci", "SUNW,sabre", "pci108e,a000\0pciclass,0\0",
+ 3, 2, 1,
+ sabre_config_cb, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t PCIbrg_devices[] = {
+ {
+ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21154, NULL,
+ "pci-bridge", "DEV,21154", "DEV,21154\0pci-bridge\0",
+ 3, 2, 1,
+ bridge_config_cb, NULL,
+ },
+ {
+ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, NULL,
+ "pci", "SUNW,simba", "pci108e,5000\0pciclass,060400\0",
+ 3, 2, 1,
+ bridge_config_cb, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t miscbrg_devices[] = {
+ {
+ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, NULL,
+ "ebus", "ebus", "pci108e,1000\0pciclass,068000\0",
+ 2, 1, 1,
+ ebus_config_cb, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_dev_t isabrg_devices[] = {
+ {
+ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, NULL,
+ "isa", "isa", "pci8086,484\0",
+ 1, 1, 1,
+ i82378_config_cb, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t bridg_subclass[] = {
+ {
+ PCI_SUBCLASS_BRIDGE_HOST, "PCI host bridge",
+ "pci", hbrg_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_ISA, "ISA bridge",
+ "isa", isabrg_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_EISA, "EISA bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_MC, "MCA bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_PCI, "PCI-to-PCI bridge",
+ "pci", PCIbrg_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_PCMCIA, "PCMCIA bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_NUBUS, "NUBUS bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_CARDBUS, "cardbus bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_RACEWAY, "raceway bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_PCI_SEMITP, "semi-transparent PCI-to-PCI bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_IB_PCI, "infiniband-to-PCI bridge",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_BRIDGE_OTHER, "misc PCI bridge",
+ NULL, miscbrg_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_iface_t serial_iface[] = {
+ {
+ 0x00, "XT serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "16450 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "16550 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x03, "16650 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x04, "16750 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x05, "16850 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x06, "16950 serial controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t par_iface[] = {
+ {
+ 0x00, "parallel port", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "bi-directional parallel port", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "ECP 1.x parallel port", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x03, "IEEE 1284 controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFE, "IEEE 1284 device", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t modem_iface[] = {
+ {
+ 0x00, "generic modem", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "Hayes 16450 modem", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "Hayes 16550 modem", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x03, "Hayes 16650 modem", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x04, "Hayes 16750 modem", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_subclass_t comm_subclass[] = {
+ {
+ PCI_SUBCLASS_COMMUNICATION_SERIAL, "serial controller",
+ NULL, NULL, serial_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_PARALLEL, "parallel port",
+ NULL, NULL, par_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_MULTISERIAL, "multiport serial controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_MODEM, "modem",
+ NULL, NULL, modem_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_GPIB, "GPIB controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_SC, "smart card",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_COMMUNICATION_OTHER, "misc communication device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_iface_t pic_iface[] = {
+ {
+ 0x00, "8259 PIC", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "ISA PIC", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "EISA PIC", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x10, "I/O APIC", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x20, "I/O APIC", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t dma_iface[] = {
+ {
+ 0x00, "8237 DMA controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "ISA DMA controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "EISA DMA controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t tmr_iface[] = {
+ {
+ 0x00, "8254 system timer", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "ISA system timer", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "EISA system timer", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t rtc_iface[] = {
+ {
+ 0x00, "generic RTC controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "ISA RTC controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_dev_t sys_devices[] = {
+ /* IBM MPIC controller */
+ {
+ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OPENPIC,
+ "open-pic", "MPIC", NULL, "chrp,open-pic\0",
+ 0, 0, 2,
+ NULL, NULL,
+ },
+ /* IBM MPIC2 controller */
+ {
+ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OPENPIC2,
+ "open-pic", "MPIC2", NULL, "chrp,open-pic\0",
+ 0, 0, 2,
+ NULL, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t sys_subclass[] = {
+ {
+ PCI_SUBCLASS_SYSTEM_PIC, "PIC",
+ NULL, NULL, pic_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SYSTEM_DMA, "DMA controller",
+ NULL, NULL, dma_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SYSTEM_TIMER, "system timer",
+ NULL, NULL, tmr_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SYSTEM_RTC, "RTC controller",
+ NULL, NULL, rtc_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SYSTEM_PCI_HOTPLUG, "PCI hotplug controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SYSTEM_OTHER, "misc system peripheral",
+ NULL, sys_devices, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t inp_subclass[] = {
+ {
+ PCI_SUBCLASS_INPUT_KEYBOARD, "keyboard controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_INPUT_PEN, "digitizer",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_INPUT_MOUSE, "mouse controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_INPUT_SCANNER, "scanner controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_INPUT_GAMEPORT, "gameport controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_INPUT_OTHER, "misc input device",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t dock_subclass[] = {
+ {
+ PCI_SUBCLASS_DOCKING_GENERIC, "generic docking station",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_DOCKING_OTHER, "misc docking station",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t cpu_subclass[] = {
+ {
+ PCI_SUBCLASS_PROCESSOR_386, "i386 processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_486, "i486 processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_PENTIUM, "pentium processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_ALPHA, "alpha processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_POWERPC, "PowerPC processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_MIPS, "MIPS processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_PROCESSOR_CO, "co-processor",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+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,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x10, "OHCI USB controller", NULL,
+ usb_devices, &usb_ohci_config_cb, NULL,
+ },
+ {
+ 0x20, "EHCI USB controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x80, "misc USB controller", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFE, "USB device", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_iface_t ipmi_iface[] = {
+ {
+ 0x00, "IPMI SMIC interface", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x01, "IPMI keyboard interface", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0x02, "IPMI block transfer interface", NULL,
+ NULL, NULL, NULL,
+ },
+ {
+ 0xFF, NULL, NULL,
+ NULL, NULL, NULL,
+ },
+};
+
+static const pci_subclass_t ser_subclass[] = {
+ {
+ PCI_SUBCLASS_SERIAL_FIREWIRE, "Firewire bus controller",
+ "ieee1394", NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_ACCESS, "ACCESS bus controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_SSA, "SSA controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_USB, "USB controller",
+ "usb", NULL, usb_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_FIBER, "fibre channel controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_SMBUS, "SMBus controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_IB, "InfiniBand controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_IPMI, "IPMI interface",
+ NULL, NULL, ipmi_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_SERCOS, "SERCOS controller",
+ NULL, NULL, ipmi_iface,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SERIAL_CANBUS, "CANbus controller",
+ NULL, NULL, ipmi_iface,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t wrl_subclass[] = {
+ {
+ PCI_SUBCLASS_WIRELESS_IRDA, "IRDA controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_WIRELESS_CIR, "consumer IR controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_WIRELESS_RF_CONTROLLER, "RF controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_WIRELESS_BLUETOOTH, "bluetooth controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_WIRELESS_BROADBAND, "broadband controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_WIRELESS_OTHER, "misc wireless controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t sat_subclass[] = {
+ {
+ PCI_SUBCLASS_SATELLITE_TV, "satellite TV controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SATELLITE_AUDIO, "satellite audio controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SATELLITE_VOICE, "satellite voice controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SATELLITE_DATA, "satellite data controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t crypt_subclass[] = {
+ {
+ PCI_SUBCLASS_CRYPT_NETWORK, "cryptographic network controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_CRYPT_ENTERTAINMENT,
+ "cryptographic entertainment controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_CRYPT_OTHER, "misc cryptographic controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_subclass_t spc_subclass[] = {
+ {
+ PCI_SUBCLASS_SP_DPIO, "DPIO module",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SP_PERF, "performances counters",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SP_SYNCH, "communication synchronisation",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SP_MANAGEMENT, "management card",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ PCI_SUBCLASS_SP_OTHER, "misc signal processing controller",
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+ {
+ 0xFF, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ },
+};
+
+static const pci_class_t pci_classes[] = {
+ /* 0x00 */
+ { "undefined", NULL, undef_subclass, },
+ /* 0x01 */
+ { "mass-storage controller", NULL, mass_subclass, },
+ /* 0x02 */
+ { "network controller", "network", net_subclass, },
+ /* 0x03 */
+ { "display controller", "display", displ_subclass, },
+ /* 0x04 */
+ { "multimedia device", NULL, media_subclass, },
+ /* 0x05 */
+ { "memory controller", "memory-controller", mem_subclass, },
+ /* 0x06 */
+ { "PCI bridge", NULL, bridg_subclass, },
+ /* 0x07 */
+ { "communication device", NULL, comm_subclass,},
+ /* 0x08 */
+ { "system peripheral", NULL, sys_subclass, },
+ /* 0x09 */
+ { "input device", NULL, inp_subclass, },
+ /* 0x0A */
+ { "docking station", NULL, dock_subclass, },
+ /* 0x0B */
+ { "processor", NULL, cpu_subclass, },
+ /* 0x0C */
+ { "serial bus controller", NULL, ser_subclass, },
+ /* 0x0D */
+ { "wireless controller", NULL, wrl_subclass, },
+ /* 0x0E */
+ { "intelligent I/O controller", NULL, NULL, },
+ /* 0x0F */
+ { "satellite communication controller", NULL, sat_subclass, },
+ /* 0x10 */
+ { "cryptographic controller", NULL, crypt_subclass, },
+ /* 0x11 */
+ { "signal processing controller", NULL, spc_subclass, },
+};
+
+static const pci_dev_t misc_pci[] = {
+ /* Heathrow Mac I/O */
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_343S1201,
+ "mac-io", "mac-io", "AAPL,343S1201", "heathrow\0",
+ 1, 1, 1,
+ &macio_heathrow_config_cb, NULL,
+ },
+ /* Paddington Mac I/O */
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_343S1211,
+ "mac-io", "mac-io", "AAPL,343S1211", "paddington\0heathrow\0",
+ 1, 1, 1,
+ &macio_heathrow_config_cb, NULL,
+ },
+ /* KeyLargo Mac I/O */
+ {
+ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_KEYL,
+ "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo\0",
+ 1, 1, 1,
+ &macio_keylargo_config_cb, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+ NULL, NULL, NULL, NULL,
+ -1, -1, -1,
+ NULL, NULL,
+ },
+};
+
+const pci_dev_t *pci_find_device (uint8_t class, uint8_t subclass,
+ uint8_t iface, uint16_t vendor,
+ uint16_t product)
+{
+ int (*config_cb)(const pci_config_t *config);
+ const pci_class_t *pclass;
+ const pci_subclass_t *psubclass;
+ const pci_iface_t *piface;
+ const pci_dev_t *dev;
+ const void *private;
+ pci_dev_t *new;
+ const char *name, *type;
+
+ name = "unknown";
+ type = "unknown";
+ config_cb = NULL;
+ private = NULL;
+
+ if (class == 0x00 && subclass == 0x01) {
+ /* Special hack for old style VGA devices */
+ class = 0x03;
+ subclass = 0x00;
+ } else if (class == 0xFF) {
+ /* Special case for misc devices */
+ dev = misc_pci;
+ goto find_device;
+ }
+ if (class > (sizeof(pci_classes) / sizeof(pci_class_t))) {
+ name = "invalid PCI device";
+ type = "invalid";
+ goto bad_device;
+ }
+ pclass = &pci_classes[class];
+ name = pclass->name;
+ type = pclass->type;
+ for (psubclass = pclass->subc; ; psubclass++) {
+ if (psubclass->subclass == 0xFF)
+ goto bad_device;
+ if (psubclass->subclass == subclass) {
+ if (psubclass->name != NULL)
+ name = psubclass->name;
+ if (psubclass->type != NULL)
+ type = psubclass->type;
+ if (psubclass->config_cb != NULL) {
+ config_cb = psubclass->config_cb;
+ }
+ if (psubclass->private != NULL)
+ private = psubclass->private;
+ if (psubclass->iface != NULL)
+ break;
+ dev = psubclass->devices;
+ goto find_device;
+ }
+ }
+ for (piface = psubclass->iface; ; piface++) {
+ if (piface->iface == 0xFF) {
+ dev = psubclass->devices;
+ break;
+ }
+ if (piface->iface == iface) {
+ if (piface->name != NULL)
+ name = piface->name;
+ if (piface->type != NULL)
+ type = piface->type;
+ if (piface->config_cb != NULL) {
+ config_cb = piface->config_cb;
+ }
+ if (piface->private != NULL)
+ private = piface->private;
+ dev = piface->devices;
+ break;
+ }
+ }
+find_device:
+ if (dev == NULL)
+ goto bad_device;
+ for (;; dev++) {
+ if (dev->vendor == 0xFFFF && dev->product == 0xFFFF) {
+ goto bad_device;
+ }
+ if (dev->vendor == vendor && dev->product == product) {
+ if (dev->name != NULL)
+ name = dev->name;
+ if (dev->type != NULL)
+ type = dev->type;
+ if (dev->config_cb != NULL) {
+ config_cb = dev->config_cb;
+ }
+ if (dev->private != NULL)
+ private = dev->private;
+ new = malloc(sizeof(pci_dev_t));
+ if (new == NULL)
+ return NULL;
+ new->vendor = vendor;
+ new->product = product;
+ new->type = type;
+ new->name = name;
+ new->model = dev->model;
+ new->compat = dev->compat;
+ new->acells = dev->acells;
+ new->scells = dev->scells;
+ new->icells = dev->icells;
+ new->config_cb = config_cb;
+ new->private = private;
+
+ return new;
+ }
+ }
+bad_device:
+ printk("Cannot manage '%s' PCI device type '%s':\n %x %x (%x %x %x)\n",
+ name, type, vendor, product, class, subclass, iface);
+
+ return NULL;
+}
diff --git a/qemu/roms/openbios/drivers/pci_database.h b/qemu/roms/openbios/drivers/pci_database.h
new file mode 100644
index 000000000..7f053d808
--- /dev/null
+++ b/qemu/roms/openbios/drivers/pci_database.h
@@ -0,0 +1,57 @@
+typedef struct pci_config_t pci_config_t;
+
+struct pci_config_t {
+ char path[256];
+ uint32_t dev; /* bus, dev, fn */
+ uint32_t regions[7];
+ uint32_t assigned[7];
+ uint32_t sizes[7];
+ int irq_pin;
+ int irq_line;
+ u32 primary_bus;
+ u32 secondary_bus;
+ u32 subordinate_bus;
+};
+
+typedef struct pci_dev_t pci_dev_t;
+struct pci_dev_t {
+ uint16_t vendor;
+ uint16_t product;
+ const char *type;
+ const char *name;
+ const char *model;
+ const char *compat;
+ int acells;
+ int scells;
+ int icells;
+ int (*config_cb)(const pci_config_t *config);
+ const void *private;
+};
+
+extern int ide_config_cb2(const pci_config_t *config);
+extern int eth_config_cb(const pci_config_t *config);
+extern int macio_heathrow_config_cb(const pci_config_t *config);
+extern int macio_keylargo_config_cb(const pci_config_t *config);
+extern int vga_config_cb(const pci_config_t *config);
+extern int host_config_cb(const pci_config_t *config);
+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)
+{
+ int len, ret;
+ const char *path = dev->compat;
+ ret = 0;
+ while ((len = strlen(path)) != 0) {
+ ret += len + 1;
+ path += len + 1;
+ }
+ return ret;
+}
+
+extern const pci_dev_t *pci_find_device(uint8_t class, uint8_t subclass,
+ uint8_t iface, uint16_t vendor,
+ uint16_t product);
diff --git a/qemu/roms/openbios/drivers/sbus.c b/qemu/roms/openbios/drivers/sbus.c
new file mode 100644
index 000000000..a9b26c0a0
--- /dev/null
+++ b/qemu/roms/openbios/drivers/sbus.c
@@ -0,0 +1,523 @@
+/*
+ * OpenBIOS SBus driver
+ *
+ * (C) 2004 Stefan Reinauer <stepan@openbios.org>
+ * (C) 2005 Ed Schouten <ed@fxq.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "libopenbios/bindings.h"
+#include "kernel/kernel.h"
+#include "libc/byteorder.h"
+#include "libc/vsprintf.h"
+#include "drivers/drivers.h"
+#include "libopenbios/ofmem.h"
+#include "libopenbios/video.h"
+
+#define SBUS_REGS 0x28
+#define SBUS_SLOTS 16
+#define APC_REGS 0x10
+#define APC_OFFSET 0x0a000000ULL
+#define CS4231_REGS 0x40
+#define CS4231_OFFSET 0x0c000000ULL
+#define MACIO_ESPDMA 0x00400000ULL /* ESP DMA controller */
+#define MACIO_ESP 0x00800000ULL /* ESP SCSI */
+#define SS600MP_ESPDMA 0x00081000ULL
+#define SS600MP_ESP 0x00080000ULL
+#define SS600MP_LEBUFFER (SS600MP_ESPDMA + 0x10) // XXX should be 0x40000
+#define LEDMA_REGS 0x4
+#define LE_REGS 0x20
+
+#ifdef CONFIG_DEBUG_SBUS
+#define DPRINTF(fmt, args...) \
+ do { printk(fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+typedef struct le_private {
+ uint32_t *dmaregs;
+ uint32_t *regs;
+} le_private_t;
+
+static void
+ob_sbus_node_init(uint64_t base)
+{
+ void *regs;
+
+ push_str("/iommu/sbus");
+ fword("find-device");
+
+ PUSH(base >> 32);
+ fword("encode-int");
+ PUSH(base & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(SBUS_REGS);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ regs = (void *)ofmem_map_io(base, SBUS_REGS);
+ PUSH((unsigned long)regs);
+ fword("encode-int");
+ push_str("address");
+ fword("property");
+}
+
+static void
+ob_le_init(unsigned int slot, uint64_t base, unsigned long leoffset, unsigned long dmaoffset)
+{
+ le_private_t *le;
+
+ le = malloc(sizeof(le_private_t));
+ if (!le) {
+ DPRINTF("Can't allocate LANCE private structure\n");
+ return;
+ }
+
+ /* Get the IO region for DMA registers */
+ le->dmaregs = (void *)ofmem_map_io(base + (uint64_t)dmaoffset, LEDMA_REGS);
+ if (le->dmaregs == NULL) {
+ DPRINTF("Can't map LANCE DMA registers\n");
+ return;
+ }
+
+ /* Now it appears that the Solaris kernel forgets to set up the LANCE DMA mapping
+ and so it must inherit the one from OpenBIOS. The symptom of this is that the
+ LANCE DMA base addr register is still zero, and so we start sending network
+ packets containing random areas of memory.
+
+ The correct fix for this should be to use dvma_alloc() to grab a section of
+ memory and point the LANCE DMA buffers to use that instead; this gets
+ slightly further but still crashes. Time-consuming investigation on various
+ hacked versions of QEMU seems to indicate that Solaris always assumes the LANCE
+ DMA base address is fixed 0xff000000 when setting up the IOMMU for the LANCE
+ card. Hence we imitate this behaviour here. */
+ le->dmaregs[3] = 0xff000000;
+
+ push_str("/iommu/sbus/ledma");
+ fword("find-device");
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(dmaoffset);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(0x00000020);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ /* Get the IO region for Lance registers */
+ le->regs = (void *)ofmem_map_io(base + (uint64_t)leoffset, LE_REGS);
+ if (le->regs == NULL) {
+ DPRINTF("Can't map LANCE registers\n");
+ return;
+ }
+
+ push_str("/iommu/sbus/ledma/le");
+ fword("find-device");
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(leoffset);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(0x00000004);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+}
+
+uint16_t graphic_depth;
+
+static void
+ob_tcx_init(unsigned int slot, const char *path)
+{
+ char buf[6];
+
+ printk("No display device located during SBus probe - falling back to internal TCX driver\n");
+
+ /* Make the sbus node the current instance and active package for probing */
+ feval("active-package my-self");
+ push_str("/iommu/sbus");
+ feval("2dup find-device open-dev to my-self");
+
+ fword("new-device");
+ PUSH(0);
+ PUSH(0);
+ snprintf(buf, 6, "%x,0", slot);
+ push_str(buf);
+ fword("set-args");
+ feval("['] tcx-driver-fcode 2 cells + 1 byte-load");
+ fword("finish-device");
+
+ /* Restore */
+ feval("to my-self active-package!");
+}
+
+static void
+ob_apc_init(unsigned int slot, unsigned long base)
+{
+ push_str("/iommu/sbus");
+ fword("find-device");
+ fword("new-device");
+
+ push_str("power-management");
+ fword("device-name");
+
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(base);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(APC_REGS);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ fword("finish-device");
+}
+
+static void
+ob_cs4231_init(unsigned int slot)
+{
+ push_str("/iommu/sbus");
+ fword("find-device");
+ fword("new-device");
+
+ push_str("SUNW,CS4231");
+ fword("device-name");
+
+ push_str("serial");
+ fword("device-type");
+
+ PUSH(slot);
+ fword("encode-int");
+ PUSH(CS4231_OFFSET);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(CS4231_REGS);
+ fword("encode-int");
+ fword("encode+");
+ push_str("reg");
+ fword("property");
+
+ PUSH(5);
+ fword("encode-int");
+ PUSH(0);
+ fword("encode-int");
+ fword("encode+");
+ push_str("intr");
+ fword("property");
+
+ PUSH(5);
+ fword("encode-int");
+ push_str("interrupts");
+ fword("property");
+
+ push_str("audio");
+ fword("encode-string");
+ push_str("alias");
+ fword("property");
+
+ fword("finish-device");
+}
+
+static void
+ob_macio_init(unsigned int slot, uint64_t base, unsigned long offset)
+{
+ // All devices were integrated to NCR89C100, see
+ // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+
+ // NCR 53c9x, aka ESP. See
+ // http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
+#ifdef CONFIG_DRIVER_ESP
+ ob_esp_init(slot, base, offset + MACIO_ESP, offset + MACIO_ESPDMA);
+#endif
+
+ // NCR 92C990, Am7990, Lance. See http://www.amd.com
+ ob_le_init(slot, base, offset + 0x00c00000, offset + 0x00400010);
+
+ // Parallel port
+ //ob_bpp_init(base);
+}
+
+static void
+sbus_probe_self(unsigned int slot, unsigned long offset)
+{
+ /* Wrapper for calling probe-self in Forth. This is mainly because some
+ drivers don't handle properties correctly when the sbus node is set
+ as the current instance during probe. */
+ char buf[6];
+
+ printk("Probing SBus slot %d offset %ld\n", slot, offset);
+
+ /* Make the sbus node the current instance and active package for probing */
+ feval("active-package my-self");
+ push_str("/iommu/sbus");
+ feval("open-dev to my-self");
+
+ PUSH(0);
+ PUSH(0);
+ snprintf(buf, 6, "%x,%lx", slot, offset);
+ push_str(buf);
+ fword("2dup");
+ fword("probe-self-sbus");
+
+ /* Restore */
+ feval("to my-self active-package!");
+}
+
+static int
+sbus_probe_sucess(void)
+{
+ /* Return true if the last sbus_probe_self() resulted in
+ the successful detection and execution of FCode */
+ fword("probe-fcode?");
+ return POP();
+}
+
+static void
+sbus_probe_slot_ss5(unsigned int slot, uint64_t base)
+{
+ /* Probe the slot */
+ sbus_probe_self(slot, 0);
+
+ /* If the device was successfully created by FCode then do nothing */
+ if (sbus_probe_sucess()) {
+ return;
+ }
+
+ switch(slot) {
+ case 3: // SUNW,tcx
+ ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
+ break;
+ case 4:
+ // SUNW,CS4231
+ ob_cs4231_init(slot);
+ // Power management (APC)
+ ob_apc_init(slot, APC_OFFSET);
+ break;
+ case 5: // MACIO: le, esp, bpp
+ ob_macio_init(slot, base, 0x08000000);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sbus_probe_slot_ss10(unsigned int slot, uint64_t base)
+{
+ /* Probe the slot */
+ sbus_probe_self(slot, 0);
+
+ /* If the device was successfully created by FCode then do nothing */
+ if (sbus_probe_sucess()) {
+ return;
+ }
+
+ switch(slot) {
+ case 2: // SUNW,tcx
+ ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
+ break;
+ case 0xf: // le, esp, bpp, power-management
+ ob_macio_init(slot, base, 0);
+ // Power management (APC) XXX should not exist
+ ob_apc_init(slot, APC_OFFSET);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sbus_probe_slot_ss600mp(unsigned int slot, uint64_t base)
+{
+ /* Probe the slot */
+ sbus_probe_self(slot, 0);
+
+ /* If the device was successfully created by FCode then do nothing */
+ if (sbus_probe_sucess()) {
+ return;
+ }
+
+ switch(slot) {
+ case 2: // SUNW,tcx
+ ob_tcx_init(slot, "/iommu/sbus/SUNW,tcx");
+ break;
+ case 0xf: // le, esp, bpp, power-management
+#ifdef CONFIG_DRIVER_ESP
+ ob_esp_init(slot, base, SS600MP_ESP, SS600MP_ESPDMA);
+#endif
+ // NCR 92C990, Am7990, Lance. See http://www.amd.com
+ ob_le_init(slot, base, 0x00060000, SS600MP_LEBUFFER);
+ // Power management (APC) XXX should not exist
+ ob_apc_init(slot, APC_OFFSET);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ob_sbus_open(void)
+{
+ int ret=1;
+ RET ( -ret );
+}
+
+static void
+ob_sbus_close(void)
+{
+ selfword("close-deblocker");
+}
+
+static void
+ob_sbus_initialize(void)
+{
+}
+
+
+NODE_METHODS(ob_sbus_node) = {
+ { NULL, ob_sbus_initialize },
+ { "open", ob_sbus_open },
+ { "close", ob_sbus_close },
+};
+
+struct sbus_offset {
+ int slot, type;
+ uint64_t base;
+ unsigned long size;
+};
+
+static const struct sbus_offset sbus_offsets_ss5[SBUS_SLOTS] = {
+ { 0, 0, 0x20000000, 0x10000000,},
+ { 1, 0, 0x30000000, 0x10000000,},
+ { 2, 0, 0x40000000, 0x10000000,},
+ { 3, 0, 0x50000000, 0x10000000,},
+ { 4, 0, 0x60000000, 0x10000000,},
+ { 5, 0, 0x70000000, 0x10000000,},
+};
+
+/* Shared with ss600mp */
+static const struct sbus_offset sbus_offsets_ss10[SBUS_SLOTS] = {
+ { 0, 0, 0xe00000000ULL, 0x10000000,},
+ { 1, 0, 0xe10000000ULL, 0x10000000,},
+ { 2, 0, 0xe20000000ULL, 0x10000000,},
+ { 3, 0, 0xe30000000ULL, 0x10000000,},
+ [0xf] = { 0xf, 0, 0xef0000000ULL, 0x10000000,},
+};
+
+static void
+ob_add_sbus_range(const struct sbus_offset *range, int notfirst)
+{
+ if (!notfirst) {
+ push_str("/iommu/sbus");
+ fword("find-device");
+ }
+ PUSH(range->slot);
+ fword("encode-int");
+ if (notfirst)
+ fword("encode+");
+ PUSH(range->type);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(range->base >> 32);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(range->base & 0xffffffff);
+ fword("encode-int");
+ fword("encode+");
+ PUSH(range->size);
+ fword("encode-int");
+ fword("encode+");
+}
+
+static int
+ob_sbus_init_ss5(void)
+{
+ unsigned int slot;
+ int notfirst = 0;
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss5[slot].size > 0)
+ ob_add_sbus_range(&sbus_offsets_ss5[slot], notfirst++);
+ }
+ push_str("ranges");
+ fword("property");
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss5[slot].size > 0)
+ sbus_probe_slot_ss5(slot, sbus_offsets_ss5[slot].base);
+ }
+
+ return 0;
+}
+
+static int
+ob_sbus_init_ss10(void)
+{
+ unsigned int slot;
+ int notfirst = 0;
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss10[slot].size > 0)
+ ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
+ }
+ push_str("ranges");
+ fword("property");
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss10[slot].size > 0)
+ sbus_probe_slot_ss10(slot, sbus_offsets_ss10[slot].base);
+ }
+
+ return 0;
+}
+
+static int
+ob_sbus_init_ss600mp(void)
+{
+ unsigned int slot;
+ int notfirst = 0;
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss10[slot].size > 0)
+ ob_add_sbus_range(&sbus_offsets_ss10[slot], notfirst++);
+ }
+ push_str("ranges");
+ fword("property");
+
+ for (slot = 0; slot < SBUS_SLOTS; slot++) {
+ if (sbus_offsets_ss10[slot].size > 0)
+ sbus_probe_slot_ss600mp(slot, sbus_offsets_ss10[slot].base);
+ }
+
+ return 0;
+}
+
+int ob_sbus_init(uint64_t base, int machine_id)
+{
+ ob_sbus_node_init(base);
+
+ switch (machine_id) {
+ case 66:
+ return ob_sbus_init_ss600mp();
+ case 64 ... 65:
+ return ob_sbus_init_ss10();
+ case 32 ... 63:
+ return ob_sbus_init_ss5();
+ default:
+ return -1;
+ }
+}
diff --git a/qemu/roms/openbios/drivers/sbus.fs b/qemu/roms/openbios/drivers/sbus.fs
new file mode 100644
index 000000000..b84a3ac72
--- /dev/null
+++ b/qemu/roms/openbios/drivers/sbus.fs
@@ -0,0 +1,94 @@
+\ -------------------------------------------------------------------------
+\ SBus encode/decode unit
+\ -------------------------------------------------------------------------
+
+: decode-unit-sbus ( str len -- id lun )
+ ascii , left-split
+ ( addr-R len-R addr-L len-L )
+ parse-hex
+ -rot parse-hex
+ swap
+;
+
+: encode-unit-sbus ( id lun -- str len)
+ swap
+ pocket tohexstr
+ " ," pocket tmpstrcat >r
+ rot pocket tohexstr r> tmpstrcat drop
+;
+
+\ Convert sbus unit (from decode-unit) to physical address using
+\ sbus node ranges property
+
+: sbus-unit>addr ( phys.lo phys.hi -- phys.lo phys.hi -1 | 0 )
+ " ranges" my-self ihandle>phandle
+ get-package-property 0= if ( phys.lo phys.hi prop prop-len )
+ begin
+ 2over swap drop 0 swap \ force phys.lo to zero for matching
+ 2swap ( unit.phys.lo unit.phys.hi 0 phys.hi res prop prop-len )
+ 0 -rot ( unit.phys.lo unit.phys.hi res prop prop-len )
+ 2 0 do
+ decode-int -rot >r >r ( unit.phys.lo unit.phys.hi res phys.x -- R: prop-len prop )
+ rot ( unit.phys.lo res phys.x phys.hi )
+ = if
+ 1+
+ then ( unit.phys.lo res )
+ r> r> ( unit.phys.lo res prop prop-len )
+ loop
+ rot ( prop prop-len res )
+ 2 = if \ did we match the unit address? if so, return the physical address
+ decode-phys 2swap 2drop 2swap ( unit.phys.lo unit.phys.hi phys.lo phys.hi )
+ drop 0 d+ \ force unit.phys.hi to zero and add address for final offset
+ -1 exit
+ else
+ decode-phys 2drop decode-int drop \ drop the size and carry on
+ then
+ dup 0= until
+ 2drop 2drop 0
+ then
+;
+
+: map-in-sbus ( phys.lo phys.hi size )
+ >r sbus-unit>addr if
+ r@ " map-in" $call-parent
+ then
+ r> drop
+;
+
+: map-out-sbus ( virt size )
+ " map-out" $call-parent
+;
+
+\ -------------------------------------------------------------------------
+\ SBus probe
+\ -------------------------------------------------------------------------
+
+: probe-self-sbus ( arg-adr arg-len reg-adr reg-len fcode-adr fcode-len -- )
+
+ 0 to probe-fcode?
+
+ ['] decode-unit-sbus catch if
+ 2drop 2drop 2drop 2drop
+ exit
+ then
+
+ h# 10000 map-in-sbus
+
+ dup cpeek if
+ dup h# f1 = swap h# fd = or if
+ new-device
+ >r set-args r>
+ dup 1 byte-load
+ finish-device
+
+ -1 to probe-fcode?
+ else
+ nip nip nip nip
+ ." Invalid FCode start byte" cr
+ then
+ else
+ nip nip nip nip
+ then
+
+ h# 10000 map-out-sbus
+;
diff --git a/qemu/roms/openbios/drivers/scsi.h b/qemu/roms/openbios/drivers/scsi.h
new file mode 100644
index 000000000..cb975fc70
--- /dev/null
+++ b/qemu/roms/openbios/drivers/scsi.h
@@ -0,0 +1,262 @@
+#ifndef _LINUX_SCSI_H
+#define _LINUX_SCSI_H
+
+/*
+ * This header file contains public constants and structures used by
+ * the scsi code for linux.
+ */
+
+/*
+ $Header: /usr/src/linux/include/linux/RCS/scsi.h,v 1.3 1993/09/24 12:20:33 drew Exp $
+
+ For documentation on the OPCODES, MESSAGES, and SENSE values,
+ please consult the SCSI standard.
+
+*/
+
+/*
+ * SCSI opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REZERO_UNIT 0x01
+#define REQUEST_SENSE 0x03
+#define FORMAT_UNIT 0x04
+#define READ_BLOCK_LIMITS 0x05
+#define REASSIGN_BLOCKS 0x07
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define SEEK_6 0x0b
+#define READ_REVERSE 0x0f
+#define WRITE_FILEMARKS 0x10
+#define SPACE 0x11
+#define INQUIRY 0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT 0x15
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define COPY 0x18
+#define ERASE 0x19
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RECEIVE_DIAGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+
+#define SET_WINDOW 0x24
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define SEEK_10 0x2b
+#define WRITE_VERIFY 0x2e
+#define VERIFY 0x2f
+#define SEARCH_HIGH 0x30
+#define SEARCH_EQUAL 0x31
+#define SEARCH_LOW 0x32
+#define SET_LIMITS 0x33
+#define PRE_FETCH 0x34
+#define READ_POSITION 0x34
+#define SYNCHRONIZE_CACHE 0x35
+#define LOCK_UNLOCK_CACHE 0x36
+#define READ_DEFECT_DATA 0x37
+#define MEDIUM_SCAN 0x38
+#define COMPARE 0x39
+#define COPY_VERIFY 0x3a
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define UPDATE_BLOCK 0x3d
+#define READ_LONG 0x3e
+#define WRITE_LONG 0x3f
+#define CHANGE_DEFINITION 0x40
+#define WRITE_SAME 0x41
+#define READ_TOC 0x43
+#define LOG_SELECT 0x4c
+#define LOG_SENSE 0x4d
+#define MODE_SELECT_10 0x55
+#define RESERVE_10 0x56
+#define RELEASE_10 0x57
+#define MODE_SENSE_10 0x5a
+#define PERSISTENT_RESERVE_IN 0x5e
+#define PERSISTENT_RESERVE_OUT 0x5f
+#define REPORT_LUNS 0xa0
+#define MOVE_MEDIUM 0xa5
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define WRITE_VERIFY_12 0xae
+#define SEARCH_HIGH_12 0xb0
+#define SEARCH_EQUAL_12 0xb1
+#define SEARCH_LOW_12 0xb2
+#define READ_ELEMENT_STATUS 0xb8
+#define SEND_VOLUME_TAG 0xb6
+#define WRITE_LONG_2 0xea
+#define READ_16 0x88
+#define WRITE_16 0x8a
+#define VERIFY_16 0x8f
+#define SERVICE_ACTION_IN 0x9e
+/* values for service action in */
+#define SAI_READ_CAPACITY_16 0x10
+
+#define SCSI_RETRY_10(c) ((c) == READ_6 || (c) == WRITE_6 || (c) == SEEK_6)
+
+/*
+ * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft
+ * T10/1561-D Revision 4 Draft dated 7th November 2002.
+ */
+#define SAM_STAT_GOOD 0x00
+#define SAM_STAT_CHECK_CONDITION 0x02
+#define SAM_STAT_CONDITION_MET 0x04
+#define SAM_STAT_BUSY 0x08
+#define SAM_STAT_INTERMEDIATE 0x10
+#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14
+#define SAM_STAT_RESERVATION_CONFLICT 0x18
+#define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */
+#define SAM_STAT_TASK_SET_FULL 0x28
+#define SAM_STAT_ACA_ACTIVE 0x30
+#define SAM_STAT_TASK_ABORTED 0x40
+
+/*
+ * Status codes
+ */
+
+#define GOOD 0x00
+#define CHECK_CONDITION 0x01
+#define CONDITION_GOOD 0x02
+#define BUSY 0x04
+#define INTERMEDIATE_GOOD 0x08
+#define INTERMEDIATE_C_GOOD 0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define COMMAND_TERMINATED 0x11
+#define QUEUE_FULL 0x14
+
+#define STATUS_MASK 0x3e
+
+/*
+ * SENSE KEYS
+ */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define BLANK_CHECK 0x08
+#define COPY_ABORTED 0x0a
+#define ABORTED_COMMAND 0x0b
+#define VOLUME_OVERFLOW 0x0d
+#define MISCOMPARE 0x0e
+
+
+/*
+ * DEVICE TYPES
+ */
+
+#define TYPE_DISK 0x00
+#define TYPE_TAPE 0x01
+#define TYPE_PRINTER 0x02
+#define TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define TYPE_ROM 0x05
+#define TYPE_SCANNER 0x06
+#define TYPE_MOD 0x07 /* Magneto-optical disk -
+ * - treated as TYPE_DISK */
+#define TYPE_MEDIUM_CHANGER 0x08
+#define TYPE_COMM 0x09 /* Communications device */
+#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define TYPE_NO_LUN 0x7f
+
+/*
+ * standard mode-select header prepended to all mode-select commands
+ *
+ * moved here from cdrom.h -- kraxel
+ */
+
+struct ccs_modesel_head
+{
+ uint8_t _r1; /* reserved */
+ uint8_t medium; /* device-specific medium type */
+ uint8_t _r2; /* reserved */
+ uint8_t block_desc_length; /* block descriptor length */
+ uint8_t density; /* device-specific density code */
+ uint8_t number_blocks_hi; /* number of blocks in this block desc */
+ uint8_t number_blocks_med;
+ uint8_t number_blocks_lo;
+ uint8_t _r3;
+ uint8_t block_length_hi; /* block length for blocks in this desc */
+ uint8_t block_length_med;
+ uint8_t block_length_lo;
+};
+
+/*
+ * MESSAGE CODES
+ */
+
+#define COMMAND_COMPLETE 0x00
+#define EXTENDED_MESSAGE 0x01
+#define EXTENDED_MODIFY_DATA_POINTER 0x00
+#define EXTENDED_SDTR 0x01
+#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */
+#define EXTENDED_WDTR 0x03
+#define SAVE_POINTERS 0x02
+#define RESTORE_POINTERS 0x03
+#define DISCONNECT 0x04
+#define INITIATOR_ERROR 0x05
+#define ABORT 0x06
+#define MESSAGE_REJECT 0x07
+#define NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define LINKED_CMD_COMPLETE 0x0a
+#define LINKED_FLG_CMD_COMPLETE 0x0b
+#define BUS_DEVICE_RESET 0x0c
+
+#define INITIATE_RECOVERY 0x0f /* SCSI-II only */
+#define RELEASE_RECOVERY 0x10 /* SCSI-II only */
+
+#define SIMPLE_QUEUE_TAG 0x20
+#define HEAD_OF_QUEUE_TAG 0x21
+#define ORDERED_QUEUE_TAG 0x22
+
+/*
+ * Here are some scsi specific ioctl commands which are sometimes useful.
+ */
+/* These are a few other constants only used by scsi devices */
+/* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395 */
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382 /* conflicts with CDROMAUDIOBUFSIZ */
+
+/* Used to turn on and off tagged queuing for scsi devices */
+
+#define SCSI_IOCTL_TAGGED_ENABLE 0x5383
+#define SCSI_IOCTL_TAGGED_DISABLE 0x5384
+
+/* Used to obtain the host number of a device. */
+#define SCSI_IOCTL_PROBE_HOST 0x5385
+
+/* Used to get the bus number for a device */
+#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
+
+/* Used to get the PCI location of a device */
+#define SCSI_IOCTL_GET_PCI 0x5387
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
+#endif
diff --git a/qemu/roms/openbios/drivers/tcx.fs b/qemu/roms/openbios/drivers/tcx.fs
new file mode 100644
index 000000000..af8991fd0
--- /dev/null
+++ b/qemu/roms/openbios/drivers/tcx.fs
@@ -0,0 +1,280 @@
+\
+\ Fcode payload for QEMU TCX graphics card
+\
+\ This is the Forth source for an Fcode payload to initialise
+\ the QEMU TCX graphics card.
+\
+\ (C) Copyright 2013 Mark Cave-Ayland
+\
+
+fcode-version3
+
+\
+\ Instead of using fixed values for the framebuffer address and the width
+\ and height, grab the ones passed in by QEMU/generated by OpenBIOS
+\
+
+: (find-xt) \ ( str len -- xt | -1 )
+ $find if
+ exit
+ else
+ 2drop
+ -1
+ then
+;
+
+: (is-openbios) \ ( -- true | false )
+ " openbios-video-width" (find-xt) -1 <> if
+ -1
+ else
+ 0
+ then
+;
+
+" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt
+" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt
+" depth-bits" (find-xt) cell+ value depth-bits-xt
+" line-bytes" (find-xt) cell+ value line-bytes-xt
+
+: openbios-video-width
+ (is-openbios) if
+ openbios-video-width-xt @
+ else
+ h# 400
+ then
+;
+
+: openbios-video-height
+ (is-openbios) if
+ openbios-video-height-xt @
+ else
+ h# 300
+ then
+;
+
+: depth-bits
+ (is-openbios) if
+ depth-bits-xt @
+ else
+ h# 8
+ then
+;
+
+: line-bytes
+ (is-openbios) if
+ line-bytes-xt @
+ else
+ h# 400
+ then
+;
+
+\
+\ Registers
+\
+
+h# 0 constant tcx-off-rom
+h# 10000 constant /tcx-off-rom
+
+h# 200000 constant tcx-off-cmap
+h# 4000 constant /tcx-off-cmap-24
+h# 4 constant /tcx-off-cmap-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-off-alt
+h# 8000 constant /tcx-off-alt-24
+h# 1 constant /tcx-off-alt-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# 701000 constant tcx-off-tec
+h# 1000 constant /tcx-off-tec
+
+h# 800000 constant tcx-off-dfb8
+h# 100000 constant /tcx-off-dfb8
+
+h# 2000000 constant tcx-off-dfb24
+h# 400000 constant /tcx-off-dfb24-24
+h# 1 constant /tcx-off-dfb24-8
+
+h# 4000000 constant tcx-off-stip
+h# 800000 constant /tcx-off-stip
+
+h# 6000000 constant tcx-off-blit
+h# 800000 constant /tcx-off-blit
+
+h# a000000 constant tcx-off-rdfb32
+h# 400000 constant /tcx-off-rdfb32-24
+h# 1 constant /tcx-off-rdfb32-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-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+
+;
+
+: tcx-8bit-reg
+ \ WARNING: order is important (at least to Solaris)
+ 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-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
+;
+
+: do-map-in ( offset size -- virt )
+ >r my-space r> " map-in" $call-parent
+;
+
+: do-map-out ( virt size )
+ " map-out" $call-parent
+;
+
+\
+\ DAC
+\
+
+-1 value tcx-dac
+-1 value /tcx-dac
+-1 value fb-addr
+
+: dac! ( data reg# -- )
+ >r dup 2dup bljoin r> tcx-dac + l!
+;
+
+external
+
+: color! ( r g b c# -- )
+ 0 dac! ( r g b )
+ swap rot ( b g r )
+ 4 dac! ( b g )
+ 4 dac! ( b )
+ 4 dac! ( )
+;
+
+headerless
+
+\
+\ Mapping
+\
+
+: dac-map
+ tcx-off-cmap /tcx-dac do-map-in to tcx-dac
+;
+
+: fb-map
+ tcx-off-dfb8 h# c0000 do-map-in to fb-addr
+;
+
+: map-regs
+ dac-map fb-map
+;
+
+\
+\ Installation
+\
+
+" SUNW,tcx" device-name
+" display" device-type
+
+: qemu-tcx-driver-install ( -- )
+ tcx-dac -1 = if
+ map-regs
+
+ \ Initial pallette taken from Sun's "Writing FCode Programs"
+ h# ff h# ff h# ff h# 0 color! \ Background white
+ h# 0 h# 0 h# 0 h# ff color! \ Foreground black
+ h# 64 h# 41 h# b4 h# 1 color! \ SUN-blue logo
+
+ fb-addr to frame-buffer-adr
+ default-font set-font
+
+ \ 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
+ then
+;
+
+: qemu-tcx-driver-init
+
+ \ Handle differences between 8-bit/24-bit mode
+ depth-bits 8 = if
+ tcx-8bit-reg
+ /tcx-off-cmap-8 to /tcx-dac
+ " true" encode-string " tcx-8-bit" property
+ else
+ tcx-24bit-reg
+ /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
+ \ mode before initialisation.
+ 8 depth-bits-xt !
+ openbios-video-width line-bytes-xt !
+ then
+
+ h# 1d encode-int " vbporch" property
+ h# a0 encode-int " hbporch" property
+ h# 06 encode-int " vsync" property
+ h# 88 encode-int " hsync" property
+ h# 03 encode-int " vfporch" property
+ h# 18 encode-int " hfporch" property
+ h# 03dfd240 encode-int " pixfreq" property
+ h# 3c encode-int " vfreq" property
+
+ openbios-video-height encode-int " height" property
+ openbios-video-width encode-int " width" property
+ line-bytes encode-int " linebytes" property
+
+ h# 39 encode-int 0 encode-int encode+ " intr" property
+ 5 encode-int " interrupts" property
+
+ ['] qemu-tcx-driver-install is-install
+;
+
+qemu-tcx-driver-init
+
+end0
diff --git a/qemu/roms/openbios/drivers/timer.c b/qemu/roms/openbios/drivers/timer.c
new file mode 100644
index 000000000..d6794ae9c
--- /dev/null
+++ b/qemu/roms/openbios/drivers/timer.c
@@ -0,0 +1,100 @@
+/*
+ * OpenBIOS native timer driver
+ *
+ * (C) 2004 Stefan Reinauer <stepan@openbios.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2
+ *
+ */
+
+#include "config.h"
+#include "drivers/drivers.h"
+#include "timer.h"
+#include "asm/io.h"
+
+#if defined(CONFIG_X86) || defined(CONFIG_AMD64)
+
+void setup_timers(void)
+{
+ /* nothing to do */
+}
+
+static void load_timer2(unsigned int ticks)
+{
+ /* Set up the timer gate, turn off the speaker */
+ outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ outb(TIMER2_SEL | WORD_ACCESS | MODE0 | BINARY_COUNT,
+ TIMER_MODE_PORT);
+ outb(ticks & 0xFF, TIMER2_PORT);
+ outb(ticks >> 8, TIMER2_PORT);
+}
+
+void udelay(unsigned int usecs)
+{
+ load_timer2((usecs * TICKS_PER_MS) / 1000);
+ while ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
+}
+
+unsigned long currticks(void)
+{
+ static unsigned long totticks = 0UL; /* High resolution */
+ unsigned long ticks = 0;
+ unsigned char portb = inb(PPC_PORTB);
+
+ /*
+ * Read the timer, and hope it hasn't wrapped around
+ * (call this again within 54ms), then restart it
+ */
+ outb(TIMER2_SEL | LATCH_COUNT, TIMER_MODE_PORT);
+ ticks = inb(TIMER2_PORT);
+ ticks |= inb(TIMER2_PORT) << 8;
+ outb(TIMER2_SEL | WORD_ACCESS | MODE0 | BINARY_COUNT,
+ TIMER_MODE_PORT);
+ outb(0, TIMER2_PORT);
+ outb(0, TIMER2_PORT);
+
+ /*
+ * Check if the timer was running. If not,
+ * result is rubbish and need to start it
+ */
+ if (portb & PPCB_T2GATE) {
+ totticks += (0x10000 - ticks);
+ } else {
+ /* Set up the timer gate, turn off the speaker */
+ outb((portb & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ }
+ return totticks / TICKS_PER_MS;
+}
+#endif
+
+#ifdef CONFIG_PPC
+
+void setup_timers(void)
+{
+ /* nothing to do */
+}
+
+/*
+ * TODO: pass via lb table
+ */
+unsigned long timer_freq = 10000000 / 4;
+
+void udelay(unsigned int usecs)
+{
+ unsigned long ticksperusec = timer_freq / 1000000;
+ _wait_ticks(ticksperusec * usecs);
+}
+
+#endif
+
+void ndelay(unsigned int nsecs)
+{
+ udelay((nsecs + 999) / 1000);
+}
+
+void mdelay(unsigned int msecs)
+{
+ udelay(msecs * 1000);
+}
diff --git a/qemu/roms/openbios/drivers/timer.h b/qemu/roms/openbios/drivers/timer.h
new file mode 100644
index 000000000..7e86db3fa
--- /dev/null
+++ b/qemu/roms/openbios/drivers/timer.h
@@ -0,0 +1,62 @@
+/* Taken from Etherboot */
+/* Defines for routines to implement a low-overhead timer for drivers */
+
+ /*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+/* Ports for the 8254 timer chip */
+#define TIMER2_PORT 0x42
+#define TIMER_MODE_PORT 0x43
+
+/* Meaning of the mode bits */
+#define TIMER0_SEL 0x00
+#define TIMER1_SEL 0x40
+#define TIMER2_SEL 0x80
+#define READBACK_SEL 0xC0
+
+#define LATCH_COUNT 0x00
+#define LOBYTE_ACCESS 0x10
+#define HIBYTE_ACCESS 0x20
+#define WORD_ACCESS 0x30
+
+#define MODE0 0x00
+#define MODE1 0x02
+#define MODE2 0x04
+#define MODE3 0x06
+#define MODE4 0x08
+#define MODE5 0x0A
+
+#define BINARY_COUNT 0x00
+#define BCD_COUNT 0x01
+
+/* Timers tick over at this rate */
+#define CLOCK_TICK_RATE 1193180U
+#define TICKS_PER_MS (CLOCK_TICK_RATE/1000)
+
+/* Parallel Peripheral Controller Port B */
+#define PPC_PORTB 0x61
+
+/* Meaning of the port bits */
+#define PPCB_T2OUT 0x20 /* Bit 5 */
+#define PPCB_SPKR 0x02 /* Bit 1 */
+#define PPCB_T2GATE 0x01 /* Bit 0 */
+
+extern void ndelay(unsigned int nsecs);
+extern void udelay(unsigned int usecs);
+extern void mdelay(unsigned int msecs);
+extern unsigned long currticks(void);
+extern unsigned long get_timer_freq(void);
+
+/* arch/ppc/timebase.S */
+void _wait_ticks(unsigned long nticks);
+
+#define TICKS_PER_SEC 1000
+
+#endif /* TIMER_H */
diff --git a/qemu/roms/openbios/drivers/usb.c b/qemu/roms/openbios/drivers/usb.c
new file mode 100644
index 000000000..c6e37174e
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usb.h b/qemu/roms/openbios/drivers/usb.h
new file mode 100644
index 000000000..2e23a1370
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usbhid.c b/qemu/roms/openbios/drivers/usbhid.c
new file mode 100644
index 000000000..a423278a8
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usbohci.c b/qemu/roms/openbios/drivers/usbohci.c
new file mode 100644
index 000000000..774164b0b
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usbohci.h b/qemu/roms/openbios/drivers/usbohci.h
new file mode 100644
index 000000000..690332871
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usbohci_private.h b/qemu/roms/openbios/drivers/usbohci_private.h
new file mode 100644
index 000000000..b3a723e21
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/usbohci_rh.c b/qemu/roms/openbios/drivers/usbohci_rh.c
new file mode 100644
index 000000000..55503be61
--- /dev/null
+++ b/qemu/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/qemu/roms/openbios/drivers/vga.fs b/qemu/roms/openbios/drivers/vga.fs
new file mode 100644
index 000000000..ec4c6c5f1
--- /dev/null
+++ b/qemu/roms/openbios/drivers/vga.fs
@@ -0,0 +1,204 @@
+\
+\ Fcode payload for QEMU VGA graphics card
+\
+\ This is the Forth source for an Fcode payload to initialise
+\ the QEMU VGA graphics card.
+\
+\ (C) Copyright 2013 Mark Cave-Ayland
+\
+
+fcode-version3
+
+\
+\ Dictionary lookups for words that don't have an FCode
+\
+
+: (find-xt) \ ( str len -- xt | -1 )
+ $find if
+ exit
+ else
+ -1
+ then
+;
+
+" openbios-video-width" (find-xt) cell+ value openbios-video-width-xt
+" openbios-video-height" (find-xt) cell+ value openbios-video-height-xt
+" depth-bits" (find-xt) cell+ value depth-bits-xt
+" line-bytes" (find-xt) cell+ value line-bytes-xt
+
+: openbios-video-width openbios-video-width-xt @ ;
+: openbios-video-height openbios-video-height-xt @ ;
+: depth-bits depth-bits-xt @ ;
+: line-bytes line-bytes-xt @ ;
+
+" fb8-fillrect" (find-xt) value fb8-fillrect-xt
+: fb8-fillrect fb8-fillrect-xt execute ;
+
+\
+\ IO port words
+\
+
+" ioc!" (find-xt) value ioc!-xt
+" iow!" (find-xt) value iow!-xt
+
+: ioc! ioc!-xt execute ;
+: iow! iow!-xt execute ;
+
+\
+\ VGA registers
+\
+
+h# 3c0 constant vga-addr
+h# 3c8 constant dac-write-addr
+h# 3c9 constant dac-data-addr
+
+: vga-color! ( r g b index -- )
+ \ Set the VGA colour registers
+ dac-write-addr ioc! rot
+ 2 >> dac-data-addr ioc! swap
+ 2 >> dac-data-addr ioc!
+ 2 >> dac-data-addr ioc!
+;
+
+\
+\ VBE registers
+\
+
+h# 0 constant VBE_DISPI_INDEX_ID
+h# 1 constant VBE_DISPI_INDEX_XRES
+h# 2 constant VBE_DISPI_INDEX_YRES
+h# 3 constant VBE_DISPI_INDEX_BPP
+h# 4 constant VBE_DISPI_INDEX_ENABLE
+h# 5 constant VBE_DISPI_INDEX_BANK
+h# 6 constant VBE_DISPI_INDEX_VIRT_WIDTH
+h# 7 constant VBE_DISPI_INDEX_VIRT_HEIGHT
+h# 8 constant VBE_DISPI_INDEX_X_OFFSET
+h# 9 constant VBE_DISPI_INDEX_Y_OFFSET
+h# a constant VBE_DISPI_INDEX_NB
+
+h# 0 constant VBE_DISPI_DISABLED
+h# 1 constant VBE_DISPI_ENABLED
+
+\
+\ Bochs VBE register writes
+\
+
+: vbe-iow! ( val addr -- )
+ h# 1ce iow!
+ h# 1d0 iow!
+;
+
+\
+\ Initialise Bochs VBE mode
+\
+
+: vbe-init ( -- )
+ h# 0 vga-addr ioc! \ Enable blanking
+ VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe-iow!
+ h# 0 VBE_DISPI_INDEX_X_OFFSET vbe-iow!
+ h# 0 VBE_DISPI_INDEX_Y_OFFSET vbe-iow!
+ openbios-video-width VBE_DISPI_INDEX_XRES vbe-iow!
+ openbios-video-height VBE_DISPI_INDEX_YRES vbe-iow!
+ depth-bits VBE_DISPI_INDEX_BPP vbe-iow!
+ VBE_DISPI_ENABLED VBE_DISPI_INDEX_ENABLE vbe-iow!
+ h# 0 vga-addr ioc!
+ h# 20 vga-addr ioc! \ Disable blanking
+;
+
+\
+\ PCI
+\
+
+" pci-bar>pci-region" (find-xt) value pci-bar>pci-region-xt
+: pci-bar>pci-region pci-bar>pci-region-xt execute ;
+
+h# 10 constant cfg-bar0 \ Framebuffer BAR
+-1 value fb-addr
+
+: map-fb ( -- )
+ cfg-bar0 pci-bar>pci-region \ ( pci-addr.lo pci-addr.hi size )
+ " pci-map-in" $call-parent
+ to fb-addr
+;
+
+\
+\ Publically visible words
+\
+
+external
+
+[IFDEF] CONFIG_MOL
+defer mol-color!
+
+\ Hook for MOL (see packages/molvideo.c)
+\
+\ Perhaps for neatness this there should be a separate molvga.fs
+\ but let's leave it here for now.
+
+: color! ( r g b index -- )
+ mol-color!
+;
+
+[ELSE]
+
+\ Standard VGA
+
+: color! ( r g b index -- )
+ vga-color!
+;
+
+[THEN]
+
+: fill-rectangle ( color_ind x y width height -- )
+ fb8-fillrect
+;
+
+: dimensions ( -- width height )
+ openbios-video-width
+ openbios-video-height
+;
+
+: set-colors ( table start count -- )
+ 0 do
+ over dup \ ( table start table table )
+ c@ swap 1+ \ ( table start r table-g )
+ dup c@ swap 1+ \ ( table start r g table-b )
+ c@ 3 pick \ ( table start r g b index )
+ color! \ ( table start )
+ 1+
+ swap 3 + swap \ ( table+3 start+1 )
+ loop
+;
+
+headerless
+
+\
+\ Installation
+\
+
+: qemu-vga-driver-install ( -- )
+ fb-addr -1 = if
+ map-fb fb-addr to frame-buffer-adr
+ default-font set-font
+
+ frame-buffer-adr encode-int " address" property
+
+ openbios-video-width openbios-video-height over char-width / over char-height /
+ fb8-install
+ then
+;
+
+: qemu-vga-driver-init
+
+ vbe-init
+ openbios-video-width encode-int " width" property
+ openbios-video-height encode-int " height" property
+ depth-bits encode-int " depth" property
+ line-bytes encode-int " linebytes" property
+
+ ['] qemu-vga-driver-install is-install
+;
+
+qemu-vga-driver-init
+
+end0
diff --git a/qemu/roms/openbios/drivers/vga.h b/qemu/roms/openbios/drivers/vga.h
new file mode 100644
index 000000000..a37e66e62
--- /dev/null
+++ b/qemu/roms/openbios/drivers/vga.h
@@ -0,0 +1,231 @@
+/*
+ *
+ * modified
+ * by Steve M. Gehlbach <steve@kesa.com>
+ *
+ * Originally from linux/drivers/video/vga16.c by
+ * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef VGA_H_INCL
+#define VGA_H_INCL 1
+
+#include "drivers/vga.h"
+
+//#include <cpu/p5/io.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define __u32 u32
+
+#define VERROR -1
+#define CHAR_HEIGHT 16
+#define LINES 25
+#define COLS 80
+
+// macros for writing to vga regs
+#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
+#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
+#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
+#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
+u8 read_seq_b(u16 addr);
+u8 read_gra_b(u16 addr);
+u8 read_crtc_b(u16 addr);
+u8 read_att_b(u16 addr);
+
+
+#ifdef VGA_HARDWARE_FIXUP
+void vga_hardware_fixup(void);
+#else
+#define vga_hardware_fixup() do{} while(0)
+#endif
+
+#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define SYNC_EXT 4 /* external sync */
+#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+
+#define SYNC_ON_GREEN 32 /* sync on green */
+
+#define VMODE_NONINTERLACED 0 /* non interlaced */
+#define VMODE_INTERLACED 1 /* interlaced */
+#define VMODE_DOUBLE 2 /* double scan */
+#define VMODE_MASK 255
+
+#define VMODE_YWRAP 256 /* ywrap instead of panning */
+#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+
+#define MIS_R 0x3CC // Misc Output Read Register
+#define MIS_W 0x3C2 // Misc Output Write Register
+
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+#define DAC_REG 0x3C8 /* DAC register */
+#define DAC_VAL 0x3C9 /* DAC value */
+
+/* standard VGA indexes max counts */
+#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
+ // the remainder are not in the par array
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+// text attributes
+#define VGA_ATTR_CLR_RED 0x4
+#define VGA_ATTR_CLR_GRN 0x2
+#define VGA_ATTR_CLR_BLU 0x1
+#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
+#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
+#define VGA_ATTR_CLR_BLK 0
+#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_BNK 0x80
+#define VGA_ATTR_ITN 0x08
+
+/*
+ * vga register parameters
+ * these are copied to the
+ * registers.
+ *
+ */
+struct vga_par {
+ u8 crtc[CRTC_C];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc; // the misc register, MIS_W
+ u8 vss;
+};
+
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* sync polarity */
+ __u32 vmode; /* interlaced etc */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+#endif
diff --git a/qemu/roms/openbios/drivers/vga_load_regs.c b/qemu/roms/openbios/drivers/vga_load_regs.c
new file mode 100644
index 000000000..dda6b798a
--- /dev/null
+++ b/qemu/roms/openbios/drivers/vga_load_regs.c
@@ -0,0 +1,496 @@
+#include "asm/io.h"
+#include "drivers/vga.h"
+#include "vga.h"
+
+/*
+ * $Id$
+ * $Source$
+ *
+ * from the Linux kernel code base.
+ * orig by Ben Pfaff and Petr Vandrovec.
+ *
+ * modified by
+ * Steve M. Gehlbach <steve@kesa.com>
+ *
+ * NOTE: to change the horiz and vertical pixels,
+ * change the xres,yres,xres_virt,yres_virt setting
+ * in the screeninfo structure below. You may also need
+ * to change the border settings as well.
+ *
+ * Convert the screeninfo structure to data for
+ * writing to the vga registers
+ *
+ */
+
+// prototypes
+static int vga_decode_var(const struct screeninfo *var, struct vga_par *par);
+static int vga_set_regs(const struct vga_par *par);
+
+u8 read_seq_b(u16 addr) {
+ outb(addr,SEQ_I);
+ return inb(SEQ_D);
+}
+u8 read_gra_b(u16 addr) {
+ outb(addr,GRA_I);
+ return inb(GRA_D);
+}
+u8 read_crtc_b(u16 addr) {
+ outb(addr,CRT_IC);
+ return inb(CRT_DC);
+}
+u8 read_att_b(u16 addr) {
+ inb(IS1_RC);
+ inb(0x80);
+ outb(addr,ATT_IW);
+ return inb(ATT_R);
+}
+
+
+/*
+From: The Frame Buffer Device
+by Geert Uytterhoeven <geert@linux-m68k.org>
+in the linux kernel docs.
+
+The following picture summarizes all timings. The horizontal retrace time is
+the sum of the left margin, the right margin and the hsync length, while the
+vertical retrace time is the sum of the upper margin, the lower margin and the
+vsync length.
+
+ +----------+---------------------------------------------+----------+-------+
+ | | ^ | | |
+ | | |upper_margin | | |
+ | | | | | |
+ +----------###############################################----------+-------+
+ | # ^ # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | left # | # right | hsync |
+ | margin # | xres # margin | len |
+ |<-------->#<---------------+--------------------------->#<-------->|<----->|
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # |yres # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ | # | # | |
+ +----------###############################################----------+-------+
+ | | ^ | | |
+ | | |lower_margin | | |
+ | | | | | |
+ +----------+---------------------------------------------+----------+-------+
+ | | ^ | | |
+ | | |vsync_len | | |
+ | | | | | |
+ +----------+---------------------------------------------+----------+-------+
+
+All horizontal timings are in number of dotclocks
+(in picoseconds, 1E-12 s), and vertical timings in number of scanlines.
+
+The vga uses the following fields:
+
+ - pixclock: pixel clock in ps (pico seconds)
+ - xres,yres,xres_v,yres_v
+ - left_margin: time from sync to picture
+ - right_margin: time from picture to sync
+ - upper_margin: time from sync to picture
+ - lower_margin: time from picture to sync
+ - hsync_len: length of horizontal sync
+ - vsync_len: length of vertical sync
+
+*/
+
+/* our display parameters per the above */
+
+static const struct screeninfo vga_settings = {
+ 640,400,640,400,/* xres,yres,xres_virt,yres_virt */
+ 0,0, /* xoffset,yoffset */
+ 4, /* bits_per_pixel NOT USED*/
+ 0, /* greyscale ? */
+ {0,0,0}, /* R */
+ {0,0,0}, /* G */
+ {0,0,0}, /* B */
+ {0,0,0}, /* transparency */
+ 0, /* standard pixel format */
+ 0, // activate now
+ -1,-1, // height and width in mm
+ 0, // accel flags
+ 39721, // pixclock: 79442 -> 12.587 Mhz (NOT USED)
+ // 70616 -> 14.161
+ // 39721 -> 25.175
+ // 35308 -> 28.322
+
+ 48, 16, 39, 8, // margins left,right,upper,lower
+ 96, // hsync length
+ 2, // vsync length
+ 0, // sync polarity
+ 0, // non interlaced, single mode
+ {0,0,0,0,0,0} // compatibility
+};
+
+// ALPHA-MODE
+// Hard coded to BIOS VGA mode 3 (alpha color text)
+// screen size settable in screeninfo structure
+
+static int vga_decode_var(const struct screeninfo *var,
+ struct vga_par *par)
+{
+ u8 VgaAttributeTable[16] =
+ { 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x014, 0x007, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F};
+
+ u32 xres, right, hslen, left, xtotal;
+ u32 yres, lower, vslen, upper, ytotal;
+ u32 vxres, xoffset, vyres, yoffset;
+ u32 pos;
+ u8 r7, rMode;
+ int i;
+
+ xres = (var->xres + 7) & ~7;
+ vxres = (var->xres_virtual + 0xF) & ~0xF;
+ xoffset = (var->xoffset + 7) & ~7;
+ left = (var->left_margin + 7) & ~7;
+ right = (var->right_margin + 7) & ~7;
+ hslen = (var->hsync_len + 7) & ~7;
+
+ if (vxres < xres)
+ vxres = xres;
+ if (xres + xoffset > vxres)
+ xoffset = vxres - xres;
+
+ xres >>= 3;
+ right >>= 3;
+ hslen >>= 3;
+ left >>= 3;
+ vxres >>= 3;
+ xtotal = xres + right + hslen + left;
+ if (xtotal >= 256)
+ return VERROR; //xtotal too big
+ if (hslen > 32)
+ return VERROR; //hslen too big
+ if (right + hslen + left > 64)
+ return VERROR; //hblank too big
+ par->crtc[CRTC_H_TOTAL] = xtotal - 5;
+ par->crtc[CRTC_H_BLANK_START] = xres - 1;
+ par->crtc[CRTC_H_DISP] = xres - 1;
+ pos = xres + right;
+ par->crtc[CRTC_H_SYNC_START] = pos;
+ pos += hslen;
+ par->crtc[CRTC_H_SYNC_END] = (pos & 0x1F) | 0x20 ; //<--- stpc text mode p178
+ pos += left - 2; /* blank_end + 2 <= total + 5 */
+ par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
+ if (pos & 0x20)
+ par->crtc[CRTC_H_SYNC_END] |= 0x80;
+
+ yres = var->yres;
+ lower = var->lower_margin;
+ vslen = var->vsync_len;
+ upper = var->upper_margin;
+ vyres = var->yres_virtual;
+ yoffset = var->yoffset;
+
+ if (yres > vyres)
+ vyres = yres;
+ if (vxres * vyres > 65536) {
+ vyres = 65536 / vxres;
+ if (vyres < yres)
+ return VERROR; // out of memory
+ }
+ if (yoffset + yres > vyres)
+ yoffset = vyres - yres;
+
+ if (var->vmode & VMODE_DOUBLE) {
+ yres <<= 1;
+ lower <<= 1;
+ vslen <<= 1;
+ upper <<= 1;
+ }
+ ytotal = yres + lower + vslen + upper;
+ if (ytotal > 1024) {
+ ytotal >>= 1;
+ yres >>= 1;
+ lower >>= 1;
+ vslen >>= 1;
+ upper >>= 1;
+ rMode = 0x04;
+ } else
+ rMode = 0x00;
+ if (ytotal > 1024)
+ return VERROR; //ytotal too big
+ if (vslen > 16)
+ return VERROR; //vslen too big
+ par->crtc[CRTC_V_TOTAL] = ytotal - 2;
+ r7 = 0x10; /* disable linecompare */
+ if (ytotal & 0x100) r7 |= 0x01;
+ if (ytotal & 0x200) r7 |= 0x20;
+ par->crtc[CRTC_PRESET_ROW] = 0;
+
+
+// GMODE <--> ALPHA-MODE
+// default using alpha mode so we need to set char rows= CHAR_HEIGHT-1
+ par->crtc[CRTC_MAX_SCAN] = 0x40 | (CHAR_HEIGHT-1); /* 16 scanlines, linecmp max*/
+
+ if (var->vmode & VMODE_DOUBLE)
+ par->crtc[CRTC_MAX_SCAN] |= 0x80;
+ par->crtc[CRTC_CURSOR_START] = 0x00; // curs enabled, start line = 0
+ par->crtc[CRTC_CURSOR_END] = CHAR_HEIGHT-1; // end line = 12
+ pos = yoffset * vxres + (xoffset >> 3);
+ par->crtc[CRTC_START_HI] = pos >> 8;
+ par->crtc[CRTC_START_LO] = pos & 0xFF;
+ par->crtc[CRTC_CURSOR_HI] = 0x00;
+ par->crtc[CRTC_CURSOR_LO] = 0x00;
+ pos = yres - 1;
+ par->crtc[CRTC_V_DISP_END] = pos & 0xFF;
+ par->crtc[CRTC_V_BLANK_START] = pos & 0xFF;
+ if (pos & 0x100)
+ r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */
+ if (pos & 0x200) {
+ r7 |= 0x40; /* 0x40 -> DISP_END */
+ par->crtc[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
+ }
+ pos += lower;
+ par->crtc[CRTC_V_SYNC_START] = pos & 0xFF;
+ if (pos & 0x100)
+ r7 |= 0x04;
+ if (pos & 0x200)
+ r7 |= 0x80;
+ pos += vslen;
+ par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled reg write prot, IRQ */
+ pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
+ par->crtc[CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
+ but some SVGA chips requires all 8 bits to set */
+ if (vxres >= 512)
+ return VERROR; //vxres too long
+ par->crtc[CRTC_OFFSET] = vxres >> 1;
+
+ // put the underline off of the character, necessary in alpha color mode
+ par->crtc[CRTC_UNDERLINE] = 0x1f;
+
+ par->crtc[CRTC_MODE] = rMode | 0xA3; // word mode
+ par->crtc[CRTC_LINE_COMPARE] = 0xFF;
+ par->crtc[CRTC_OVERFLOW] = r7;
+
+
+ // not used ??
+ par->vss = 0x00; /* 3DA */
+
+ for (i = 0x00; i < 0x10; i++) {
+ par->atc[i] = VgaAttributeTable[i];
+ }
+ // GMODE <--> ALPHA-MODE
+ par->atc[ATC_MODE] = 0x0c; // text mode
+
+ par->atc[ATC_OVERSCAN] = 0x00; // no border
+ par->atc[ATC_PLANE_ENABLE] = 0x0F;
+ par->atc[ATC_PEL] = xoffset & 7;
+ par->atc[ATC_COLOR_PAGE] = 0x00;
+
+ par->misc = 0x67; /* enable CPU, ports 0x3Dx, positive sync*/
+ if (var->sync & SYNC_HOR_HIGH_ACT)
+ par->misc &= ~0x40;
+ if (var->sync & SYNC_VERT_HIGH_ACT)
+ par->misc &= ~0x80;
+
+ par->seq[SEQ_CLOCK_MODE] = 0x01; //8-bit char; 0x01=alpha mode
+ par->seq[SEQ_PLANE_WRITE] = 0x03; // just char/attr plane
+ par->seq[SEQ_CHARACTER_MAP] = 0x00;
+ par->seq[SEQ_MEMORY_MODE] = 0x02; // A/G bit not used in stpc; O/E on, C4 off
+
+ par->gdc[GDC_SR_VALUE] = 0x00;
+ // bits set in the SR_EN regs will enable set/reset action
+ // based on the bit settings in the SR_VAL register
+ par->gdc[GDC_SR_ENABLE] = 0x00;
+ par->gdc[GDC_COMPARE_VALUE] = 0x00;
+ par->gdc[GDC_DATA_ROTATE] = 0x00;
+ par->gdc[GDC_PLANE_READ] = 0;
+ par->gdc[GDC_MODE] = 0x10; //Okay
+
+ // GMODE <--> ALPHA-MMODE
+ par->gdc[GDC_MISC] = 0x0e; // b0=0 ->alpha mode; memory at 0xb8000
+
+ par->gdc[GDC_COMPARE_MASK] = 0x00;
+ par->gdc[GDC_BIT_MASK] = 0xFF;
+
+ return 0;
+}
+
+//
+// originally from the stpc web site
+//
+static const unsigned char VgaLookupTable[3 * 0x3f + 3] = {
+ // Red Green Blue
+ 0x000, 0x000, 0x000, // 00h
+ 0x000, 0x000, 0x02A, // 01h
+ 0x000, 0x02A, 0x000, // 02h
+ 0x000, 0x02A, 0x02A, // 03h
+ 0x02A, 0x000, 0x000, // 04h
+ 0x02A, 0x000, 0x02A, // 05h
+ 0x02A, 0x02A, 0x000, // 06h
+ 0x02A, 0x02A, 0x02A, // 07h
+ 0x000, 0x000, 0x015, // 08h
+ 0x000, 0x000, 0x03F, // 09h
+ 0x000, 0x02A, 0x015, // 0Ah
+ 0x000, 0x02A, 0x03F, // 0Bh
+ 0x02A, 0x000, 0x015, // 0Ch
+ 0x02A, 0x000, 0x03F, // 0Dh
+ 0x02A, 0x02A, 0x015, // 0Eh
+ 0x02A, 0x02A, 0x03F, // 0Fh
+ 0x000, 0x015, 0x000, // 10h
+ 0x000, 0x015, 0x02A, // 11h
+ 0x000, 0x03F, 0x000, // 12h
+ 0x000, 0x03F, 0x02A, // 13h
+ 0x02A, 0x015, 0x000, // 14h
+ 0x02A, 0x015, 0x02A, // 15h
+ 0x02A, 0x03F, 0x000, // 16h
+ 0x02A, 0x03F, 0x02A, // 17h
+ 0x000, 0x015, 0x015, // 18h
+ 0x000, 0x015, 0x03F, // 19h
+ 0x000, 0x03F, 0x015, // 1Ah
+ 0x000, 0x03F, 0x03F, // 1Bh
+ 0x02A, 0x015, 0x015, // 1Ch
+ 0x02A, 0x015, 0x03F, // 1Dh
+ 0x02A, 0x03F, 0x015, // 1Eh
+ 0x02A, 0x03F, 0x03F, // 1Fh
+ 0x015, 0x000, 0x000, // 20h
+ 0x015, 0x000, 0x02A, // 21h
+ 0x015, 0x02A, 0x000, // 22h
+ 0x015, 0x02A, 0x02A, // 23h
+ 0x03F, 0x000, 0x000, // 24h
+ 0x03F, 0x000, 0x02A, // 25h
+ 0x03F, 0x02A, 0x000, // 26h
+ 0x03F, 0x02A, 0x02A, // 27h
+ 0x015, 0x000, 0x015, // 28h
+ 0x015, 0x000, 0x03F, // 29h
+ 0x015, 0x02A, 0x015, // 2Ah
+ 0x015, 0x02A, 0x03F, // 2Bh
+ 0x03F, 0x000, 0x015, // 2Ch
+ 0x03F, 0x000, 0x03F, // 2Dh
+ 0x03F, 0x02A, 0x015, // 2Eh
+ 0x03F, 0x02A, 0x03F, // 2Fh
+ 0x015, 0x015, 0x000, // 30h
+ 0x015, 0x015, 0x02A, // 31h
+ 0x015, 0x03F, 0x000, // 32h
+ 0x015, 0x03F, 0x02A, // 33h
+ 0x03F, 0x015, 0x000, // 34h
+ 0x03F, 0x015, 0x02A, // 35h
+ 0x03F, 0x03F, 0x000, // 36h
+ 0x03F, 0x03F, 0x02A, // 37h
+ 0x015, 0x015, 0x015, // 38h
+ 0x015, 0x015, 0x03F, // 39h
+ 0x015, 0x03F, 0x015, // 3Ah
+ 0x015, 0x03F, 0x03F, // 3Bh
+ 0x03F, 0x015, 0x015, // 3Ch
+ 0x03F, 0x015, 0x03F, // 3Dh
+ 0x03F, 0x03F, 0x015, // 3Eh
+ 0x03F, 0x03F, 0x03F, // 3Fh
+};
+
+/*
+ * From the Linux kernel.
+ * orig by Ben Pfaff and Petr Vandrovec.
+ * see the note in the vga.h for attribution.
+ *
+ * modified by
+ * Steve M. Gehlbach <steve@kesa.com>
+ * for the linuxbios project
+ *
+ * Write the data in the vga parameter structure
+ * to the vga registers, along with other default
+ * settings.
+ *
+ */
+static int vga_set_regs(const struct vga_par *par)
+{
+ int i;
+
+ /* update misc output register */
+ outb(par->misc, MIS_W);
+
+ /* synchronous reset on */
+ outb(0x00, SEQ_I);
+ outb(0x00, SEQ_D);
+
+ /* write sequencer registers */
+ outb(1, SEQ_I);
+ outb(par->seq[1] | 0x20, SEQ_D); // blank display
+ for (i = 2; i < SEQ_C; i++) {
+ outb(i, SEQ_I);
+ outb(par->seq[i], SEQ_D);
+ }
+
+ /* synchronous reset off */
+ outb(0x00, SEQ_I);
+ outb(0x03, SEQ_D);
+
+ /* deprotect CRT registers 0-7 */
+ outb(0x11, CRT_IC);
+ outb(par->crtc[0x11], CRT_DC);
+
+ /* write CRT registers */
+ for (i = 0; i < CRTC_C; i++) {
+ outb(i, CRT_IC);
+ outb(par->crtc[i], CRT_DC);
+ }
+ /* write graphics controller registers */
+ for (i = 0; i < GRA_C; i++) {
+ outb(i, GRA_I);
+ outb(par->gdc[i], GRA_D);
+ }
+
+ /* write attribute controller registers */
+ for (i = 0; i < ATT_C; i++) {
+ inb(IS1_RC); /* reset flip-flop */
+ inb(0x80); //delay
+ outb(i, ATT_IW);
+ inb(0x80); //delay
+
+ outb(par->atc[i], ATT_IW);
+ inb(0x80); //delay
+ }
+
+ // initialize the color table
+ outb(0, PEL_IW);
+ i = 0;
+ // length is a magic number right now
+ while ( i < (0x3f*3 + 3) ) {
+ outb(VgaLookupTable[i++], PEL_D);
+ outb(VgaLookupTable[i++], PEL_D);
+ outb(VgaLookupTable[i++], PEL_D);
+ }
+
+ outb(0x0ff, PEL_MSK); // palette mask
+
+ // very important
+ // turn on video, disable palette access
+ inb(IS1_RC); /* reset flip-flop */
+ inb(0x80); //delay
+ outb(0x20, ATT_IW);
+
+ /* Wait for screen to stabilize. */
+ //for(i=0;i<1000;i++) { inb(0x80); }
+
+ outb(0x01, SEQ_I); // unblank display
+ outb(par->seq[1], SEQ_D);
+
+// turn on display, disable access to attr palette
+ inb(IS1_RC);
+ outb(0x20, ATT_IW);
+
+return 0;
+}
+
+void
+vga_load_regs(void)
+{
+ struct vga_par par;
+
+ if (vga_decode_var(&vga_settings, &par) == 0) {
+ vga_set_regs(&par);
+ }
+}
diff --git a/qemu/roms/openbios/drivers/vga_set_mode.c b/qemu/roms/openbios/drivers/vga_set_mode.c
new file mode 100644
index 000000000..339ad2966
--- /dev/null
+++ b/qemu/roms/openbios/drivers/vga_set_mode.c
@@ -0,0 +1,148 @@
+/*
+ * $Id$
+ * $Source$
+ *
+ * by
+ * Steve M. Gehlbach <steve@kesa.com>
+ *
+ * These routines set graphics mode and alpha mode
+ * for switching back and forth.
+ *
+ * Register settings are
+ * more or less as follows:
+ *
+ * Register Graphics Alpha
+ * 16 color
+ * ------------------------------------------------
+ * GDC_MODE 0x00 0x10
+ * GDC_MISC 0x05 0x0e
+ * SEQ_MEMORY_MODE 0x06 0x02
+ * SEQ_PLANE_WRITE 0x0f 0x03
+ * CRTC_CURSOR_START 0x20 0x00
+ * CRTC_CURSOR_END 0x00 CHAR_HEIGHT-1
+ * CRTC_MODE 0xe3 0xa3
+ * CRTC_MAX_SCAN 0x40 0x40 | CHAR_HEIGHT-1
+ * ATC_MODE 0x01 0x0c
+ *
+ */
+
+#include "asm/io.h"
+#include "vga.h"
+
+void vga_set_gmode (void) {
+ u8 byte;
+
+ byte = read_att_b(ATC_MODE) & ~0x0f;
+ write_att(byte|0x1, ATC_MODE);
+//
+// display is off at this point
+
+ byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf;
+ write_seq(byte|0xf,SEQ_PLANE_WRITE); // all planes
+ byte = read_seq_b(SEQ_MEMORY_MODE);
+ write_seq(byte|4,SEQ_MEMORY_MODE);
+
+ byte = read_gra_b(GDC_MODE) & ~0x10;
+ write_gra(byte,GDC_MODE);
+ write_gra(0x05, GDC_MISC);
+
+ write_crtc(0x20, CRTC_CURSOR_START);
+ write_crtc(0x00, CRTC_CURSOR_END);
+ byte = read_crtc_b(CRTC_MODE) & ~0xe0;
+ write_crtc(byte|0xe0, CRTC_MODE);
+ byte = read_crtc_b(CRTC_MAX_SCAN) & ~0x01f;
+ write_crtc(byte, CRTC_MAX_SCAN);
+
+ byte = inb(MIS_R); // get 3c2 value by reading 3cc
+ outb(byte & ~0xc,MIS_W); // clear last bits to set 25Mhz clock and low page
+
+
+// turn on display, disable access to attr palette
+ inb(IS1_RC);
+ outb(0x20, ATT_IW);
+}
+
+void vga_set_amode (void) {
+ u8 byte;
+ write_att(0x0c, ATC_MODE);
+
+ //reset palette to normal in the case it was changed
+ write_att(0x0, ATC_COLOR_PAGE);
+//
+// display is off at this point
+
+ write_seq(0x3,SEQ_PLANE_WRITE); // planes 0 & 1
+ byte = read_seq_b(SEQ_MEMORY_MODE) & ~0x04;
+ write_seq(byte,SEQ_MEMORY_MODE);
+
+ byte = read_gra_b(GDC_MODE) & ~0x60;
+ write_gra(byte|0x10,GDC_MODE);
+
+ write_gra(0x0e, GDC_MISC);
+
+ write_crtc(0x00, CRTC_CURSOR_START);
+ write_crtc(CHAR_HEIGHT-1, CRTC_CURSOR_END);
+
+ byte = read_crtc_b(CRTC_MODE) & ~0xe0;
+ write_crtc(byte|0xa0, CRTC_MODE);
+ byte = read_crtc_b(CRTC_MAX_SCAN) & ~0x01f;
+ write_crtc(byte | (CHAR_HEIGHT-1), CRTC_MAX_SCAN);
+
+
+// turn on display, disable access to attr palette
+ inb(IS1_RC);
+ outb(0x20, ATT_IW);
+}
+
+/*
+ * by Steve M. Gehlbach, Ph.D. <steve@kesa.com>
+ *
+ * vga_font_load loads a font into font memory. It
+ * assumes alpha mode has been set.
+ *
+ * The font load code follows technique used
+ * in the tiara project, which came from
+ * the Universal Talkware Boot Loader,
+ * http://www.talkware.net.
+ */
+
+void vga_font_load(unsigned char *vidmem, const unsigned char *font, int height, int num_chars) {
+
+/* Note: the font table is 'height' long but the font storage area
+ * is 32 bytes long.
+ */
+
+ int i,j;
+ u8 byte;
+
+ // set sequencer map 2, odd/even off
+ byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf;
+ write_seq(byte|4,SEQ_PLANE_WRITE);
+ byte = read_seq_b(SEQ_MEMORY_MODE);
+ write_seq(byte|4,SEQ_MEMORY_MODE);
+
+ // select graphics map 2, odd/even off, map starts at 0xa0000
+ write_gra(2,GDC_PLANE_READ);
+ byte = read_gra_b(GDC_MODE) & ~0x10;
+ write_gra(byte,GDC_MODE);
+ write_gra(0,GDC_MISC);
+
+ for (i = 0 ; i < num_chars ; i++) {
+ for (j = 0 ; j < height ; j++) {
+ vidmem[i*32+j] = font[i*16+j];
+ }
+ }
+
+ // set sequencer back to maps 0,1, odd/even on
+ byte = read_seq_b(SEQ_PLANE_WRITE) & ~0xf;
+ write_seq(byte|3,SEQ_PLANE_WRITE);
+ byte = read_seq_b(SEQ_MEMORY_MODE) & ~0x4;
+ write_seq(byte,SEQ_MEMORY_MODE);
+
+ // select graphics back to map 0,1, odd/even on
+ write_gra(0,GDC_PLANE_READ);
+ byte = read_gra_b(GDC_MODE);
+ write_gra(byte|0x10,GDC_MODE);
+ write_gra(0xe,GDC_MISC);
+
+}