diff -urN qemu.orig/Makefile.target qemu.usb/Makefile.target
--- qemu.orig/Makefile.target	2004-06-15 03:02:36.000000000 +0100
+++ qemu.usb/Makefile.target	2004-06-17 19:35:51.000000000 +0100
@@ -232,7 +232,7 @@
 endif
 
 # must use static linking to avoid leaving stuff in virtual address space
-VL_OBJS=vl.o osdep.o block.o monitor.o pci.o
+VL_OBJS=vl.o osdep.o block.o monitor.o pci.o usb-ohci.o
 
 ifeq ($(TARGET_ARCH), i386)
 # Hardware support
diff -urN qemu.orig/hw/pc.c qemu.usb/hw/pc.c
--- qemu.orig/hw/pc.c	2004-06-15 03:02:36.000000000 +0100
+++ qemu.usb/hw/pc.c	2004-06-18 14:29:00.000000000 +0100
@@ -432,6 +432,9 @@
             pci_ne2000_init(&nd_table[i]);
         }
         pci_piix3_ide_init(bs_table);
+
+        /* Shut up! */
+        usb_ohci_init("Apple KeyLargo/Intrepid", 0x106b, 0x003f, 0, -1);
     } else {
         nb_nics1 = nb_nics;
         if (nb_nics1 > NE2000_NB_MAX)
diff -urN qemu.orig/hw/usb-ohci.c qemu.usb/hw/usb-ohci.c
--- qemu.orig/hw/usb-ohci.c	1970-01-01 01:00:00.000000000 +0100
+++ qemu.usb/hw/usb-ohci.c	2004-06-19 17:44:55.000000000 +0100
@@ -0,0 +1,586 @@
+/*
+ * QEMU USB OHCI Interface v0.1
+ * Copyright (c) 2004 Gianni Tedesco
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * TODO:
+ *  o Dynamically manage hub ports
+ *  o Implement actual data transfers
+*/
+
+static const char copyright[] =
+	"usb-ohci.c: v0.1 Copyright (c) Gianni Tedesco 2004";
+
+#include "vl.h"
+#include "cpu.h"
+
+#if 0
+#define dprintf(...)
+#else
+#define dprintf printf
+#endif
+
+/* Number of Downstream Ports on the root hub, if you change this
+ * you need to add more status register words to the 'opreg' array
+ */
+#define OHCI_NDP 8
+
+struct ohci {
+	struct PCIDevice pci_dev;
+	target_phys_addr_t mem_base;
+	int mem;
+
+	/* OHCI state */
+	/* Control partition */
+	uint32_t ctl, status;
+	uint32_t intr_status;
+	uint32_t intr;
+
+	/* memory pointer partition */
+	uint32_t hcca;
+	uint32_t ctrl_head;
+	uint32_t bulk_head;
+
+	/* Frame counter partition */
+	uint32_t fmi;
+	uint32_t pstart;
+
+	/* Root Hub partition */
+	uint32_t rhdesc_a, rhdesc_b;
+	uint32_t rhstatus;
+	uint32_t rhport[OHCI_NDP];
+};
+
+struct ohci_hcca {
+	uint32_t intr[32];
+	uint16_t frame, pad;
+	uint32_t done;
+};
+
+extern struct ohci_opreg opreg[];
+struct ohci_opreg {
+	const char *name;
+	uint32_t (*read)(struct ohci *ohci, uint32_t ofs);
+	void (*write)(struct ohci *ohci, uint32_t ofs, uint32_t val);
+};
+
+#define OHCI_CTL_IR		(1<<8)
+
+#define OHCI_STATUS_HCR		(1<<0)
+#define OHCI_STATUS_SOC		((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO		(1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WDH		(1<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF		(1<<2) /* Start of frame */
+#define OHCI_INTR_RD		(1<<3) /* Resume detect */
+#define OHCI_INTR_UE		(1<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO		(1<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC		(1<<6) /* Root hub status change */
+#define OHCI_INTR_OC		(1<<30) /* Ownership change */
+#define OHCI_INTR_MIE		(1<<31) /* Master Interrupt Enable */
+
+#define OHCI_HCCA_SIZE		0x100
+#define OHCI_HCCA_MASK		0xffffff00
+
+#define OHCI_FMI_FI		0x00003fff
+#define OHCI_FMI_FSMPS		0xffff0000
+#define OHCI_FMI_FIT		0x80000000
+
+#define OHCI_LS_THRESH		0x628
+
+#define OHCI_RHA_NPS		(1<<9)
+
+#define OHCI_RHS_LPS		(1<<0)
+#define OHCI_RHS_OCI		(1<<1)
+#define OHCI_RHS_DRWE		(1<<15)
+#define OHCI_RHS_LPSC		(1<<16)
+#define OHCI_RHS_OCIC		(1<<17)
+#define OHCI_RHS_CRWE		(1<<31)
+
+#define OHCI_PORT_CCS		(1<<0)
+#define OHCI_PORT_PES		(1<<1)
+#define OHCI_PORT_PSS		(1<<2)
+#define OHCI_PORT_POCI		(1<<3)
+#define OHCI_PORT_PRS		(1<<4)
+#define OHCI_PORT_PPS		(1<<8)
+#define OHCI_PORT_LSDA		(1<<9)
+#define OHCI_PORT_CSC		(1<<16)
+#define OHCI_PORT_PESC		(1<<17)
+#define OHCI_PORT_PSSC		(1<<18)
+#define OHCI_PORT_OCIC		(1<<19)
+#define OHCI_PORT_PRSC		(1<<20)
+
+/* Reset the controller */
+static void ohci_reset(struct ohci *ohci)
+{
+	int i;
+
+	ohci->ctl = ohci->ctl & OHCI_CTL_IR;
+	ohci->status = 0;
+	ohci->intr_status = 0;
+	ohci->intr = 0;
+
+	ohci->hcca = 0;
+	ohci->ctrl_head = ohci->bulk_head = 0;
+
+	/* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+	 * I took the value linux sets ...
+	 */
+	ohci->fmi = (0x2778 << 16) | 0x2edf;
+	ohci->pstart = 0;
+
+	ohci->rhdesc_a = OHCI_RHA_NPS | OHCI_NDP;
+	ohci->rhdesc_b = 0x0; /* Impl. specific */
+	ohci->rhstatus = 0;
+
+	for(i=0; i < OHCI_NDP; i++)
+		ohci->rhport[i] = 0;
+
+	dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name);
+}
+
+/* Convert a guest pointer to a qemu pointer */
+static uint8_t *reg2ptr(uint32_t addr, uint32_t len)
+{
+	if ( addr + len > phys_ram_size ) {
+		fprintf(stderr, "usb-ohci: OS tried to access outside RAM\n");
+		return NULL;
+	}
+
+	return phys_ram_base + len;
+}
+
+static uint32_t rev_r(struct ohci *ohci, uint32_t reg)
+{
+	return 0x10;
+}
+
+/* HcControlRegister */
+static uint32_t ctl_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->ctl;
+}
+
+static void ctl_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	ohci->ctl = val;
+}
+
+/* HcCommandStatus */
+static uint32_t status_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->status;
+}
+
+static void status_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	/* SOC is read-only */
+	val = (val & ~OHCI_STATUS_SOC);
+
+	/* "bits written as '0' remain unchanged in the register */
+	ohci->status |= val;
+
+	if ( ohci->status & OHCI_STATUS_HCR )
+		ohci_reset(ohci);
+}
+
+/* HcInterruptStatus */
+static uint32_t is_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->intr_status;
+}
+
+static void is_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	/* "The Host Controller Driver may clear specific bits in this
+	 * register by writing '1' to bit positions to be cleared
+	 */
+	ohci->intr_status &= ~val;
+}
+
+/* HcInterruptEnable */
+static uint32_t ie_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->intr;
+}
+
+static void ie_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	ohci->intr |= val;
+}
+
+/* HcInterruptDisable */
+static uint32_t id_r(struct ohci *ohci, uint32_t reg)
+{
+	return ~ohci->intr;
+}
+
+static void id_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	ohci->intr &= ~val;
+}
+
+/* Host Controller Communications Area */
+static uint32_t hcca_r(struct ohci *ohci, uint32_t reg)
+{
+	/* We return minimum allowed alignment, because we don't care */
+	if ( ohci->hcca & 0x1 ) {
+		ohci->hcca &= ~1;
+		return ~OHCI_HCCA_MASK;
+	}
+
+	return ohci->hcca;
+}
+
+static void hcca_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	/* HCCA alignment querying mechanism, low order bits
+	 * are safe for us to use privately as they can't ever
+	 * be set...
+	 */
+	if ( val == 0xffffffff ) {
+		ohci->hcca |= 0x1;
+		return;
+	}
+
+	ohci->hcca = val & OHCI_HCCA_MASK;
+}
+
+/* Bulk and control head ED's */
+static uint32_t head_r(struct ohci *ohci, uint32_t reg)
+{
+	uint32_t *head;
+
+	switch ( reg ) {
+	case 8:
+		head = &ohci->ctrl_head;
+		break;
+	case 10:
+		head = &ohci->bulk_head;
+		break;
+	default:
+		abort();
+	}
+
+	return *head;
+}
+
+static void head_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	uint32_t *head;
+
+	switch ( reg ) {
+	case 8:
+		head = &ohci->ctrl_head;
+		break;
+	case 10:
+		head = &ohci->bulk_head;
+		break;
+	default:
+		abort();
+	}
+
+	if ( val & ~0x7 ) {
+		fprintf(stderr, "usb-ohci: Mis-aligned %s pointer\n",
+			opreg[reg].name);
+	}
+
+	*head = val & ~0x7;
+}
+
+/* Frame Interval */
+static uint32_t fmi_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->fmi;
+}
+
+static void fmi_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	ohci->fmi = val;
+}
+
+/* Periodic Start */
+static uint32_t pstart_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->pstart;
+}
+
+static void pstart_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	ohci->pstart = val;
+}
+
+/* LS threshold */
+static uint32_t lst_r(struct ohci *ohci, uint32_t reg)
+{
+	return OHCI_LS_THRESH;
+}
+
+static void lst_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	/* "Neither the Host Controller nor the Host Controller Driver
+	 * are allowed to change this value." -- However, they are allowed
+	 * to write to it (WTF?!), so supress a warning if they write the
+	 * correct value.
+	 */
+	if ( val != OHCI_LS_THRESH ) {
+		fprintf(stderr, "usb-ohci: HCD tried to write bad LS "
+			"threshold: 0x%x\n", val);
+	}
+}
+
+/* Root Hub Descriptor A */
+static uint32_t rhda_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->rhdesc_a;
+}
+
+static void rhda_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	/* All the optional features are not supported */
+	if ( ohci->rhdesc_a != val )
+		fprintf(stderr, "usb-ohci: %s: invalid write to "
+				"root decriptor A: "
+				"0x%.8x -> 0x%.8x\n",
+				ohci->pci_dev.name,
+				ohci->rhdesc_a, val);
+}
+
+/* Root Hub Descriptor B */
+static uint32_t rhdb_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->rhdesc_b;
+}
+
+static void rhdb_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	if ( ohci->rhdesc_b != val )
+		fprintf(stderr, "usb-ohci: %s: invalid write to "
+				"root decriptor B: "
+				"0x%.8x -> 0x%.8x\n",
+				ohci->pci_dev.name,
+				ohci->rhdesc_b, val);
+}
+
+/* Root hub status */
+static uint32_t rhstatus_r(struct ohci *ohci, uint32_t reg)
+{
+	return ohci->rhstatus;
+}
+
+static void rhstatus_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	int i;
+
+	/* write 1 to clear OCIC */
+	if ( val & OHCI_RHS_OCIC )
+		ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+	if ( val & OHCI_RHS_LPS ) {
+		for(i=0; i < OHCI_NDP; i++)
+			ohci->rhport[i] &= ~OHCI_PORT_PPS;
+		dprintf("usb-ohci: %s: powered down all ports\n",
+			ohci->pci_dev.name);
+	}
+
+	if ( val & OHCI_RHS_LPSC ) {
+		for(i=0; i < OHCI_NDP; i++)
+			ohci->rhport[i] |= OHCI_PORT_PPS;
+		dprintf("usb-ohci: %s: powered up all ports\n",
+			ohci->pci_dev.name);
+	}
+
+	if ( val & OHCI_RHS_DRWE ) {
+		/* TODO: set DeviceRemoveWakeupEnable */
+	}
+
+	if ( val & OHCI_RHS_CRWE ) {
+		/* TODO: clear DeviceRemoveWakeupEnable */
+	}
+}
+
+/* Root hub port status */
+static uint32_t ps_r(struct ohci *ohci, uint32_t reg)
+{
+	reg -= 21;
+	return ohci->rhport[reg];
+}
+
+static void ps_w(struct ohci *ohci, uint32_t reg, uint32_t val)
+{
+	reg -= 21;
+	//ohci->rhport[reg] = val;
+	return;
+}
+
+/* Register descriptor table */
+static struct ohci_opreg opreg[] = {
+	{.name = "HcRevision", .read = rev_r},
+	{.name = "HcControl", .read = ctl_r, .write = ctl_w},
+	{.name = "HcCommandStatus", .read = status_r, .write = status_w},
+	{.name = "HcInterruptStatus", .read = is_r, .write = is_w},
+	{.name = "HcInterruptEnable", .read = ie_r, .write = ie_w},
+	{.name = "HcInterruptDisable", .read = id_r, .write = id_w},
+	{.name = "HcHCCA", .read = hcca_r, .write = hcca_w},
+	{.name = "HcPeriodCurrentED"},
+	{.name = "HcControlHeadED", .read = head_r, .write = head_w},
+	{.name = "HcControlCurrentED"},
+	{.name = "HcBulkHeadED", .read = head_r, .write = head_w},
+	{.name = "HcBulkCurrentED"},
+	{.name = "HcDoneHead"},
+	{.name = "HcFmInterval", .read = fmi_r, .write = fmi_w},
+	{.name = "HcFmRemaining"},
+	{.name = "HcFmNumber"},
+	{.name = "HcPeriodicStart", .read = pstart_r, .write = pstart_w},
+	{.name = "HcLSThreshold", .read = lst_r, .write = lst_w},
+	{.name = "HcRhDescriptorA", .read = rhda_r, .write = rhda_w},
+	{.name = "HcRhDescriptorB", .read = rhdb_r, .write = rhdb_w},
+	{.name = "HcRhStatus", .read = rhstatus_r, .write = rhstatus_w},
+
+	/* The number of port status register depends on the definition
+	 * of OHCI_NDP macro
+	 */
+	{.name = "HcRhPortStatus[0]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[1]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[2]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[3]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[4]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[5]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[6]", .read = ps_r, .write = ps_w},
+	{.name = "HcRhPortStatus[7]", .read = ps_r, .write = ps_w},
+};
+
+static size_t num_opreg = sizeof(opreg)/sizeof(*opreg);
+
+static uint32_t mem_read(void *ptr, target_phys_addr_t addr)
+{
+	struct ohci *ohci = ptr;
+	uint32_t ofs = (addr - ohci->mem_base) >> 2;
+	struct ohci_opreg *reg;
+	uint32_t ret;
+
+	/* Only aligned reads are allowed on OHCI */
+	if ( addr & 3 ) {
+		fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+		return 0xffffffff;
+	}
+
+	if ( ofs >= num_opreg ) {
+		fprintf(stderr, "usb-ohci: Trying to read register %u/%u\n",
+			ofs, num_opreg);
+		return 0xffffffff;
+	}
+
+	reg = &opreg[ofs];
+
+	if ( !reg->read ) {
+		fprintf(stderr, "usb-ohci: register %u (%s) is not readable\n",
+			ofs, reg->name);
+		return 0xffffffff;
+	}
+
+	ret = reg->read(ohci, ofs);
+
+	//dprintf("usb-ohci: read 0x%.8x from %u (%s)\n", ret, ofs, reg->name);
+
+	return ret;
+}
+
+static void mem_write(void *ptr, target_phys_addr_t addr, uint32_t value)
+{
+	struct ohci *ohci = ptr;
+	uint32_t ofs = (addr - ohci->mem_base) >> 2;
+	struct ohci_opreg *reg;
+
+	/* Only aligned writes are allowed on OHCI */
+	if ( addr & 3 ) {
+		fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+		return;
+	}
+
+	if ( ofs >= num_opreg ) {
+		fprintf(stderr, "usb-ohci: Trying to write register %u/%u\n",
+			ofs, num_opreg);
+		return;
+	}
+
+	reg = &opreg[ofs];
+
+	if ( !reg->write ) {
+		fprintf(stderr, "usb-ohci: register %u (%s) is not writable\n",
+			ofs, reg->name);
+		return;
+	}
+
+	reg->write(ohci, ofs, value);
+
+	//dprintf("usb-ohci: write 0x%.8x to %u (%s)\n", value, ofs, reg->name);
+}
+
+/* Only dword reads are defined on OHCI register space */
+static CPUReadMemoryFunc *cpu_callback_read[3]={
+	NULL,
+	NULL,
+	mem_read,
+};
+
+/* Only dword writes are defined on OHCI register space */
+static CPUWriteMemoryFunc *cpu_callback_write[3]={
+	NULL,
+	NULL,
+	mem_write,
+};
+
+static void mapfunc(PCIDevice *pci_dev, int i,
+			uint32_t addr, uint32_t size, int type)
+{
+	struct ohci *ohci = (struct ohci *)pci_dev;
+	ohci->mem_base = addr;
+	cpu_register_physical_memory(addr, size, ohci->mem);
+}
+
+void usb_ohci_init(const char *name, uint16_t vid, uint16_t did,
+			int bus_num, int devfn)
+{
+	struct ohci *ohci;
+
+	ohci = (struct ohci *)pci_register_device(name, sizeof(*ohci),
+							bus_num, devfn,
+							NULL, NULL);
+	if ( ohci == NULL ) {
+		fprintf(stderr, "ohci: %s: Failed to register PCI device\n",
+				name);
+		return;
+	}
+
+	ohci->pci_dev.config[0x00] = vid & 0xff;
+	ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff;
+	ohci->pci_dev.config[0x02] = did & 0xff;
+	ohci->pci_dev.config[0x03] = (did >> 8) & 0xff;
+	ohci->pci_dev.config[0x09] = 0x10; /* OHCI */
+	ohci->pci_dev.config[0x0a] = 0x3;
+	ohci->pci_dev.config[0x0b] = 0xc;
+	ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
+
+	ohci->mem = cpu_register_io_memory(0,
+						cpu_callback_read,
+						cpu_callback_write,
+						ohci);
+	pci_register_io_region((struct PCIDevice *)ohci, 0, 256,
+				PCI_ADDRESS_SPACE_MEM, mapfunc);
+
+	ohci_reset(ohci);
+}
diff -urN qemu.orig/vl.h qemu.usb/vl.h
--- qemu.orig/vl.h	2004-06-15 03:02:36.000000000 +0100
+++ qemu.usb/vl.h	2004-06-17 19:32:29.000000000 +0100
@@ -510,6 +510,9 @@
 void pci_bios_init(void);
 void pci_info(void);
 
+void usb_ohci_init(const char *name, uint16_t vid, uint16_t did,
+			int bus_num, int devfn);
+
 /* temporary: will be moved in platform specific file */
 void pci_prep_init(void);
 void pci_pmac_init(void);
