aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
commit8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch)
tree7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /hw
parentf721e3ac031f892af46f255a47d7f54a91317b30 (diff)
downloadexternal_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.zip
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.gz
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'hw')
-rw-r--r--hw/android_arm.c175
-rw-r--r--hw/arm-misc.h49
-rw-r--r--hw/arm_boot.c251
-rw-r--r--hw/arm_gic.c747
-rw-r--r--hw/arm_pic.c48
-rw-r--r--hw/arm_pic.h25
-rw-r--r--hw/armv7m.c206
-rw-r--r--hw/armv7m_nvic.c407
-rw-r--r--hw/audiodev.h17
-rw-r--r--hw/baum.h29
-rw-r--r--hw/boards.h122
-rw-r--r--hw/cdrom.c157
-rw-r--r--hw/devices.h74
-rw-r--r--hw/dma.c548
-rw-r--r--hw/goldfish_audio.c533
-rw-r--r--hw/goldfish_battery.c261
-rw-r--r--hw/goldfish_device.c200
-rw-r--r--hw/goldfish_device.h58
-rw-r--r--hw/goldfish_events_device.c423
-rw-r--r--hw/goldfish_fb.c405
-rw-r--r--hw/goldfish_interrupt.c190
-rw-r--r--hw/goldfish_memlog.c78
-rw-r--r--hw/goldfish_mmc.c468
-rw-r--r--hw/goldfish_nand.c636
-rw-r--r--hw/goldfish_nand.h28
-rw-r--r--hw/goldfish_nand_reg.h54
-rw-r--r--hw/goldfish_switch.c172
-rw-r--r--hw/goldfish_timer.c256
-rw-r--r--hw/goldfish_trace.c251
-rw-r--r--hw/goldfish_trace.h79
-rw-r--r--hw/goldfish_tty.c226
-rw-r--r--hw/hw.h110
-rw-r--r--hw/irq.c71
-rw-r--r--hw/irq.h34
-rw-r--r--hw/isa.h27
-rw-r--r--hw/mmc.h214
-rw-r--r--hw/pc.h148
-rw-r--r--hw/pci.c701
-rw-r--r--hw/pci.h142
-rw-r--r--hw/pci_host.h93
-rw-r--r--hw/pcmcia.h50
-rw-r--r--hw/power_supply.h109
-rw-r--r--hw/pxa.h227
-rw-r--r--hw/scsi-disk.c809
-rw-r--r--hw/scsi-disk.h36
-rw-r--r--hw/sd.h83
-rw-r--r--hw/smc91c111.c715
-rw-r--r--hw/usb-hid.c896
-rw-r--r--hw/usb-hub.c554
-rw-r--r--hw/usb-msd.c578
-rw-r--r--hw/usb-ohci.c1684
-rw-r--r--hw/usb.c231
-rw-r--r--hw/usb.h291
53 files changed, 14976 insertions, 0 deletions
diff --git a/hw/android_arm.c b/hw/android_arm.c
new file mode 100644
index 0000000..efc8ba1
--- /dev/null
+++ b/hw/android_arm.c
@@ -0,0 +1,175 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "hw.h"
+#include "boards.h"
+#include "devices.h"
+#include "net.h"
+#include "arm_pic.h"
+#include "sysemu.h"
+#include "goldfish_device.h"
+#include "android/globals.h"
+#include "audio/audio.h"
+#include "arm-misc.h"
+
+#define ARM_CPU_SAVE_VERSION 1
+
+int android_audio_enabled;
+char* audio_input_source = NULL;
+
+void goldfish_memlog_init(uint32_t base);
+
+static struct goldfish_device event0_device = {
+ .name = "goldfish_events",
+ .id = 0,
+ .size = 0x1000,
+ .irq_count = 1
+};
+
+static struct goldfish_device nand_device = {
+ .name = "goldfish_nand",
+ .id = 0,
+ .size = 0x1000
+};
+
+static struct goldfish_device trace_device = {
+ .name = "qemu_trace",
+ .id = -1,
+ .size = 0x1000
+};
+
+/* Board init. */
+
+#define TEST_SWITCH 1
+#if TEST_SWITCH
+uint32_t switch_test_write(void *opaque, uint32_t state)
+{
+ goldfish_switch_set_state(opaque, state);
+ return state;
+}
+#endif
+
+static void android_arm_init(ram_addr_t ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env;
+ qemu_irq *cpu_pic;
+ qemu_irq *goldfish_pic;
+ int i;
+ struct arm_boot_info info;
+
+ if (!cpu_model)
+ cpu_model = "arm926";
+
+ env = cpu_init(cpu_model);
+
+ register_savevm( "cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, env );
+
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ cpu_pic = arm_pic_init_cpu(env);
+ goldfish_pic = goldfish_interrupt_init(0xff000000, cpu_pic[ARM_PIC_CPU_IRQ], cpu_pic[ARM_PIC_CPU_FIQ]);
+ goldfish_device_init(goldfish_pic, 0xff010000, 0x7f0000, 10, 22);
+
+ goldfish_device_bus_init(0xff001000, 1);
+
+ goldfish_timer_and_rtc_init(0xff003000, 3);
+
+ goldfish_tty_add(serial_hds[0], 0, 0xff002000, 4);
+ for(i = 1; i < MAX_SERIAL_PORTS; i++) {
+ //printf("android_arm_init serial %d %x\n", i, serial_hds[i]);
+ if(serial_hds[i]) {
+ goldfish_tty_add(serial_hds[i], i, 0, 0);
+ }
+ }
+
+ for(i = 0; i < MAX_NICS; i++) {
+ if (nd_table[i].vlan) {
+ if (nd_table[i].model == NULL
+ || strcmp(nd_table[i].model, "smc91c111") == 0) {
+ struct goldfish_device *smc_device;
+ smc_device = qemu_mallocz(sizeof(*smc_device));
+ smc_device->name = "smc91x";
+ smc_device->id = i;
+ smc_device->size = 0x1000;
+ smc_device->irq_count = 1;
+ goldfish_add_device_no_io(smc_device);
+ smc91c111_init(&nd_table[i], smc_device->base, goldfish_pic[smc_device->irq]);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+ }
+
+ goldfish_fb_init(ds, 0);
+#ifdef HAS_AUDIO
+ if (android_audio_enabled) {
+ AUD_init();
+ goldfish_audio_init(0xff004000, 0, audio_input_source);
+ }
+#endif
+ {
+ int idx = drive_get_index( IF_IDE, 0, 0 );
+ if (idx >= 0)
+ goldfish_mmc_init(0xff005000, 0, drives_table[idx].bdrv);
+ }
+
+ goldfish_memlog_init(0xff006000);
+
+ if (android_hw->hw_battery)
+ goldfish_battery_init();
+
+ goldfish_add_device_no_io(&event0_device);
+ events_dev_init(event0_device.base, goldfish_pic[event0_device.irq]);
+
+#ifdef CONFIG_NAND
+ goldfish_add_device_no_io(&nand_device);
+ nand_dev_init(nand_device.base);
+#endif
+#ifdef CONFIG_TRACE
+ extern const char *trace_filename;
+ if(trace_filename != NULL) {
+ goldfish_add_device_no_io(&trace_device);
+ trace_dev_init(trace_device.base);
+ }
+#endif
+
+#if TEST_SWITCH
+ {
+ void *sw;
+ sw = goldfish_switch_add("test", NULL, NULL, 0);
+ goldfish_switch_set_state(sw, 1);
+ goldfish_switch_add("test2", switch_test_write, sw, 1);
+ }
+#endif
+
+ memset(&info, 0, sizeof info);
+ info.ram_size = ram_size;
+ info.kernel_filename = kernel_filename;
+ info.kernel_cmdline = kernel_cmdline;
+ info.initrd_filename = initrd_filename;
+ info.nb_cpus = 1;
+ info.board_id = 1441;
+
+ arm_load_kernel(env, &info);
+}
+
+QEMUMachine android_arm_machine = {
+ "android_arm",
+ "ARM Android Emulator",
+ android_arm_init,
+ NULL
+};
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
new file mode 100644
index 0000000..707e699
--- /dev/null
+++ b/hw/arm-misc.h
@@ -0,0 +1,49 @@
+/*
+ * Misc ARM declarations
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ */
+
+#ifndef ARM_MISC_H
+#define ARM_MISC_H 1
+
+#include "cpu.h"
+
+/* The CPU is also modeled as an interrupt controller. */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+qemu_irq *arm_pic_init_cpu(CPUState *env);
+
+/* armv7m.c */
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model);
+
+/* arm_boot.c */
+struct arm_boot_info {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+ target_phys_addr_t loader_start;
+ int nb_cpus;
+ int board_id;
+ int (*atag_board)(struct arm_boot_info *info, void *p);
+};
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info);
+
+/* armv7m_nvic.c */
+
+/* Multiplication factor to convert from system clock ticks to qemu timer
+ ticks. */
+int system_clock_scale;
+qemu_irq *armv7m_nvic_init(CPUState *env);
+
+/* stellaris_enent.c */
+void stellaris_enet_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
+#endif /* !ARM_MISC_H */
+
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
new file mode 100644
index 0000000..5990961
--- /dev/null
+++ b/hw/arm_boot.c
@@ -0,0 +1,251 @@
+/*
+ * ARM kernel loader.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+#define INITRD_LOAD_ADDR 0x00800000
+
+/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+static uint32_t bootloader[] = {
+ 0xe3a00000, /* mov r0, #0 */
+ 0xe3a01000, /* mov r1, #0x?? */
+ 0xe3811c00, /* orr r1, r1, #0x??00 */
+ 0xe59f2000, /* ldr r2, [pc, #0] */
+ 0xe59ff000, /* ldr pc, [pc, #0] */
+ 0, /* Address of kernel args. Set by integratorcp_init. */
+ 0 /* Kernel entry point. Set by integratorcp_init. */
+};
+
+/* Entry point for secondary CPUs. Enable interrupt controller and
+ Issue WFI until start address is written to system controller. */
+static uint32_t smpboot[] = {
+ 0xe3a00201, /* mov r0, #0x10000000 */
+ 0xe3800601, /* orr r0, r0, #0x001000000 */
+ 0xe3a01001, /* mov r1, #1 */
+ 0xe5801100, /* str r1, [r0, #0x100] */
+ 0xe3a00201, /* mov r0, #0x10000000 */
+ 0xe3800030, /* orr r0, #0x30 */
+ 0xe320f003, /* wfi */
+ 0xe5901000, /* ldr r1, [r0] */
+ 0xe3110003, /* tst r1, #3 */
+ 0x1afffffb, /* bne <wfi> */
+ 0xe12fff11 /* bx r1 */
+};
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ if (env->boot_info)
+ arm_load_kernel(env, env->boot_info);
+
+ /* TODO: Reset secondary CPUs. */
+}
+
+static void set_kernel_args(struct arm_boot_info *info,
+ int initrd_size, void *base)
+{
+ uint32_t *p;
+
+ p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+ /* ATAG_CORE */
+ stl_raw(p++, 5);
+ stl_raw(p++, 0x54410001);
+ stl_raw(p++, 1);
+ stl_raw(p++, 0x1000);
+ stl_raw(p++, 0);
+ /* ATAG_MEM */
+ /* TODO: handle multiple chips on one ATAG list */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54410002);
+ stl_raw(p++, info->ram_size);
+ stl_raw(p++, info->loader_start);
+ if (initrd_size) {
+ /* ATAG_INITRD2 */
+ stl_raw(p++, 4);
+ stl_raw(p++, 0x54420005);
+ stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+ stl_raw(p++, initrd_size);
+ }
+ if (info->kernel_cmdline && *info->kernel_cmdline) {
+ /* ATAG_CMDLINE */
+ int cmdline_size;
+
+ cmdline_size = strlen(info->kernel_cmdline);
+ memcpy(p + 2, info->kernel_cmdline, cmdline_size + 1);
+ cmdline_size = (cmdline_size >> 2) + 1;
+ stl_raw(p++, cmdline_size + 2);
+ stl_raw(p++, 0x54410009);
+ p += cmdline_size;
+ }
+ if (info->atag_board) {
+ /* ATAG_BOARD */
+ int atag_board_len;
+
+ atag_board_len = (info->atag_board(info, p + 2) + 3) >> 2;
+ stl_raw(p++, 2 + atag_board_len);
+ stl_raw(p++, 0x414f4d50);
+ p += atag_board_len;
+ }
+ /* ATAG_END */
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+}
+
+static void set_kernel_args_old(struct arm_boot_info *info,
+ int initrd_size, void *base)
+{
+ uint32_t *p;
+ unsigned char *s;
+
+ /* see linux/include/asm-arm/setup.h */
+ p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+ /* page_size */
+ stl_raw(p++, 4096);
+ /* nr_pages */
+ stl_raw(p++, info->ram_size / 4096);
+ /* ramdisk_size */
+ stl_raw(p++, 0);
+#define FLAG_READONLY 1
+#define FLAG_RDLOAD 4
+#define FLAG_RDPROMPT 8
+ /* flags */
+ stl_raw(p++, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
+ /* rootdev */
+ stl_raw(p++, (31 << 8) | 0); /* /dev/mtdblock0 */
+ /* video_num_cols */
+ stl_raw(p++, 0);
+ /* video_num_rows */
+ stl_raw(p++, 0);
+ /* video_x */
+ stl_raw(p++, 0);
+ /* video_y */
+ stl_raw(p++, 0);
+ /* memc_control_reg */
+ stl_raw(p++, 0);
+ /* unsigned char sounddefault */
+ /* unsigned char adfsdrives */
+ /* unsigned char bytes_per_char_h */
+ /* unsigned char bytes_per_char_v */
+ stl_raw(p++, 0);
+ /* pages_in_bank[4] */
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+ stl_raw(p++, 0);
+ /* pages_in_vram */
+ stl_raw(p++, 0);
+ /* initrd_start */
+ if (initrd_size)
+ stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+ else
+ stl_raw(p++, 0);
+ /* initrd_size */
+ stl_raw(p++, initrd_size);
+ /* rd_start */
+ stl_raw(p++, 0);
+ /* system_rev */
+ stl_raw(p++, 0);
+ /* system_serial_low */
+ stl_raw(p++, 0);
+ /* system_serial_high */
+ stl_raw(p++, 0);
+ /* mem_fclk_21285 */
+ stl_raw(p++, 0);
+ /* zero unused fields */
+ memset(p, 0, 256 + 1024 -
+ (p - ((uint32_t *)(base + KERNEL_ARGS_ADDR))));
+ s = base + KERNEL_ARGS_ADDR + 256 + 1024;
+ if (info->kernel_cmdline)
+ strcpy (s, info->kernel_cmdline);
+ else
+ stb_raw(s, 0);
+}
+
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
+{
+ int kernel_size;
+ int initrd_size;
+ int n;
+ int is_linux = 0;
+ uint64_t elf_entry;
+ target_ulong entry;
+ uint32_t pd;
+ void *loader_phys;
+
+ /* Load the kernel. */
+ if (!info->kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ if (!env->boot_info) {
+ if (info->nb_cpus == 0)
+ info->nb_cpus = 1;
+ env->boot_info = info;
+ qemu_register_reset(main_cpu_reset, env);
+ }
+
+ pd = cpu_get_physical_page_desc(info->loader_start);
+ loader_phys = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+ (info->loader_start & ~TARGET_PAGE_MASK);
+
+ /* Assume that raw images are linux kernels, and ELF images are not. */
+ kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uboot(info->kernel_filename, &entry, &is_linux);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image(info->kernel_filename,
+ loader_phys + KERNEL_LOAD_ADDR);
+ entry = info->loader_start + KERNEL_LOAD_ADDR;
+ is_linux = 1;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ info->kernel_filename);
+ exit(1);
+ }
+ if (!is_linux) {
+ /* Jump to the entry point. */
+ env->regs[15] = entry & 0xfffffffe;
+ env->thumb = entry & 1;
+ } else {
+ if (info->initrd_filename) {
+ initrd_size = load_image(info->initrd_filename,
+ loader_phys + INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n",
+ info->initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+ bootloader[1] |= info->board_id & 0xff;
+ bootloader[2] |= (info->board_id >> 8) & 0xff;
+ bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+ bootloader[6] = entry;
+ for (n = 0; n < sizeof(bootloader) / 4; n++)
+ stl_raw(loader_phys + (n * 4), bootloader[n]);
+ if (info->nb_cpus > 1)
+ for (n = 0; n < sizeof(smpboot) / 4; n++)
+ stl_raw(loader_phys + info->ram_size + (n * 4), smpboot[n]);
+ if (old_param)
+ set_kernel_args_old(info, initrd_size, loader_phys);
+ else
+ set_kernel_args(info, initrd_size, loader_phys);
+ }
+}
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
new file mode 100644
index 0000000..54e99f4
--- /dev/null
+++ b/hw/arm_gic.c
@@ -0,0 +1,747 @@
+/*
+ * ARM Generic/Distributed Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* This file contains implementation code for the RealView EB interrupt
+ controller, MPCore distributed interrupt controller and ARMv7-M
+ Nested Vectored Interrupt Controller. */
+
+//#define DEBUG_GIC
+
+#ifdef DEBUG_GIC
+#define DPRINTF(fmt, args...) \
+do { printf("arm_gic: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#ifdef NVIC
+static const uint8_t gic_id[] =
+{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0
+/* The NVIC has 16 internal vectors. However these are not exposed
+ through the normal GIC interface. */
+#define GIC_BASE_IRQ 32
+#else
+static const uint8_t gic_id[] =
+{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0x1000
+#define GIC_BASE_IRQ 0
+#endif
+
+typedef struct gic_irq_state
+{
+ /* ??? The documentation seems to imply the enable bits are global, even
+ for per-cpu interrupts. This seems strange. */
+ unsigned enabled:1;
+ unsigned pending:NCPU;
+ unsigned active:NCPU;
+ unsigned level:1;
+ unsigned model:1; /* 0 = N:N, 1 = 1:N */
+ unsigned trigger:1; /* nonzero = edge triggered. */
+} gic_irq_state;
+
+#define ALL_CPU_MASK ((1 << NCPU) - 1)
+
+#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
+#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
+#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
+#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
+#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
+#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
+#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
+#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
+#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
+#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
+#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
+#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
+#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
+#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
+#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
+#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
+#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
+#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_GET_PRIORITY(irq, cpu) \
+ (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#ifdef NVIC
+#define GIC_TARGET(irq) 1
+#else
+#define GIC_TARGET(irq) s->irq_target[irq]
+#endif
+
+typedef struct gic_state
+{
+ uint32_t base;
+ qemu_irq parent_irq[NCPU];
+ int enabled;
+ int cpu_enabled[NCPU];
+
+ gic_irq_state irq_state[GIC_NIRQ];
+#ifndef NVIC
+ int irq_target[GIC_NIRQ];
+#endif
+ int priority1[32][NCPU];
+ int priority2[GIC_NIRQ - 32];
+ int last_active[GIC_NIRQ][NCPU];
+
+ int priority_mask[NCPU];
+ int running_irq[NCPU];
+ int running_priority[NCPU];
+ int current_pending[NCPU];
+
+ qemu_irq *in;
+#ifdef NVIC
+ void *nvic;
+#endif
+} gic_state;
+
+/* TODO: Many places that call this routine could be optimized. */
+/* Update interrupt status after enabled or pending bits have been changed. */
+static void gic_update(gic_state *s)
+{
+ int best_irq;
+ int best_prio;
+ int irq;
+ int level;
+ int cpu;
+ int cm;
+
+ for (cpu = 0; cpu < NCPU; cpu++) {
+ cm = 1 << cpu;
+ s->current_pending[cpu] = 1023;
+ if (!s->enabled || !s->cpu_enabled[cpu]) {
+ qemu_irq_lower(s->parent_irq[cpu]);
+ return;
+ }
+ best_prio = 0x100;
+ best_irq = 1023;
+ for (irq = 0; irq < GIC_NIRQ; irq++) {
+ if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+ if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
+ best_prio = GIC_GET_PRIORITY(irq, cpu);
+ best_irq = irq;
+ }
+ }
+ }
+ level = 0;
+ if (best_prio <= s->priority_mask[cpu]) {
+ s->current_pending[cpu] = best_irq;
+ if (best_prio < s->running_priority[cpu]) {
+ DPRINTF("Raised pending IRQ %d\n", best_irq);
+ level = 1;
+ }
+ }
+ qemu_set_irq(s->parent_irq[cpu], level);
+ }
+}
+
+static void __attribute__((unused))
+gic_set_pending_private(gic_state *s, int cpu, int irq)
+{
+ int cm = 1 << cpu;
+
+ if (GIC_TEST_PENDING(irq, cm))
+ return;
+
+ DPRINTF("Set %d pending cpu %d\n", irq, cpu);
+ GIC_SET_PENDING(irq, cm);
+ gic_update(s);
+}
+
+/* Process a change in an external IRQ input. */
+static void gic_set_irq(void *opaque, int irq, int level)
+{
+ gic_state *s = (gic_state *)opaque;
+ /* The first external input line is internal interrupt 32. */
+ irq += 32;
+ if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
+ return;
+
+ if (level) {
+ GIC_SET_LEVEL(irq, ALL_CPU_MASK);
+ if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
+ DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
+ GIC_SET_PENDING(irq, GIC_TARGET(irq));
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
+ }
+ gic_update(s);
+}
+
+static void gic_set_running_irq(gic_state *s, int cpu, int irq)
+{
+ s->running_irq[cpu] = irq;
+ if (irq == 1023) {
+ s->running_priority[cpu] = 0x100;
+ } else {
+ s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ }
+ gic_update(s);
+}
+
+static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
+{
+ int new_irq;
+ int cm = 1 << cpu;
+ new_irq = s->current_pending[cpu];
+ if (new_irq == 1023
+ || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
+ DPRINTF("ACK no pending IRQ\n");
+ return 1023;
+ }
+ s->last_active[new_irq][cpu] = s->running_irq[cpu];
+ /* Clear pending flags for both level and edge triggered interrupts.
+ Level triggered IRQs will be reasserted once they become inactive. */
+ GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+ gic_set_running_irq(s, cpu, new_irq);
+ DPRINTF("ACK %d\n", new_irq);
+ return new_irq;
+}
+
+static void gic_complete_irq(gic_state * s, int cpu, int irq)
+{
+ int update = 0;
+ int cm = 1 << cpu;
+ DPRINTF("EOI %d\n", irq);
+ if (s->running_irq[cpu] == 1023)
+ return; /* No active IRQ. */
+ if (irq != 1023) {
+ /* Mark level triggered interrupts as pending if they are still
+ raised. */
+ if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
+ GIC_SET_PENDING(irq, cm);
+ update = 1;
+ }
+ }
+ if (irq != s->running_irq[cpu]) {
+ /* Complete an IRQ that is not currently running. */
+ int tmp = s->running_irq[cpu];
+ while (s->last_active[tmp][cpu] != 1023) {
+ if (s->last_active[tmp][cpu] == irq) {
+ s->last_active[tmp][cpu] = s->last_active[irq][cpu];
+ break;
+ }
+ tmp = s->last_active[tmp][cpu];
+ }
+ if (update) {
+ gic_update(s);
+ }
+ } else {
+ /* Complete the current running IRQ. */
+ gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
+ }
+}
+
+static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
+{
+ gic_state *s = (gic_state *)opaque;
+ uint32_t res;
+ int irq;
+ int i;
+ int cpu;
+ int cm;
+ int mask;
+
+ cpu = gic_get_current_cpu();
+ cm = 1 << cpu;
+ offset -= s->base + GIC_DIST_OFFSET;
+ if (offset < 0x100) {
+#ifndef NVIC
+ if (offset == 0)
+ return s->enabled;
+ if (offset == 4)
+ return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
+ if (offset < 0x08)
+ return 0;
+#endif
+ goto bad_reg;
+ } else if (offset < 0x200) {
+ /* Interrupt Set/Clear Enable. */
+ if (offset < 0x180)
+ irq = (offset - 0x100) * 8;
+ else
+ irq = (offset - 0x180) * 8;
+ irq += GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_ENABLED(irq + i)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x300) {
+ /* Interrupt Set/Clear Pending. */
+ if (offset < 0x280)
+ irq = (offset - 0x200) * 8;
+ else
+ irq = (offset - 0x280) * 8;
+ irq += GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_PENDING(irq + i, mask)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x400) {
+ /* Interrupt Active. */
+ irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_ACTIVE(irq + i, mask)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x800) {
+ /* Interrupt Priority. */
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = GIC_GET_PRIORITY(irq, cpu);
+#ifndef NVIC
+ } else if (offset < 0xc00) {
+ /* Interrupt CPU Target. */
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq >= 29 && irq <= 31) {
+ res = cm;
+ } else {
+ res = GIC_TARGET(irq);
+ }
+ } else if (offset < 0xf00) {
+ /* Interrupt Configuration. */
+ irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ for (i = 0; i < 4; i++) {
+ if (GIC_TEST_MODEL(irq + i))
+ res |= (1 << (i * 2));
+ if (GIC_TEST_TRIGGER(irq + i))
+ res |= (2 << (i * 2));
+ }
+#endif
+ } else if (offset < 0xfe0) {
+ goto bad_reg;
+ } else /* offset >= 0xfe0 */ {
+ if (offset & 3) {
+ res = 0;
+ } else {
+ res = gic_id[(offset - 0xfe0) >> 2];
+ }
+ }
+ return res;
+bad_reg:
+ cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
+ return 0;
+}
+
+static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = gic_dist_readb(opaque, offset);
+ val |= gic_dist_readb(opaque, offset + 1) << 8;
+ return val;
+}
+
+static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+#ifdef NVIC
+ gic_state *s = (gic_state *)opaque;
+ uint32_t addr;
+ addr = offset - s->base;
+ if (addr < 0x100 || addr > 0xd00)
+ return nvic_readl(s->nvic, addr);
+#endif
+ val = gic_dist_readw(opaque, offset);
+ val |= gic_dist_readw(opaque, offset + 2) << 16;
+ return val;
+}
+
+static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+ int irq;
+ int i;
+ int cpu;
+
+ cpu = gic_get_current_cpu();
+ offset -= s->base + GIC_DIST_OFFSET;
+ if (offset < 0x100) {
+#ifdef NVIC
+ goto bad_reg;
+#else
+ if (offset == 0) {
+ s->enabled = (value & 1);
+ DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
+ } else if (offset < 4) {
+ /* ignored. */
+ } else {
+ goto bad_reg;
+ }
+#endif
+ } else if (offset < 0x180) {
+ /* Interrupt Set Enable. */
+ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ value = 0xff;
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
+ if (!GIC_TEST_ENABLED(irq + i))
+ DPRINTF("Enabled IRQ %d\n", irq + i);
+ GIC_SET_ENABLED(irq + i);
+ /* If a raised level triggered IRQ enabled then mark
+ is as pending. */
+ if (GIC_TEST_LEVEL(irq + i, mask)
+ && !GIC_TEST_TRIGGER(irq + i)) {
+ DPRINTF("Set %d pending mask %x\n", irq + i, mask);
+ GIC_SET_PENDING(irq + i, mask);
+ }
+ }
+ }
+ } else if (offset < 0x200) {
+ /* Interrupt Clear Enable. */
+ irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ value = 0;
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ if (GIC_TEST_ENABLED(irq + i))
+ DPRINTF("Disabled IRQ %d\n", irq + i);
+ GIC_CLEAR_ENABLED(irq + i);
+ }
+ }
+ } else if (offset < 0x280) {
+ /* Interrupt Set Pending. */
+ irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ irq = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
+ }
+ }
+ } else if (offset < 0x300) {
+ /* Interrupt Clear Pending. */
+ irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ for (i = 0; i < 8; i++) {
+ /* ??? This currently clears the pending bit for all CPUs, even
+ for per-CPU interrupts. It's unclear whether this is the
+ corect behavior. */
+ if (value & (1 << i)) {
+ GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
+ }
+ }
+ } else if (offset < 0x400) {
+ /* Interrupt Active. */
+ goto bad_reg;
+ } else if (offset < 0x800) {
+ /* Interrupt Priority. */
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 32) {
+ s->priority1[irq][cpu] = value;
+ } else {
+ s->priority2[irq - 32] = value;
+ }
+#ifndef NVIC
+ } else if (offset < 0xc00) {
+ /* Interrupt CPU Target. */
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 29)
+ value = 0;
+ else if (irq < 32)
+ value = ALL_CPU_MASK;
+ s->irq_target[irq] = value & ALL_CPU_MASK;
+ } else if (offset < 0xf00) {
+ /* Interrupt Configuration. */
+ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 32)
+ value |= 0xaa;
+ for (i = 0; i < 4; i++) {
+ if (value & (1 << (i * 2))) {
+ GIC_SET_MODEL(irq + i);
+ } else {
+ GIC_CLEAR_MODEL(irq + i);
+ }
+ if (value & (2 << (i * 2))) {
+ GIC_SET_TRIGGER(irq + i);
+ } else {
+ GIC_CLEAR_TRIGGER(irq + i);
+ }
+ }
+#endif
+ } else {
+ /* 0xf00 is only handled for 32-bit writes. */
+ goto bad_reg;
+ }
+ gic_update(s);
+ return;
+bad_reg:
+ cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
+}
+
+static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_dist_writeb(opaque, offset, value & 0xff);
+ gic_dist_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+#ifdef NVIC
+ uint32_t addr;
+ addr = offset - s->base;
+ if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
+ nvic_writel(s->nvic, addr, value);
+ return;
+ }
+#endif
+ if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
+ int cpu;
+ int irq;
+ int mask;
+
+ cpu = gic_get_current_cpu();
+ irq = value & 0x3ff;
+ switch ((value >> 24) & 3) {
+ case 0:
+ mask = (value >> 16) & ALL_CPU_MASK;
+ break;
+ case 1:
+ mask = 1 << cpu;
+ break;
+ case 2:
+ mask = ALL_CPU_MASK ^ (1 << cpu);
+ break;
+ default:
+ DPRINTF("Bad Soft Int target filter\n");
+ mask = ALL_CPU_MASK;
+ break;
+ }
+ GIC_SET_PENDING(irq, mask);
+ gic_update(s);
+ return;
+ }
+ gic_dist_writew(opaque, offset, value & 0xffff);
+ gic_dist_writew(opaque, offset + 2, value >> 16);
+}
+
+static CPUReadMemoryFunc *gic_dist_readfn[] = {
+ gic_dist_readb,
+ gic_dist_readw,
+ gic_dist_readl
+};
+
+static CPUWriteMemoryFunc *gic_dist_writefn[] = {
+ gic_dist_writeb,
+ gic_dist_writew,
+ gic_dist_writel
+};
+
+#ifndef NVIC
+static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
+{
+ switch (offset) {
+ case 0x00: /* Control */
+ return s->cpu_enabled[cpu];
+ case 0x04: /* Priority mask */
+ return s->priority_mask[cpu];
+ case 0x08: /* Binary Point */
+ /* ??? Not implemented. */
+ return 0;
+ case 0x0c: /* Acknowledge */
+ return gic_acknowledge_irq(s, cpu);
+ case 0x14: /* Runing Priority */
+ return s->running_priority[cpu];
+ case 0x18: /* Highest Pending Interrupt */
+ return s->current_pending[cpu];
+ default:
+ cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
+{
+ switch (offset) {
+ case 0x00: /* Control */
+ s->cpu_enabled[cpu] = (value & 1);
+ DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
+ break;
+ case 0x04: /* Priority mask */
+ s->priority_mask[cpu] = (value & 0xff);
+ break;
+ case 0x08: /* Binary Point */
+ /* ??? Not implemented. */
+ break;
+ case 0x10: /* End Of Interrupt */
+ return gic_complete_irq(s, cpu, value & 0x3ff);
+ default:
+ cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
+ (int)offset);
+ return;
+ }
+ gic_update(s);
+}
+#endif
+
+static void gic_reset(gic_state *s)
+{
+ int i;
+ memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
+ for (i = 0 ; i < NCPU; i++) {
+ s->priority_mask[i] = 0xf0;
+ s->current_pending[i] = 1023;
+ s->running_irq[i] = 1023;
+ s->running_priority[i] = 0x100;
+#ifdef NVIC
+ /* The NVIC doesn't have per-cpu interfaces, so enable by default. */
+ s->cpu_enabled[i] = 1;
+#else
+ s->cpu_enabled[i] = 0;
+#endif
+ }
+ for (i = 0; i < 16; i++) {
+ GIC_SET_ENABLED(i);
+ GIC_SET_TRIGGER(i);
+ }
+#ifdef NVIC
+ /* The NVIC is always enabled. */
+ s->enabled = 1;
+#else
+ s->enabled = 0;
+#endif
+}
+
+static void gic_save(QEMUFile *f, void *opaque)
+{
+ gic_state *s = (gic_state *)opaque;
+ int i;
+ int j;
+
+ qemu_put_be32(f, s->enabled);
+ for (i = 0; i < NCPU; i++) {
+ qemu_put_be32(f, s->cpu_enabled[i]);
+#ifndef NVIC
+ qemu_put_be32(f, s->irq_target[i]);
+#endif
+ for (j = 0; j < 32; j++)
+ qemu_put_be32(f, s->priority1[j][i]);
+ for (j = 0; j < GIC_NIRQ; j++)
+ qemu_put_be32(f, s->last_active[j][i]);
+ qemu_put_be32(f, s->priority_mask[i]);
+ qemu_put_be32(f, s->running_irq[i]);
+ qemu_put_be32(f, s->running_priority[i]);
+ qemu_put_be32(f, s->current_pending[i]);
+ }
+ for (i = 0; i < GIC_NIRQ - 32; i++) {
+ qemu_put_be32(f, s->priority2[i]);
+ }
+ for (i = 0; i < GIC_NIRQ; i++) {
+ qemu_put_byte(f, s->irq_state[i].enabled);
+ qemu_put_byte(f, s->irq_state[i].pending);
+ qemu_put_byte(f, s->irq_state[i].active);
+ qemu_put_byte(f, s->irq_state[i].level);
+ qemu_put_byte(f, s->irq_state[i].model);
+ qemu_put_byte(f, s->irq_state[i].trigger);
+ }
+}
+
+static int gic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ gic_state *s = (gic_state *)opaque;
+ int i;
+ int j;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->enabled = qemu_get_be32(f);
+ for (i = 0; i < NCPU; i++) {
+ s->cpu_enabled[i] = qemu_get_be32(f);
+#ifndef NVIC
+ s->irq_target[i] = qemu_get_be32(f);
+#endif
+ for (j = 0; j < 32; j++)
+ s->priority1[j][i] = qemu_get_be32(f);
+ for (j = 0; j < GIC_NIRQ; j++)
+ s->last_active[j][i] = qemu_get_be32(f);
+ s->priority_mask[i] = qemu_get_be32(f);
+ s->running_irq[i] = qemu_get_be32(f);
+ s->running_priority[i] = qemu_get_be32(f);
+ s->current_pending[i] = qemu_get_be32(f);
+ }
+ for (i = 0; i < GIC_NIRQ - 32; i++) {
+ s->priority2[i] = qemu_get_be32(f);
+ }
+ for (i = 0; i < GIC_NIRQ; i++) {
+ s->irq_state[i].enabled = qemu_get_byte(f);
+ s->irq_state[i].pending = qemu_get_byte(f);
+ s->irq_state[i].active = qemu_get_byte(f);
+ s->irq_state[i].level = qemu_get_byte(f);
+ s->irq_state[i].model = qemu_get_byte(f);
+ s->irq_state[i].trigger = qemu_get_byte(f);
+ }
+
+ return 0;
+}
+
+static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
+{
+ gic_state *s;
+ int iomemtype;
+ int i;
+
+ s = (gic_state *)qemu_mallocz(sizeof(gic_state));
+ if (!s)
+ return NULL;
+ s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
+ for (i = 0; i < NCPU; i++) {
+ s->parent_irq[i] = parent_irq[i];
+ }
+ iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
+ gic_dist_writefn, s);
+ cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
+ iomemtype);
+ s->base = base;
+ gic_reset(s);
+ register_savevm("arm_gic", -1, 1, gic_save, gic_load, s);
+ return s;
+}
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
new file mode 100644
index 0000000..1fe55b7
--- /dev/null
+++ b/hw/arm_pic.c
@@ -0,0 +1,48 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_info(void)
+{
+}
+
+void irq_info(void)
+{
+}
+
+
+/* Input 0 is IRQ and input 1 is FIQ. */
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ CPUState *env = (CPUState *)opaque;
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ);
+ break;
+ default:
+ cpu_abort(env, "arm_pic_cpu_handler: Bad interrput line %d\n", irq);
+ }
+}
+
+qemu_irq *arm_pic_init_cpu(CPUState *env)
+{
+ return qemu_allocate_irqs(arm_pic_cpu_handler, env, 2);
+}
diff --git a/hw/arm_pic.h b/hw/arm_pic.h
new file mode 100644
index 0000000..7886bcf
--- /dev/null
+++ b/hw/arm_pic.h
@@ -0,0 +1,25 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Arm hardware uses a wide variety of interrupt handling hardware.
+ * This provides a generic framework for connecting interrupt sources and
+ * inputs.
+ */
+
+#ifndef ARM_INTERRUPT_H
+#define ARM_INTERRUPT_H 1
+
+#include "irq.h"
+
+/* The CPU is also modeled as an interrupt controller. */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+qemu_irq *arm_pic_init_cpu(CPUState *env);
+
+#endif /* !ARM_INTERRUPT_H */
+
diff --git a/hw/armv7m.c b/hw/armv7m.c
new file mode 100644
index 0000000..b2bad3c
--- /dev/null
+++ b/hw/armv7m.c
@@ -0,0 +1,206 @@
+/*
+ * ARMV7M System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/* Bitbanded IO. Each word corresponds to a single bit. */
+
+/* Get the byte address of the real memory for a bitband acess. */
+static inline uint32_t bitband_addr(uint32_t addr)
+{
+ uint32_t res;
+
+ res = addr & 0xe0000000;
+ res |= (addr & 0x1ffffff) >> 5;
+ return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
+{
+ uint8_t v;
+ cpu_physical_memory_read(bitband_addr(offset), &v, 1);
+ return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint8_t mask;
+ uint8_t v;
+ addr = bitband_addr(offset);
+ mask = (1 << ((offset >> 2) & 7));
+ cpu_physical_memory_read(addr, &v, 1);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static CPUReadMemoryFunc *bitband_readfn[] = {
+ bitband_readb,
+ bitband_readw,
+ bitband_readl
+};
+
+static CPUWriteMemoryFunc *bitband_writefn[] = {
+ bitband_writeb,
+ bitband_writew,
+ bitband_writel
+};
+
+static void armv7m_bitband_init(void)
+{
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
+ NULL);
+ cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
+ cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
+}
+
+/* Board init. */
+/* Init CPU and memory for a v7-M based board.
+ flash_size and sram_size are in kb.
+ Returns the NVIC array. */
+
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model)
+{
+ CPUState *env;
+ qemu_irq *pic;
+ uint32_t pc;
+ int image_size;
+ uint64_t entry;
+ uint64_t lowaddr;
+
+ flash_size *= 1024;
+ sram_size *= 1024;
+
+ if (!cpu_model)
+ cpu_model = "cortex-m3";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+#if 0
+ /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+ We don't have proper commandline options, so allocate half of memory
+ as SRAM, up to a maximum of 32Mb, and the rest as code. */
+ if (ram_size > (512 + 32) * 1024 * 1024)
+ ram_size = (512 + 32) * 1024 * 1024;
+ sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+ if (sram_size > 32 * 1024 * 1024)
+ sram_size = 32 * 1024 * 1024;
+ code_size = ram_size - sram_size;
+#endif
+
+ /* Flash programming is done via the SCU, so pretend it is ROM. */
+ cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
+ cpu_register_physical_memory(0x20000000, sram_size,
+ flash_size + IO_MEM_RAM);
+ armv7m_bitband_init();
+
+ pic = armv7m_nvic_init(env);
+
+ image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
+ if (image_size < 0) {
+ image_size = load_image(kernel_filename, phys_ram_base);
+ lowaddr = 0;
+ }
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* If the image was loaded at address zero then assume it is a
+ regular ROM image and perform the normal CPU reset sequence.
+ Otherwise jump directly to the entry point. */
+ if (lowaddr == 0) {
+ env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
+ pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
+ } else {
+ pc = entry;
+ }
+ env->thumb = pc & 1;
+ env->regs[15] = pc & ~1;
+
+ /* Hack to map an additional page of ram at the top of the address
+ space. This stops qemu complaining about executing code outside RAM
+ when returning from an exception. */
+ cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
+
+ return pic;
+}
+
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
new file mode 100644
index 0000000..c55c958
--- /dev/null
+++ b/hw/armv7m_nvic.c
@@ -0,0 +1,407 @@
+/*
+ * ARM Nested Vectored Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ *
+ * The ARMv7M System controller is fairly tightly tied in with the
+ * NVIC. Much of that is also implemented here.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "arm-misc.h"
+
+/* 32 internal lines (16 used for system exceptions) plus 64 external
+ interrupt lines. */
+#define GIC_NIRQ 96
+#define NCPU 1
+#define NVIC 1
+
+/* Only a single "CPU" interface is present. */
+static inline int
+gic_get_current_cpu(void)
+{
+ return 0;
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset);
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
+
+#include "arm_gic.c"
+
+typedef struct {
+ struct {
+ uint32_t control;
+ uint32_t reload;
+ int64_t tick;
+ QEMUTimer *timer;
+ } systick;
+ gic_state *gic;
+} nvic_state;
+
+/* qemu timers run at 1GHz. We want something closer to 1MHz. */
+#define SYSTICK_SCALE 1000ULL
+
+#define SYSTICK_ENABLE (1 << 0)
+#define SYSTICK_TICKINT (1 << 1)
+#define SYSTICK_CLKSOURCE (1 << 2)
+#define SYSTICK_COUNTFLAG (1 << 16)
+
+/* Conversion factor from qemu timer to SysTick frequencies. */
+static inline int64_t systick_scale(nvic_state *s)
+{
+ if (s->systick.control & SYSTICK_CLKSOURCE)
+ return system_clock_scale;
+ else
+ return 1000;
+}
+
+static void systick_reload(nvic_state *s, int reset)
+{
+ if (reset)
+ s->systick.tick = qemu_get_clock(vm_clock);
+ s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+}
+
+static void systick_timer_tick(void * opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ s->systick.control |= SYSTICK_COUNTFLAG;
+ if (s->systick.control & SYSTICK_TICKINT) {
+ /* Trigger the interrupt. */
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ }
+ if (s->systick.reload == 0) {
+ s->systick.control &= ~SYSTICK_ENABLE;
+ } else {
+ systick_reload(s, 0);
+ }
+}
+
+/* The external routines use the hardware vector numbering, ie. the first
+ IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
+void armv7m_nvic_set_pending(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_set_pending_private(s->gic, 0, irq);
+}
+
+/* Make pending IRQ active. */
+int armv7m_nvic_acknowledge_irq(void *opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t irq;
+
+ irq = gic_acknowledge_irq(s->gic, 0);
+ if (irq == 1023)
+ cpu_abort(cpu_single_env, "Interrupt but no vector\n");
+ if (irq >= 32)
+ irq -= 16;
+ return irq;
+}
+
+void armv7m_nvic_complete_irq(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_complete_irq(s->gic, 0, irq);
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t val;
+ int irq;
+
+ switch (offset) {
+ case 4: /* Interrupt Control Type. */
+ return (GIC_NIRQ / 32) - 1;
+ case 0x10: /* SysTick Control and Status. */
+ val = s->systick.control;
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ return val;
+ case 0x14: /* SysTick Reload Value. */
+ return s->systick.reload;
+ case 0x18: /* SysTick Current Value. */
+ {
+ int64_t t;
+ if ((s->systick.control & SYSTICK_ENABLE) == 0)
+ return 0;
+ t = qemu_get_clock(vm_clock);
+ if (t >= s->systick.tick)
+ return 0;
+ val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
+ /* The interrupt in triggered when the timer reaches zero.
+ However the counter is not reloaded until the next clock
+ tick. This is a hack to return zero during the first tick. */
+ if (val > s->systick.reload)
+ val = 0;
+ return val;
+ }
+ case 0x1c: /* SysTick Calibration Value. */
+ return 10000;
+ case 0xd00: /* CPUID Base. */
+ return cpu_single_env->cp15.c0_cpuid;
+ case 0xd04: /* Interrypt Control State. */
+ /* VECTACTIVE */
+ val = s->gic->running_irq[0];
+ if (val == 1023) {
+ val = 0;
+ } else if (val >= 32) {
+ val -= 16;
+ }
+ /* RETTOBASE */
+ if (s->gic->running_irq[0] == 1023
+ || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
+ val |= (1 << 11);
+ }
+ /* VECTPENDING */
+ if (s->gic->current_pending[0] != 1023)
+ val |= (s->gic->current_pending[0] << 12);
+ /* ISRPENDING */
+ for (irq = 32; irq < GIC_NIRQ; irq++) {
+ if (s->gic->irq_state[irq].pending) {
+ val |= (1 << 22);
+ break;
+ }
+ }
+ /* PENDSTSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
+ val |= (1 << 26);
+ /* PENDSVSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
+ val |= (1 << 28);
+ /* NMIPENDSET */
+ if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
+ val |= (1 << 31);
+ return val;
+ case 0xd08: /* Vector Table Offset. */
+ return cpu_single_env->v7m.vecbase;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ return 0xfa05000;
+ case 0xd10: /* System Control. */
+ /* TODO: Implement SLEEPONEXIT. */
+ return 0;
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement Configuration Control bits. */
+ return 0;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ irq = offset - 0xd14;
+ val = 0;
+ val = s->gic->priority1[irq++][0];
+ val = s->gic->priority1[irq++][0] << 8;
+ val = s->gic->priority1[irq++][0] << 16;
+ val = s->gic->priority1[irq][0] << 24;
+ return val;
+ case 0xd24: /* System Handler Status. */
+ val = 0;
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+ if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+ if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+ if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+ if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+ if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+ if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+ if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+ if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+ return val;
+ case 0xd28: /* Configurable Fault Status. */
+ /* TODO: Implement Fault Status. */
+ cpu_abort(cpu_single_env,
+ "Not implemented: Configurable Fault Status.");
+ return 0;
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ /* TODO: Implement fault status registers. */
+ goto bad_reg;
+ case 0xd40: /* PFR0. */
+ return 0x00000030;
+ case 0xd44: /* PRF1. */
+ return 0x00000200;
+ case 0xd48: /* DFR0. */
+ return 0x00100000;
+ case 0xd4c: /* AFR0. */
+ return 0x00000000;
+ case 0xd50: /* MMFR0. */
+ return 0x00000030;
+ case 0xd54: /* MMFR1. */
+ return 0x00000000;
+ case 0xd58: /* MMFR2. */
+ return 0x00000000;
+ case 0xd5c: /* MMFR3. */
+ return 0x00000000;
+ case 0xd60: /* ISAR0. */
+ return 0x01141110;
+ case 0xd64: /* ISAR1. */
+ return 0x02111000;
+ case 0xd68: /* ISAR2. */
+ return 0x21112231;
+ case 0xd6c: /* ISAR3. */
+ return 0x01111110;
+ case 0xd70: /* ISAR4. */
+ return 0x01310102;
+ /* TODO: Implement debug registers. */
+ default:
+ bad_reg:
+ cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
+ }
+}
+
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t oldval;
+ switch (offset) {
+ case 0x10: /* SysTick Control and Status. */
+ oldval = s->systick.control;
+ s->systick.control &= 0xfffffff8;
+ s->systick.control |= value & 7;
+ if ((oldval ^ value) & SYSTICK_ENABLE) {
+ int64_t now = qemu_get_clock(vm_clock);
+ if (value & SYSTICK_ENABLE) {
+ if (s->systick.tick) {
+ s->systick.tick += now;
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+ } else {
+ systick_reload(s, 1);
+ }
+ } else {
+ qemu_del_timer(s->systick.timer);
+ s->systick.tick -= now;
+ if (s->systick.tick < 0)
+ s->systick.tick = 0;
+ }
+ } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
+ /* This is a hack. Force the timer to be reloaded
+ when the reference clock is changed. */
+ systick_reload(s, 1);
+ }
+ break;
+ case 0x14: /* SysTick Reload Value. */
+ s->systick.reload = value;
+ break;
+ case 0x18: /* SysTick Current Value. Writes reload the timer. */
+ systick_reload(s, 1);
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ break;
+ case 0xd04: /* Interrupt Control State. */
+ if (value & (1 << 31)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+ }
+ if (value & (1 << 28)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+ } else if (value & (1 << 27)) {
+ s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+ gic_update(s->gic);
+ }
+ if (value & (1 << 26)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ } else if (value & (1 << 25)) {
+ s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+ gic_update(s->gic);
+ }
+ break;
+ case 0xd08: /* Vector Table Offset. */
+ cpu_single_env->v7m.vecbase = value & 0xffffff80;
+ break;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ if ((value >> 16) == 0x05fa) {
+ if (value & 2) {
+ cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
+ }
+ if (value & 5) {
+ cpu_abort(cpu_single_env, "System reset");
+ }
+ }
+ break;
+ case 0xd10: /* System Control. */
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement control registers. */
+ goto bad_reg;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ {
+ int irq;
+ irq = offset - 0xd14;
+ s->gic->priority1[irq++][0] = value & 0xff;
+ s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
+ s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
+ s->gic->priority1[irq][0] = (value >> 24) & 0xff;
+ gic_update(s->gic);
+ }
+ break;
+ case 0xd24: /* System Handler Control. */
+ /* TODO: Real hardware allows you to set/clear the active bits
+ under some circumstances. We don't implement this. */
+ s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+ s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+ s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+ break;
+ case 0xd28: /* Configurable Fault Status. */
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ goto bad_reg;
+ default:
+ bad_reg:
+ cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
+ }
+}
+
+static void nvic_save(QEMUFile *f, void *opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+
+ qemu_put_be32(f, s->systick.control);
+ qemu_put_be32(f, s->systick.reload);
+ qemu_put_be64(f, s->systick.tick);
+ qemu_put_timer(f, s->systick.timer);
+}
+
+static int nvic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ nvic_state *s = (nvic_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->systick.control = qemu_get_be32(f);
+ s->systick.reload = qemu_get_be32(f);
+ s->systick.tick = qemu_get_be64(f);
+ qemu_get_timer(f, s->systick.timer);
+
+ return 0;
+}
+
+qemu_irq *armv7m_nvic_init(CPUState *env)
+{
+ nvic_state *s;
+ qemu_irq *parent;
+
+ parent = arm_pic_init_cpu(env);
+ s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
+ s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
+ s->gic->nvic = s;
+ s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
+ if (env->v7m.nvic)
+ cpu_abort(env, "CPU can only have one NVIC\n");
+ env->v7m.nvic = s;
+ register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
+ return s->gic->in;
+}
diff --git a/hw/audiodev.h b/hw/audiodev.h
new file mode 100644
index 0000000..5f4a211
--- /dev/null
+++ b/hw/audiodev.h
@@ -0,0 +1,17 @@
+/* es1370.c */
+int es1370_init (PCIBus *bus, AudioState *s);
+
+/* sb16.c */
+int SB16_init (AudioState *s, qemu_irq *pic);
+
+/* adlib.c */
+int Adlib_init (AudioState *s, qemu_irq *pic);
+
+/* gus.c */
+int GUS_init (AudioState *s, qemu_irq *pic);
+
+/* ac97.c */
+int ac97_init (PCIBus *buf, AudioState *s);
+
+/* cs4231a.c */
+int cs4231a_init (AudioState *s, qemu_irq *pic);
diff --git a/hw/baum.h b/hw/baum.h
new file mode 100644
index 0000000..ac34b30
--- /dev/null
+++ b/hw/baum.h
@@ -0,0 +1,29 @@
+/*
+ * QEMU Baum
+ *
+ * Copyright (c) 2008 Samuel Thibault
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* usb device */
+USBDevice *usb_baum_init(void);
+
+/* char device */
+CharDriverState *chr_baum_init(void);
diff --git a/hw/boards.h b/hw/boards.h
new file mode 100644
index 0000000..cfb7c42
--- /dev/null
+++ b/hw/boards.h
@@ -0,0 +1,122 @@
+/* Declarations for use by board files for creating devices. */
+
+#ifndef HW_BOARDS_H
+#define HW_BOARDS_H
+
+typedef void QEMUMachineInitFunc(ram_addr_t ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model);
+
+typedef struct QEMUMachine {
+ const char *name;
+ const char *desc;
+ QEMUMachineInitFunc *init;
+#define RAMSIZE_FIXED (1 << 0)
+ ram_addr_t ram_require;
+ int nodisk_ok;
+ struct QEMUMachine *next;
+} QEMUMachine;
+
+int qemu_register_machine(QEMUMachine *m);
+void register_machines(void);
+
+/* Axis ETRAX. */
+extern QEMUMachine bareetraxfs_machine;
+
+/* pc.c */
+extern QEMUMachine pc_machine;
+extern QEMUMachine isapc_machine;
+
+/* ppc.c */
+extern QEMUMachine prep_machine;
+extern QEMUMachine core99_machine;
+extern QEMUMachine heathrow_machine;
+extern QEMUMachine ref405ep_machine;
+extern QEMUMachine taihu_machine;
+
+/* mips_r4k.c */
+extern QEMUMachine mips_machine;
+
+/* mips_jazz.c */
+extern QEMUMachine mips_magnum_machine;
+extern QEMUMachine mips_pica61_machine;
+
+/* mips_malta.c */
+extern QEMUMachine mips_malta_machine;
+
+/* mips_mipssim.c */
+extern QEMUMachine mips_mipssim_machine;
+
+/* shix.c */
+extern QEMUMachine shix_machine;
+
+/* r2d.c */
+extern QEMUMachine r2d_machine;
+
+/* sun4m.c */
+extern QEMUMachine ss5_machine, ss10_machine, ss600mp_machine, ss20_machine;
+extern QEMUMachine voyager_machine, ss_lx_machine, ss4_machine, scls_machine;
+extern QEMUMachine sbook_machine;
+extern QEMUMachine ss2_machine;
+extern QEMUMachine ss1000_machine, ss2000_machine;
+
+/* sun4u.c */
+extern QEMUMachine sun4u_machine;
+extern QEMUMachine sun4v_machine;
+
+/* integratorcp.c */
+extern QEMUMachine integratorcp_machine;
+
+/* versatilepb.c */
+extern QEMUMachine versatilepb_machine;
+extern QEMUMachine versatileab_machine;
+
+/* realview.c */
+extern QEMUMachine realview_machine;
+
+/* spitz.c */
+extern QEMUMachine akitapda_machine;
+extern QEMUMachine spitzpda_machine;
+extern QEMUMachine borzoipda_machine;
+extern QEMUMachine terrierpda_machine;
+
+/* palm.c */
+extern QEMUMachine palmte_machine;
+
+/* nseries.c */
+extern QEMUMachine n800_machine;
+extern QEMUMachine n810_machine;
+
+/* gumstix.c */
+extern QEMUMachine connex_machine;
+extern QEMUMachine verdex_machine;
+
+/* stellaris.c */
+extern QEMUMachine lm3s811evb_machine;
+extern QEMUMachine lm3s6965evb_machine;
+
+/* an5206.c */
+extern QEMUMachine an5206_machine;
+
+/* mcf5208.c */
+extern QEMUMachine mcf5208evb_machine;
+
+/* dummy_m68k.c */
+extern QEMUMachine dummy_m68k_machine;
+
+/* mainstone.c */
+extern QEMUMachine mainstone2_machine;
+
+/* musicpal.c */
+extern QEMUMachine musicpal_machine;
+
+/* tosa.c */
+extern QEMUMachine tosapda_machine;
+
+/* android_arm.c */
+extern QEMUMachine android_arm_machine;
+
+#endif
diff --git a/hw/cdrom.c b/hw/cdrom.c
new file mode 100644
index 0000000..2aa4d3b
--- /dev/null
+++ b/hw/cdrom.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU ATAPI CD-ROM Emulator
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
+ here. */
+
+#include "qemu-common.h"
+#include "scsi-disk.h"
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+
+/* same toc as bochs. Return -1 if error or the toc length */
+/* XXX: check this */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+{
+ uint8_t *q;
+ int len;
+
+ if (start_track > 1 && start_track != 0xaa)
+ return -1;
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ if (start_track <= 1) {
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 1; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ /* sector 0 */
+ cpu_to_be32wu((uint32_t *)q, 0);
+ q += 4;
+ }
+ }
+ /* lead out track */
+ *q++ = 0; /* reserved */
+ *q++ = 0x16; /* ADR, control */
+ *q++ = 0xaa; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
+
+/* mostly same info as PearPc */
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
+{
+ uint8_t *q;
+ int len;
+
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa0; /* lead-in */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* first track */
+ *q++ = 0x00; /* disk type */
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa1;
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* last track */
+ *q++ = 0x00;
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa2; /* lead-out */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 0; /* track number */
+ *q++ = 1; /* point */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0;
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
+
+
diff --git a/hw/devices.h b/hw/devices.h
new file mode 100644
index 0000000..45fead9
--- /dev/null
+++ b/hw/devices.h
@@ -0,0 +1,74 @@
+#ifndef QEMU_DEVICES_H
+#define QEMU_DEVICES_H
+
+/* Devices that have nowhere better to go. */
+
+/* smc91c111.c */
+void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
+
+/* ssd0323.c */
+int ssd0323_xfer_ssi(void *opaque, int data);
+void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p);
+
+/* ads7846.c */
+struct ads7846_state_s;
+uint32_t ads7846_read(void *opaque);
+void ads7846_write(void *opaque, uint32_t value);
+struct ads7846_state_s *ads7846_init(qemu_irq penirq);
+
+/* tsc210x.c */
+struct uwire_slave_s;
+struct mouse_transform_info_s;
+struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio);
+struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq,
+ qemu_irq dav, AudioState *audio);
+struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip);
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
+void tsc210x_set_transform(struct uwire_slave_s *chip,
+ struct mouse_transform_info_s *info);
+void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down);
+
+/* tsc2005.c */
+void *tsc2005_init(qemu_irq pintdav);
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
+void tsc2005_set_transform(void *opaque, struct mouse_transform_info_s *info);
+
+/* stellaris_input.c */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
+
+/* blizzard.c */
+void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds);
+void s1d13745_write(void *opaque, int dc, uint16_t value);
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch);
+uint16_t s1d13745_read(void *opaque, int dc);
+
+/* cbus.c */
+struct cbus_s {
+ qemu_irq clk;
+ qemu_irq dat;
+ qemu_irq sel;
+};
+struct cbus_s *cbus_init(qemu_irq dat_out);
+void cbus_attach(struct cbus_s *bus, void *slave_opaque);
+
+void *retu_init(qemu_irq irq, int vilma);
+void *tahvo_init(qemu_irq irq, int betty);
+
+void retu_key_event(void *retu, int state);
+
+/* tusb6010.c */
+struct tusb_s;
+struct tusb_s *tusb6010_init(qemu_irq intr);
+int tusb6010_sync_io(struct tusb_s *s);
+int tusb6010_async_io(struct tusb_s *s);
+void tusb6010_power(struct tusb_s *s, int on);
+
+/* tc6393xb.c */
+struct tc6393xb_s;
+struct tc6393xb_s *tc6393xb_init(uint32_t base, qemu_irq irq);
+void tc6393xb_gpio_out_set(struct tc6393xb_s *s, int line,
+ qemu_irq handler);
+qemu_irq *tc6393xb_gpio_in_get(struct tc6393xb_s *s);
+
+#endif
diff --git a/hw/dma.c b/hw/dma.c
new file mode 100644
index 0000000..00c6332
--- /dev/null
+++ b/hw/dma.c
@@ -0,0 +1,548 @@
+/*
+ * QEMU DMA emulation
+ *
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+
+/* #define DEBUG_DMA */
+
+#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#ifdef DEBUG_DMA
+#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#else
+#define lwarn(...)
+#define linfo(...)
+#define ldebug(...)
+#endif
+
+#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
+
+struct dma_regs {
+ int now[2];
+ uint16_t base[2];
+ uint8_t mode;
+ uint8_t page;
+ uint8_t pageh;
+ uint8_t dack;
+ uint8_t eop;
+ DMA_transfer_handler transfer_handler;
+ void *opaque;
+};
+
+#define ADDR 0
+#define COUNT 1
+
+static struct dma_cont {
+ uint8_t status;
+ uint8_t command;
+ uint8_t mask;
+ uint8_t flip_flop;
+ int dshift;
+ struct dma_regs regs[4];
+} dma_controllers[2];
+
+enum {
+ CMD_MEMORY_TO_MEMORY = 0x01,
+ CMD_FIXED_ADDRESS = 0x02,
+ CMD_BLOCK_CONTROLLER = 0x04,
+ CMD_COMPRESSED_TIME = 0x08,
+ CMD_CYCLIC_PRIORITY = 0x10,
+ CMD_EXTENDED_WRITE = 0x20,
+ CMD_LOW_DREQ = 0x40,
+ CMD_LOW_DACK = 0x80,
+ CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
+ | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
+ | CMD_LOW_DREQ | CMD_LOW_DACK
+
+};
+
+static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+
+static void write_page (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].page = data;
+}
+
+static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].pageh = data;
+}
+
+static uint32_t read_page (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].page;
+}
+
+static uint32_t read_pageh (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].pageh;
+}
+
+static inline void init_chan (struct dma_cont *d, int ichan)
+{
+ struct dma_regs *r;
+
+ r = d->regs + ichan;
+ r->now[ADDR] = r->base[ADDR] << d->dshift;
+ r->now[COUNT] = 0;
+}
+
+static inline int getff (struct dma_cont *d)
+{
+ int ff;
+
+ ff = d->flip_flop;
+ d->flip_flop = !ff;
+ return ff;
+}
+
+static uint32_t read_chan (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan, nreg, iport, ff, val, dir;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+
+ dir = ((r->mode >> 5) & 1) ? -1 : 1;
+ ff = getff (d);
+ if (nreg)
+ val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
+ else
+ val = r->now[ADDR] + r->now[COUNT] * dir;
+
+ ldebug ("read_chan %#x -> %d\n", iport, val);
+ return (val >> (d->dshift + (ff << 3))) & 0xff;
+}
+
+static void write_chan (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan, nreg;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+ if (getff (d)) {
+ r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
+ init_chan (d, ichan);
+ } else {
+ r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
+ }
+}
+
+static void write_cont (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan = 0;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* command */
+ if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
+ dolog ("command %#x not supported\n", data);
+ return;
+ }
+ d->command = data;
+ break;
+
+ case 0x09:
+ ichan = data & 3;
+ if (data & 4) {
+ d->status |= 1 << (ichan + 4);
+ }
+ else {
+ d->status &= ~(1 << (ichan + 4));
+ }
+ d->status &= ~(1 << ichan);
+ break;
+
+ case 0x0a: /* single mask */
+ if (data & 4)
+ d->mask |= 1 << (data & 3);
+ else
+ d->mask &= ~(1 << (data & 3));
+ break;
+
+ case 0x0b: /* mode */
+ {
+ ichan = data & 3;
+#ifdef DEBUG_DMA
+ {
+ int op, ai, dir, opmode;
+ op = (data >> 2) & 3;
+ ai = (data >> 4) & 1;
+ dir = (data >> 5) & 1;
+ opmode = (data >> 6) & 3;
+
+ linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
+ ichan, op, ai, dir, opmode);
+ }
+#endif
+ d->regs[ichan].mode = data;
+ break;
+ }
+
+ case 0x0c: /* clear flip flop */
+ d->flip_flop = 0;
+ break;
+
+ case 0x0d: /* reset */
+ d->flip_flop = 0;
+ d->mask = ~0;
+ d->status = 0;
+ d->command = 0;
+ break;
+
+ case 0x0e: /* clear mask for all channels */
+ d->mask = 0;
+ break;
+
+ case 0x0f: /* write mask for all channels */
+ d->mask = data;
+ break;
+
+ default:
+ dolog ("unknown iport %#x\n", iport);
+ break;
+ }
+
+#ifdef DEBUG_DMA
+ if (0xc != iport) {
+ linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
+ nport, ichan, data);
+ }
+#endif
+}
+
+static uint32_t read_cont (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int iport, val;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* status */
+ val = d->status;
+ d->status &= 0xf0;
+ break;
+ case 0x0f: /* mask */
+ val = d->mask;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
+ return val;
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+ return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+}
+
+void DMA_hold_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("held cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status |= 1 << (ichan + 4);
+}
+
+void DMA_release_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("released cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status &= ~(1 << (ichan + 4));
+}
+
+static void channel_run (int ncont, int ichan)
+{
+ int n;
+ struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+#ifdef DEBUG_DMA
+ int dir, opmode;
+
+ dir = (r->mode >> 5) & 1;
+ opmode = (r->mode >> 6) & 3;
+
+ if (dir) {
+ dolog ("DMA in address decrement mode\n");
+ }
+ if (opmode != 1) {
+ dolog ("DMA not in single mode select %#x\n", opmode);
+ }
+#endif
+
+ r = dma_controllers[ncont].regs + ichan;
+ n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
+ r->now[COUNT], (r->base[COUNT] + 1) << ncont);
+ r->now[COUNT] = n;
+ ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+}
+
+void DMA_run (void)
+{
+ struct dma_cont *d;
+ int icont, ichan;
+
+ d = dma_controllers;
+
+ for (icont = 0; icont < 2; icont++, d++) {
+ for (ichan = 0; ichan < 4; ichan++) {
+ int mask;
+
+ mask = 1 << ichan;
+
+ if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4))))
+ channel_run (icont, ichan);
+ }
+ }
+}
+
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+ struct dma_regs *r;
+ int ichan, ncont;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+
+ r = dma_controllers[ncont].regs + ichan;
+ r->transfer_handler = transfer_handler;
+ r->opaque = opaque;
+}
+
+int DMA_read_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_read (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len >> 1; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_read (addr + pos, buf, len);
+
+ return len;
+}
+
+int DMA_write_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_write (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_write (addr + pos, buf, len);
+
+ return len;
+}
+
+/* request the emulator to transfer a new DMA memory block ASAP */
+void DMA_schedule(int nchan)
+{
+ CPUState *env = cpu_single_env;
+ if (env)
+ cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+}
+
+static void dma_reset(void *opaque)
+{
+ struct dma_cont *d = opaque;
+ write_cont (d, (0x0d << d->dshift), 0);
+}
+
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+ nchan, dma_pos, dma_len);
+ return dma_pos;
+}
+
+/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
+static void dma_init2(struct dma_cont *d, int base, int dshift,
+ int page_base, int pageh_base)
+{
+ static const int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
+ int i;
+
+ d->dshift = dshift;
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
+ register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
+ }
+ for (i = 0; i < LENOFA (page_port_list); i++) {
+ register_ioport_write (page_base + page_port_list[i], 1, 1,
+ write_page, d);
+ register_ioport_read (page_base + page_port_list[i], 1, 1,
+ read_page, d);
+ if (pageh_base >= 0) {
+ register_ioport_write (pageh_base + page_port_list[i], 1, 1,
+ write_pageh, d);
+ register_ioport_read (pageh_base + page_port_list[i], 1, 1,
+ read_pageh, d);
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + ((i + 8) << dshift), 1, 1,
+ write_cont, d);
+ register_ioport_read (base + ((i + 8) << dshift), 1, 1,
+ read_cont, d);
+ }
+ qemu_register_reset(dma_reset, d);
+ dma_reset(d);
+ for (i = 0; i < LENOFA (d->regs); ++i) {
+ d->regs[i].transfer_handler = dma_phony_handler;
+ }
+}
+
+static void dma_save (QEMUFile *f, void *opaque)
+{
+ struct dma_cont *d = opaque;
+ int i;
+
+ /* qemu_put_8s (f, &d->status); */
+ qemu_put_8s (f, &d->command);
+ qemu_put_8s (f, &d->mask);
+ qemu_put_8s (f, &d->flip_flop);
+ qemu_put_be32 (f, d->dshift);
+
+ for (i = 0; i < 4; ++i) {
+ struct dma_regs *r = &d->regs[i];
+ qemu_put_be32 (f, r->now[0]);
+ qemu_put_be32 (f, r->now[1]);
+ qemu_put_be16s (f, &r->base[0]);
+ qemu_put_be16s (f, &r->base[1]);
+ qemu_put_8s (f, &r->mode);
+ qemu_put_8s (f, &r->page);
+ qemu_put_8s (f, &r->pageh);
+ qemu_put_8s (f, &r->dack);
+ qemu_put_8s (f, &r->eop);
+ }
+}
+
+static int dma_load (QEMUFile *f, void *opaque, int version_id)
+{
+ struct dma_cont *d = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ /* qemu_get_8s (f, &d->status); */
+ qemu_get_8s (f, &d->command);
+ qemu_get_8s (f, &d->mask);
+ qemu_get_8s (f, &d->flip_flop);
+ d->dshift=qemu_get_be32 (f);
+
+ for (i = 0; i < 4; ++i) {
+ struct dma_regs *r = &d->regs[i];
+ r->now[0]=qemu_get_be32 (f);
+ r->now[1]=qemu_get_be32 (f);
+ qemu_get_be16s (f, &r->base[0]);
+ qemu_get_be16s (f, &r->base[1]);
+ qemu_get_8s (f, &r->mode);
+ qemu_get_8s (f, &r->page);
+ qemu_get_8s (f, &r->pageh);
+ qemu_get_8s (f, &r->dack);
+ qemu_get_8s (f, &r->eop);
+ }
+ return 0;
+}
+
+void DMA_init (int high_page_enable)
+{
+ dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
+ high_page_enable ? 0x480 : -1);
+ dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
+ high_page_enable ? 0x488 : -1);
+ register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
+ register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
+}
diff --git a/hw/goldfish_audio.c b/hw/goldfish_audio.c
new file mode 100644
index 0000000..d0a44b5
--- /dev/null
+++ b/hw/goldfish_audio.c
@@ -0,0 +1,533 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "audio/audio.h"
+#include "qemu_debug.h"
+#include "android/globals.h"
+
+#define DEBUG 1
+
+#if DEBUG
+# define D(...) VERBOSE_PRINT(audio,__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+extern void dprint(const char* fmt, ...);
+
+/* define USE_QEMU_AUDIO_IN to 1 to use QEMU's audio subsystem to
+ * implement the audio input. if 0, this will try to read a .wav file
+ * directly...
+ */
+#define USE_QEMU_AUDIO_IN 1
+
+enum {
+ /* audio status register */
+ AUDIO_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ AUDIO_INT_ENABLE = 0x04,
+ /* set these to specify buffer addresses */
+ AUDIO_SET_WRITE_BUFFER_1 = 0x08,
+ AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
+ /* set number of bytes in buffer to write */
+ AUDIO_WRITE_BUFFER_1 = 0x10,
+ AUDIO_WRITE_BUFFER_2 = 0x14,
+
+ /* true if audio input is supported */
+ AUDIO_READ_SUPPORTED = 0x18,
+ /* buffer to use for audio input */
+ AUDIO_SET_READ_BUFFER = 0x1C,
+
+ /* driver writes number of bytes to read */
+ AUDIO_START_READ = 0x20,
+
+ /* number of bytes available in read buffer */
+ AUDIO_READ_BUFFER_AVAILABLE = 0x24,
+
+ /* AUDIO_INT_STATUS bits */
+
+ /* this bit set when it is safe to write more bytes to the buffer */
+ AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0,
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1,
+ AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
+};
+
+
+struct goldfish_audio_state {
+ struct goldfish_device dev;
+ // pointers to our two write buffers
+ uint32_t buffer_1, buffer_2;
+ uint32_t read_buffer;
+ // buffer flags
+ uint32_t int_status;
+ // irq enable mask for int_status
+ uint32_t int_enable;
+
+#if USE_QEMU_AUDIO_IN
+ uint32_t read_pos;
+ uint32_t read_size;
+#else
+ // path to file or device to use for input
+ const char* input_source;
+ // true if input is a wav file
+ int input_is_wav;
+ // true if we need to convert stereo -> mono
+ int input_is_stereo;
+ // file descriptor to use for input
+ int input_fd;
+#endif
+
+ // number of bytes available in the read buffer
+ int read_buffer_available;
+
+ // set to 1 or 2 to indicate which buffer we are writing from, or zero if both buffers are empty
+ int current_buffer;
+
+ // current data to write
+ uint8* data_1;
+ uint32_t data_1_length;
+ uint8* data_2;
+ uint32_t data_2_length;
+
+
+ // for QEMU sound output
+ QEMUSoundCard card;
+ SWVoiceOut *voice;
+#if USE_QEMU_AUDIO_IN
+ SWVoiceIn* voicein;
+#endif
+};
+
+/* update this whenever you change the goldfish_audio_state structure */
+#define AUDIO_STATE_SAVE_VERSION 1
+
+#define QFIELD_STRUCT struct goldfish_audio_state
+QFIELD_BEGIN(audio_state_fields)
+ QFIELD_INT32(buffer_1),
+ QFIELD_INT32(buffer_2),
+ QFIELD_INT32(read_buffer),
+ QFIELD_INT32(int_status),
+ QFIELD_INT32(int_enable),
+#if USE_QEMU_AUDIO_IN
+ QFIELD_INT32(read_pos),
+ QFIELD_INT32(read_size),
+#endif
+ QFIELD_INT32(read_buffer_available),
+ QFIELD_INT32(current_buffer),
+ QFIELD_INT32(data_1_length),
+ QFIELD_INT32(data_2_length),
+QFIELD_END
+
+static void audio_state_save( QEMUFile* f, void* opaque )
+{
+ struct goldfish_audio_state* s = opaque;
+
+ qemu_put_struct(f, audio_state_fields, s);
+
+ /* we can't write data_1 and data_2 directly */
+ qemu_put_be32( f, s->data_1 - phys_ram_base );
+ qemu_put_be32( f, s->data_2 - phys_ram_base );
+}
+
+static int audio_state_load( QEMUFile* f, void* opaque, int version_id )
+{
+ struct goldfish_audio_state* s = opaque;
+ int ret;
+
+ if (version_id != AUDIO_STATE_SAVE_VERSION)
+ return -1;
+
+ ret = qemu_get_struct(f, audio_state_fields, s);
+ if (!ret) {
+ s->data_1 = qemu_get_be32(f) + phys_ram_base;
+ s->data_2 = qemu_get_be32(f) + phys_ram_base;
+ }
+ return -1;
+}
+
+static void enable_audio(struct goldfish_audio_state *s, int enable)
+{
+ // enable or disable the output voice
+ if (s->voice != NULL)
+ AUD_set_active_out(s->voice, (enable & (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY)) != 0);
+
+ if (s->voicein)
+ AUD_set_active_in (s->voicein, (enable & AUDIO_INT_READ_BUFFER_FULL) != 0);
+ // reset buffer information
+ s->data_1_length = 0;
+ s->data_2_length = 0;
+ s->current_buffer = 0;
+ s->read_pos = 0;
+}
+
+#if USE_QEMU_AUDIO_IN
+static void start_read(struct goldfish_audio_state *s, uint32_t count)
+{
+ //printf( "... goldfish audio start_read, count=%d\n", count );
+ s->read_size = count;
+ s->read_buffer_available = 0;
+ s->read_pos = 0;
+}
+#else
+static void start_read(struct goldfish_audio_state *s, uint32_t count)
+{
+ uint8 wav_header[44];
+ int result;
+
+ if (!s->input_source) return;
+
+ if (s->input_fd < 0) {
+ s->input_fd = open(s->input_source, O_BINARY | O_RDONLY);
+
+ if (s->input_fd < 0) {
+ fprintf(stderr, "goldfish_audio could not open %s for audio input\n", s->input_source);
+ s->input_source = NULL; // set to to avoid endless retries
+ return;
+ }
+
+ // skip WAV header if we have a WAV file
+ if (s->input_is_wav) {
+ if (read(s->input_fd, wav_header, sizeof(wav_header)) != sizeof(wav_header)) {
+ fprintf(stderr, "goldfish_audio could not read WAV file header %s\n", s->input_source);
+ s->input_fd = -1;
+ s->input_source = NULL; // set to to avoid endless retries
+ return;
+ }
+
+ // is the WAV file stereo?
+ s->input_is_stereo = (wav_header[22] == 2);
+ } else {
+ // assume input from an audio device is stereo
+ s->input_is_stereo = 1;
+ }
+ }
+
+ uint8* buffer = (uint8*)phys_ram_base + s->read_buffer;
+ if (s->input_is_stereo) {
+ // need to read twice as much data
+ count *= 2;
+ }
+
+try_again:
+ result = read(s->input_fd, buffer, count);
+ if (result == 0 && s->input_is_wav) {
+ // end of file, so seek back to the beginning
+ lseek(s->input_fd, sizeof(wav_header), SEEK_SET);
+ goto try_again;
+ }
+
+ if (result > 0 && s->input_is_stereo) {
+ // we need to convert stereo to mono
+ uint8* src = (uint8*)buffer;
+ uint8* dest = src;
+ int count = result/2;
+ while (count-- > 0) {
+ int sample1 = src[0] | (src[1] << 8);
+ int sample2 = src[2] | (src[3] << 8);
+ int sample = (sample1 + sample2) >> 1;
+ dst[0] = (uint8_t) sample;
+ dst[1] = (uint8_t)(sample >> 8);
+ src += 4;
+ dst += 2;
+ }
+
+ // we reduced the number of bytes by 2
+ result /= 2;
+ }
+
+ s->read_buffer_available = (result > 0 ? result : 0);
+ s->int_status |= AUDIO_INT_READ_BUFFER_FULL;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+}
+#endif
+
+static uint32_t goldfish_audio_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t ret;
+ struct goldfish_audio_state *s = opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case AUDIO_INT_STATUS:
+ // return current buffer status flags
+ ret = s->int_status & s->int_enable;
+ if(ret) {
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ }
+ return ret;
+
+ case AUDIO_READ_SUPPORTED:
+#if USE_QEMU_AUDIO_IN
+ D("%s: AUDIO_READ_SUPPORTED returns %d", __FUNCTION__,
+ (s->voicein != NULL));
+ return (s->voicein != NULL);
+#else
+ return (s->input_source ? 1 : 0);
+#endif
+
+ case AUDIO_READ_BUFFER_AVAILABLE:
+ D("%s: AUDIO_READ_BUFFER_AVAILABLE returns %d", __FUNCTION__,
+ s->read_buffer_available);
+ return s->read_buffer_available;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_audio_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_audio_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ struct goldfish_audio_state *s = opaque;
+ offset -= s->dev.base;
+
+ switch(offset) {
+ case AUDIO_INT_ENABLE:
+ /* enable buffer empty interrupts */
+ D("%s: AUDIO_INT_ENABLE %d", __FUNCTION__, val );
+ enable_audio(s, val);
+ s->int_enable = val;
+ s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+ case AUDIO_SET_WRITE_BUFFER_1:
+ /* save pointer to buffer 1 */
+ s->buffer_1 = val;
+ break;
+ case AUDIO_SET_WRITE_BUFFER_2:
+ /* save pointer to buffer 2 */
+ s->buffer_2 = val;
+ break;
+ case AUDIO_WRITE_BUFFER_1:
+ /* record that data in buffer 1 is ready to write */
+ if (s->current_buffer == 0) s->current_buffer = 1;
+ s->data_1 = phys_ram_base + s->buffer_1;
+ s->data_1_length = val;
+ s->int_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY;
+ break;
+ case AUDIO_WRITE_BUFFER_2:
+ /* record that data in buffer 2 is ready to write */
+ if (s->current_buffer == 0) s->current_buffer = 2;
+ s->data_2 = phys_ram_base + s->buffer_2;
+ s->data_2_length = val;
+ s->int_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY;
+ break;
+
+ case AUDIO_SET_READ_BUFFER:
+ /* save pointer to the read buffer */
+ s->read_buffer = val;
+ D( "%s: AUDIO_SET_READ_BUFFER %p", __FUNCTION__, (void*)val );
+ break;
+
+ case AUDIO_START_READ:
+ D( "%s: AUDIO_START_READ %d", __FUNCTION__, val );
+ start_read(s, val);
+ s->int_status &= ~AUDIO_INT_READ_BUFFER_FULL;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
+ }
+}
+
+static void goldfish_audio_callback(void *opaque, int free)
+{
+ struct goldfish_audio_state *s = opaque;
+ int new_status = 0;
+
+ /* loop until free is zero or both buffers are empty */
+ while (free && s->current_buffer) {
+
+ /* write data in buffer 1 */
+ while (free && s->current_buffer == 1) {
+ int write = s->data_1_length;
+ if (write > free) write = free;
+
+ int written = AUD_write(s->voice, s->data_1, write);
+ if (written) {
+ D("%s: sent %d bytes to audio output", __FUNCTION__, write);
+ s->data_1 += written;
+ s->data_1_length -= written;
+ free -= written;
+
+ if (s->data_1_length == 0) {
+ new_status |= AUDIO_INT_WRITE_BUFFER_1_EMPTY;
+ s->current_buffer = (s->data_2_length ? 2 : 0);
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* write data in buffer 2 */
+ while (free && s->current_buffer == 2) {
+ int write = s->data_2_length;
+ if (write > free) write = free;
+
+ int written = AUD_write(s->voice, s->data_2, write);
+ if (written) {
+ D("%s: sent %d bytes to audio output", __FUNCTION__, write);
+ s->data_2 += written;
+ s->data_2_length -= written;
+ free -= written;
+
+ if (s->data_2_length == 0) {
+ new_status |= AUDIO_INT_WRITE_BUFFER_2_EMPTY;
+ s->current_buffer = (s->data_1_length ? 1 : 0);
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (new_status && new_status != s->int_status) {
+ s->int_status |= new_status;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ }
+}
+
+#if USE_QEMU_AUDIO_IN
+static void
+goldfish_audio_in_callback(void *opaque, int avail)
+{
+ struct goldfish_audio_state *s = opaque;
+ int new_status = 0;
+
+ if (s->read_pos >= s->read_size)
+ return;
+
+ if (0 && s->read_size > 0)
+ D("%s: in %d (pos=%d size=%d)", __FUNCTION__,
+ avail, s->read_pos, s->read_size );
+
+ while (avail > 0) {
+ int pos = s->read_pos;
+ int missing = s->read_size - pos;
+ uint8* buffer = (uint8*)phys_ram_base + s->read_buffer + pos;
+ int read;
+ int avail2 = (avail > missing) ? missing : avail;
+
+ read = AUD_read(s->voicein, buffer, avail2);
+ if (read == 0)
+ break;
+
+ if (avail2 > 0)
+ D("%s: AUD_read(%d) returned %d", __FUNCTION__, avail2, read);
+
+ s->read_buffer_available += read;
+
+ avail -= read;
+ pos += read;
+ if (pos == s->read_size) {
+ new_status |= AUDIO_INT_READ_BUFFER_FULL;
+ D("%s: AUDIO_INT_READ_BUFFER_FULL available=%d", __FUNCTION__, s->read_buffer_available);
+ }
+ s->read_pos = pos;
+ }
+
+ if (new_status && new_status != s->int_status) {
+ s->int_status |= new_status;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ }
+}
+#endif /* USE_QEMU_AUDIO_IN */
+
+static CPUReadMemoryFunc *goldfish_audio_readfn[] = {
+ goldfish_audio_read,
+ goldfish_audio_read,
+ goldfish_audio_read
+};
+
+static CPUWriteMemoryFunc *goldfish_audio_writefn[] = {
+ goldfish_audio_write,
+ goldfish_audio_write,
+ goldfish_audio_write
+};
+
+void goldfish_audio_init(uint32_t base, int id, const char* input_source)
+{
+ struct goldfish_audio_state *s;
+ audsettings_t as;
+
+ /* nothing to do if no audio input and output */
+ if (!android_hw->hw_audioOutput && !android_hw->hw_audioInput)
+ return;
+
+ s = (struct goldfish_audio_state *)qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish_audio";
+ s->dev.id = id;
+ s->dev.base = base;
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+
+#ifndef USE_QEMU_AUDIO_IN
+ s->input_fd = -1;
+ if (input_source) {
+ s->input_source = input_source;
+ char* extension = strrchr(input_source, '.');
+ if (extension && strcasecmp(extension, ".wav") == 0) {
+ s->input_is_wav = 1;
+ }
+ }
+#endif
+
+ AUD_register_card( &glob_audio_state, "goldfish_audio", &s->card);
+
+ as.freq = 44100;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ if (android_hw->hw_audioOutput) {
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "goldfish_audio",
+ s,
+ goldfish_audio_callback,
+ &as
+ );
+ if (!s->voice) {
+ dprint("warning: opening audio output failed\n");
+ return;
+ }
+ }
+
+#if USE_QEMU_AUDIO_IN
+ as.freq = 8000;
+ as.nchannels = 1;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ if (android_hw->hw_audioInput) {
+ s->voicein = AUD_open_in (
+ &s->card,
+ NULL,
+ "goldfish_audio_in",
+ s,
+ goldfish_audio_in_callback,
+ &as
+ );
+ if (!s->voicein) {
+ dprint("warning: opening audio input failed\n");
+ }
+ }
+#endif
+
+ goldfish_device_add(&s->dev, goldfish_audio_readfn, goldfish_audio_writefn, s);
+
+ register_savevm( "audio_state", 0, AUDIO_STATE_SAVE_VERSION,
+ audio_state_save, audio_state_load, s );
+}
+
diff --git a/hw/goldfish_battery.c b/hw/goldfish_battery.c
new file mode 100644
index 0000000..d9ef785
--- /dev/null
+++ b/hw/goldfish_battery.c
@@ -0,0 +1,261 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "power_supply.h"
+
+
+enum {
+ /* status register */
+ BATTERY_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ BATTERY_INT_ENABLE = 0x04,
+
+ BATTERY_AC_ONLINE = 0x08,
+ BATTERY_STATUS = 0x0C,
+ BATTERY_HEALTH = 0x10,
+ BATTERY_PRESENT = 0x14,
+ BATTERY_CAPACITY = 0x18,
+
+ BATTERY_STATUS_CHANGED = 1U << 0,
+ AC_STATUS_CHANGED = 1U << 1,
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+struct goldfish_battery_state {
+ struct goldfish_device dev;
+ // IRQs
+ uint32_t int_status;
+ // irq enable mask for int_status
+ uint32_t int_enable;
+
+ int ac_online;
+ int status;
+ int health;
+ int present;
+ int capacity;
+};
+
+/* update this each time you update the battery_state struct */
+#define BATTERY_STATE_SAVE_VERSION 1
+
+#define QFIELD_STRUCT struct goldfish_battery_state
+QFIELD_BEGIN(goldfish_battery_fields)
+ QFIELD_INT32(int_status),
+ QFIELD_INT32(int_enable),
+ QFIELD_INT32(ac_online),
+ QFIELD_INT32(status),
+ QFIELD_INT32(health),
+ QFIELD_INT32(present),
+ QFIELD_INT32(capacity),
+QFIELD_END
+
+static void goldfish_battery_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_battery_state* s = opaque;
+
+ qemu_put_struct(f, goldfish_battery_fields, s);
+}
+
+static int goldfish_battery_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_battery_state* s = opaque;
+
+ if (version_id != BATTERY_STATE_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, goldfish_battery_fields, s);
+}
+
+static struct goldfish_battery_state *battery_state;
+
+static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t ret;
+ struct goldfish_battery_state *s = opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case BATTERY_INT_STATUS:
+ // return current buffer status flags
+ ret = s->int_status & s->int_enable;
+ if (ret) {
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ s->int_status = 0;
+ }
+ return ret;
+
+ case BATTERY_INT_ENABLE:
+ return s->int_enable;
+ case BATTERY_AC_ONLINE:
+ return s->ac_online;
+ case BATTERY_STATUS:
+ return s->status;
+ case BATTERY_HEALTH:
+ return s->health;
+ case BATTERY_PRESENT:
+ return s->present;
+ case BATTERY_CAPACITY:
+ return s->capacity;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_battery_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ struct goldfish_battery_state *s = opaque;
+ offset -= s->dev.base;
+
+ switch(offset) {
+ case BATTERY_INT_ENABLE:
+ /* enable interrupts */
+ s->int_enable = val;
+// s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+// goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
+ goldfish_battery_read,
+ goldfish_battery_read,
+ goldfish_battery_read
+};
+
+
+static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
+ goldfish_battery_write,
+ goldfish_battery_write,
+ goldfish_battery_write
+};
+
+void goldfish_battery_init()
+{
+ struct goldfish_battery_state *s;
+
+ s = (struct goldfish_battery_state *)qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish-battery";
+ s->dev.base = 0; // will be allocated dynamically
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+
+ // default values for the battery
+ s->ac_online = 1;
+ s->status = POWER_SUPPLY_STATUS_CHARGING;
+ s->health = POWER_SUPPLY_HEALTH_GOOD;
+ s->present = 1; // battery is present
+ s->capacity = 50; // 50% charged
+
+ battery_state = s;
+
+ goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
+
+ register_savevm( "battery_state", 0, BATTERY_STATE_SAVE_VERSION,
+ goldfish_battery_save, goldfish_battery_load, s);
+}
+
+void goldfish_battery_set_prop(int ac, int property, int value)
+{
+ int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED);
+
+ if (ac) {
+ switch (property) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ battery_state->ac_online = value;
+ break;
+ }
+ } else {
+ switch (property) {
+ case POWER_SUPPLY_PROP_STATUS:
+ battery_state->status = value;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ battery_state->health = value;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ battery_state->present = value;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ battery_state->capacity = value;
+ break;
+ }
+ }
+
+ if (new_status != battery_state->int_status) {
+ battery_state->int_status |= new_status;
+ goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable));
+ }
+}
+
+void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data)
+{
+ char buffer[100];
+ char* value;
+
+ sprintf(buffer, "AC: %s\r\n", (battery_state->ac_online ? "online" : "offline"));
+ callback(data, buffer);
+
+ switch (battery_state->status) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ value = "Charging";
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ value = "Discharging";
+ break;
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ value = "Not charging";
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ value = "Full";
+ break;
+ default:
+ value = "Unknown";
+ break;
+ }
+ sprintf(buffer, "status: %s\r\n", value);
+ callback(data, buffer);
+
+ switch (battery_state->health) {
+ case POWER_SUPPLY_HEALTH_GOOD:
+ value = "Good";
+ break;
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ value = "Overhead";
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ value = "Dead";
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ value = "Overvoltage";
+ break;
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ value = "Unspecified failure";
+ break;
+ default:
+ value = "Unknown";
+ break;
+ }
+ sprintf(buffer, "health: %s\r\n", value);
+ callback(data, buffer);
+
+ sprintf(buffer, "present: %s\r\n", (battery_state->present ? "true" : "false"));
+ callback(data, buffer);
+
+ sprintf(buffer, "capacity: %d\r\n", battery_state->capacity);
+ callback(data, buffer);
+}
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
new file mode 100644
index 0000000..2c9dd6e
--- /dev/null
+++ b/hw/goldfish_device.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+
+#define PDEV_BUS_OP_DONE (0x00)
+#define PDEV_BUS_OP_REMOVE_DEV (0x04)
+#define PDEV_BUS_OP_ADD_DEV (0x08)
+
+#define PDEV_BUS_OP_INIT (0x00)
+
+#define PDEV_BUS_OP (0x00)
+#define PDEV_BUS_GET_NAME (0x04)
+#define PDEV_BUS_NAME_LEN (0x08)
+#define PDEV_BUS_ID (0x0c)
+#define PDEV_BUS_IO_BASE (0x10)
+#define PDEV_BUS_IO_SIZE (0x14)
+#define PDEV_BUS_IRQ (0x18)
+#define PDEV_BUS_IRQ_COUNT (0x1c)
+
+struct bus_state {
+ struct goldfish_device dev;
+ struct goldfish_device *current;
+};
+
+qemu_irq *goldfish_pic;
+static struct goldfish_device *first_device;
+static struct goldfish_device *last_device;
+uint32_t goldfish_free_base;
+uint32_t goldfish_free_irq;
+
+void goldfish_device_set_irq(struct goldfish_device *dev, int irq, int level)
+{
+ if(irq >= dev->irq_count)
+ cpu_abort (cpu_single_env, "goldfish_device_set_irq: Bad irq %d >= %d\n", irq, dev->irq_count);
+ else
+ qemu_set_irq(goldfish_pic[dev->irq + irq], level);
+}
+
+int goldfish_add_device_no_io(struct goldfish_device *dev)
+{
+ if(dev->base == 0) {
+ dev->base = goldfish_free_base;
+ goldfish_free_base += dev->size;
+ }
+ if(dev->irq == 0 && dev->irq_count > 0) {
+ dev->irq = goldfish_free_irq;
+ goldfish_free_irq += dev->irq_count;
+ }
+ //printf("goldfish_add_device: %s, base %x %x, irq %d %d\n",
+ // dev->name, dev->base, dev->size, dev->irq, dev->irq_count);
+ dev->next = NULL;
+ if(last_device) {
+ last_device->next = dev;
+ }
+ else {
+ first_device = dev;
+ }
+ last_device = dev;
+ return 0;
+}
+
+int goldfish_device_add(struct goldfish_device *dev,
+ CPUReadMemoryFunc **mem_read,
+ CPUWriteMemoryFunc **mem_write,
+ void *opaque)
+{
+ int iomemtype;
+ goldfish_add_device_no_io(dev);
+ iomemtype = cpu_register_io_memory(0, mem_read,
+ mem_write, opaque);
+ cpu_register_physical_memory(dev->base, dev->size, iomemtype);
+ return 0;
+}
+
+static uint32_t goldfish_bus_read(void *opaque, target_phys_addr_t offset)
+{
+ struct bus_state *s = (struct bus_state *)opaque;
+ offset -= s->dev.base;
+
+ switch (offset) {
+ case PDEV_BUS_OP:
+ if(s->current) {
+ s->current->reported_state = 1;
+ s->current = s->current->next;
+ }
+ else {
+ s->current = first_device;
+ }
+ while(s->current && s->current->reported_state == 1)
+ s->current = s->current->next;
+ if(s->current)
+ return PDEV_BUS_OP_ADD_DEV;
+ else {
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ return PDEV_BUS_OP_DONE;
+ }
+
+ case PDEV_BUS_NAME_LEN:
+ return s->current ? strlen(s->current->name) : 0;
+ case PDEV_BUS_ID:
+ return s->current ? s->current->id : 0;
+ case PDEV_BUS_IO_BASE:
+ return s->current ? s->current->base : 0;
+ case PDEV_BUS_IO_SIZE:
+ return s->current ? s->current->size : 0;
+ case PDEV_BUS_IRQ:
+ return s->current ? s->current->irq : 0;
+ case PDEV_BUS_IRQ_COUNT:
+ return s->current ? s->current->irq_count : 0;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_bus_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_bus_op_init(struct bus_state *s)
+{
+ struct goldfish_device *dev = first_device;
+ while(dev) {
+ dev->reported_state = 0;
+ dev = dev->next;
+ }
+ s->current = NULL;
+ goldfish_device_set_irq(&s->dev, 0, first_device != NULL);
+}
+
+static void goldfish_bus_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct bus_state *s = (struct bus_state *)opaque;
+ offset -= s->dev.base;
+
+ switch(offset) {
+ case PDEV_BUS_OP:
+ switch(value) {
+ case PDEV_BUS_OP_INIT:
+ goldfish_bus_op_init(s);
+ break;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_bus_write: Bad PDEV_BUS_OP value %x\n", value);
+ };
+ break;
+ case PDEV_BUS_GET_NAME:
+ if(s->current)
+ pmemcpy(value, s->current->name, strlen(s->current->name));
+ break;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_bus_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_bus_readfn[] = {
+ goldfish_bus_read,
+ goldfish_bus_read,
+ goldfish_bus_read
+};
+
+static CPUWriteMemoryFunc *goldfish_bus_writefn[] = {
+ goldfish_bus_write,
+ goldfish_bus_write,
+ goldfish_bus_write
+};
+
+
+static struct bus_state bus_state = {
+ .dev = {
+ .name = "goldfish_device_bus",
+ .id = -1,
+ .base = 0x10001000,
+ .size = 0x1000,
+ .irq = 1,
+ .irq_count = 1,
+ }
+};
+
+void goldfish_device_init(qemu_irq *pic, uint32_t base, uint32_t size, uint32_t irq, uint32_t irq_count)
+{
+ goldfish_pic = pic;
+ goldfish_free_base = base;
+ goldfish_free_irq = irq;
+}
+
+int goldfish_device_bus_init(uint32_t base, uint32_t irq)
+{
+ bus_state.dev.base = base;
+ bus_state.dev.irq = irq;
+
+ return goldfish_device_add(&bus_state.dev, goldfish_bus_readfn, goldfish_bus_writefn, &bus_state);
+}
+
diff --git a/hw/goldfish_device.h b/hw/goldfish_device.h
new file mode 100644
index 0000000..abe102e
--- /dev/null
+++ b/hw/goldfish_device.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#ifndef GOLDFISH_DEVICE_H
+#define GOLDFISH_DEVICE_H
+
+struct goldfish_device {
+ struct goldfish_device *next;
+ struct goldfish_device *prev;
+ uint32_t reported_state;
+ void *cookie;
+ const char *name;
+ uint32_t id;
+ uint32_t base; // filled in by goldfish_device_add if 0
+ uint32_t size;
+ uint32_t irq; // filled in by goldfish_device_add if 0
+ uint32_t irq_count;
+};
+
+
+void goldfish_device_set_irq(struct goldfish_device *dev, int irq, int level);
+int goldfish_device_add(struct goldfish_device *dev,
+ CPUReadMemoryFunc **mem_read,
+ CPUWriteMemoryFunc **mem_write,
+ void *opaque);
+
+int goldfish_add_device_no_io(struct goldfish_device *dev);
+
+void goldfish_device_init(qemu_irq *pic, uint32_t base, uint32_t size, uint32_t irq, uint32_t irq_count);
+int goldfish_device_bus_init(uint32_t base, uint32_t irq);
+
+// device init functions:
+qemu_irq *goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq);
+void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq);
+int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq);
+void goldfish_fb_init(DisplayState *ds, int id);
+void goldfish_audio_init(uint32_t base, int id, const char* input_source);
+void goldfish_battery_init();
+void goldfish_battery_set_prop(int ac, int property, int value);
+void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data);
+void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs);
+void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id);
+void goldfish_switch_set_state(void *opaque, uint32_t state);
+
+// these do not add a device
+void trace_dev_init(uint32_t base);
+void events_dev_init(uint32_t base, qemu_irq irq);
+void nand_dev_init(uint32_t base);
+
+#endif
diff --git a/hw/goldfish_events_device.c b/hw/goldfish_events_device.c
new file mode 100644
index 0000000..4cb2904
--- /dev/null
+++ b/hw/goldfish_events_device.c
@@ -0,0 +1,423 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "android/hw-events.h"
+#include "irq.h"
+
+#if 0
+// From kernel...
+#define EV_SYN 0x00
+#define EV_KEY 0x01
+#define EV_REL 0x02
+#define EV_ABS 0x03
+#define EV_MSC 0x04
+#define EV_SW 0x05
+#define EV_LED 0x11
+#define EV_SND 0x12
+#define EV_REP 0x14
+#define EV_FF 0x15
+#define EV_PWR 0x16
+#define EV_FF_STATUS 0x17
+#define EV_MAX 0x1f
+
+#define BTN_MISC 0x100
+#define BTN_0 0x100
+#define BTN_1 0x101
+#define BTN_2 0x102
+#define BTN_3 0x103
+#define BTN_4 0x104
+#define BTN_5 0x105
+#define BTN_6 0x106
+#define BTN_7 0x107
+#define BTN_8 0x108
+#define BTN_9 0x109
+
+#define BTN_MOUSE 0x110
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_SIDE 0x113
+#define BTN_EXTRA 0x114
+#define BTN_FORWARD 0x115
+#define BTN_BACK 0x116
+#define BTN_TASK 0x117
+
+#define BTN_JOYSTICK 0x120
+#define BTN_TRIGGER 0x120
+#define BTN_THUMB 0x121
+#define BTN_THUMB2 0x122
+#define BTN_TOP 0x123
+#define BTN_TOP2 0x124
+#define BTN_PINKIE 0x125
+#define BTN_BASE 0x126
+#define BTN_BASE2 0x127
+#define BTN_BASE3 0x128
+#define BTN_BASE4 0x129
+#define BTN_BASE5 0x12a
+#define BTN_BASE6 0x12b
+#define BTN_DEAD 0x12f
+
+#define BTN_GAMEPAD 0x130
+#define BTN_A 0x130
+#define BTN_B 0x131
+#define BTN_C 0x132
+#define BTN_X 0x133
+#define BTN_Y 0x134
+#define BTN_Z 0x135
+#define BTN_TL 0x136
+#define BTN_TR 0x137
+#define BTN_TL2 0x138
+#define BTN_TR2 0x139
+#define BTN_SELECT 0x13a
+#define BTN_START 0x13b
+#define BTN_MODE 0x13c
+#define BTN_THUMBL 0x13d
+#define BTN_THUMBR 0x13e
+
+#define BTN_DIGI 0x140
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOOL_LENS 0x147
+#define BTN_TOUCH 0x14a
+#define BTN_STYLUS 0x14b
+#define BTN_STYLUS2 0x14c
+#define BTN_TOOL_DOUBLETAP 0x14d
+#define BTN_TOOL_TRIPLETAP 0x14e
+
+#define BTN_WHEEL 0x150
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define REL_X 0x00
+#define REL_Y 0x01
+
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_RX 0x03
+#define ABS_RY 0x04
+#define ABS_RZ 0x05
+#define ABS_THROTTLE 0x06
+#define ABS_RUDDER 0x07
+#define ABS_WHEEL 0x08
+#define ABS_GAS 0x09
+#define ABS_BRAKE 0x0a
+#define ABS_HAT0X 0x10
+#define ABS_HAT0Y 0x11
+#define ABS_HAT1X 0x12
+#define ABS_HAT1Y 0x13
+#define ABS_HAT2X 0x14
+#define ABS_HAT2Y 0x15
+#define ABS_HAT3X 0x16
+#define ABS_HAT3Y 0x17
+#define ABS_PRESSURE 0x18
+#define ABS_DISTANCE 0x19
+#define ABS_TILT_X 0x1a
+#define ABS_TILT_Y 0x1b
+#define ABS_TOOL_WIDTH 0x1c
+#define ABS_VOLUME 0x20
+#define ABS_MISC 0x28
+#define ABS_MAX 0x3f
+#endif
+
+#define MAX_EVENTS 256*4
+
+enum {
+ REG_READ = 0x00,
+ REG_SET_PAGE = 0x00,
+ REG_LEN = 0x04,
+ REG_DATA = 0x08,
+
+ PAGE_NAME = 0x00000,
+ PAGE_EVBITS = 0x10000,
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
+};
+
+typedef struct
+{
+ uint32_t base;
+ qemu_irq irq;
+ int pending;
+ int page;
+
+ unsigned events[MAX_EVENTS];
+ unsigned first;
+ unsigned last;
+
+ const char *name;
+ struct {
+ size_t len;
+ uint8_t *bits;
+ } ev_bits[EV_MAX + 1];
+ int32_t *abs_info;
+ size_t abs_info_count;
+} events_state;
+
+/* modify this each time you change the events_device structure. you
+ * will also need to upadte events_state_load and events_state_save
+ */
+#define EVENTS_STATE_SAVE_VERSION 1
+
+#undef QFIELD_STRUCT
+#define QFIELD_STRUCT events_state
+
+QFIELD_BEGIN(events_state_fields)
+ QFIELD_INT32(pending),
+ QFIELD_INT32(page),
+ QFIELD_BUFFER(events),
+ QFIELD_INT32(first),
+ QFIELD_INT32(last),
+QFIELD_END
+
+static void events_state_save(QEMUFile* f, void* opaque)
+{
+ events_state* s = opaque;
+
+ qemu_put_struct(f, events_state_fields, s);
+}
+
+static int events_state_load(QEMUFile* f, void* opaque, int version_id)
+{
+ events_state* s = opaque;
+
+ if (version_id != EVENTS_STATE_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, events_state_fields, s);
+}
+
+extern const char* android_skin_keycharmap;
+
+static void enqueue_event(events_state *s, unsigned int type, unsigned int code, int value)
+{
+ int enqueued = s->last - s->first;
+
+ if (enqueued < 0)
+ enqueued += MAX_EVENTS;
+
+ if (enqueued + 3 >= MAX_EVENTS-1) {
+ fprintf(stderr, "##KBD: Full queue, lose event\n");
+ return;
+ }
+
+ if(s->first == s->last){
+ qemu_irq_raise(s->irq);
+ }
+
+ //fprintf(stderr, "##KBD: type=%d code=%d value=%d\n", type, code, value);
+
+ s->events[s->last] = type;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+ s->events[s->last] = code;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+ s->events[s->last] = value;
+ s->last = (s->last + 1) & (MAX_EVENTS-1);
+}
+
+static unsigned dequeue_event(events_state *s)
+{
+ unsigned n;
+
+ if(s->first == s->last) {
+ return 0;
+ }
+
+ n = s->events[s->first];
+
+ s->first = (s->first + 1) & (MAX_EVENTS - 1);
+
+ if(s->first == s->last) {
+ qemu_irq_lower(s->irq);
+ }
+
+ return n;
+}
+
+static int get_page_len(events_state *s)
+{
+ int page = s->page;
+ if (page == PAGE_NAME)
+ return strlen(s->name);
+ if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
+ return s->ev_bits[page - PAGE_EVBITS].len;
+ if (page == PAGE_ABSDATA)
+ return s->abs_info_count * sizeof(s->abs_info[0]);
+ return 0;
+}
+
+static int get_page_data(events_state *s, int offset)
+{
+ int page_len = get_page_len(s);
+ int page = s->page;
+ if (offset > page_len)
+ return 0;
+ if (page == PAGE_NAME)
+ return s->name[offset];
+ if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
+ return s->ev_bits[page - PAGE_EVBITS].bits[offset];
+ if (page == PAGE_ABSDATA)
+ return s->abs_info[offset / sizeof(s->abs_info[0])];
+ return 0;
+}
+
+static uint32_t events_read(void *x, target_phys_addr_t off)
+{
+ events_state *s = (events_state *) x;
+ int offset = off - s->base;
+ if (offset == REG_READ)
+ return dequeue_event(s);
+ else if (offset == REG_LEN)
+ return get_page_len(s);
+ else if (offset >= REG_DATA)
+ return get_page_data(s, offset - REG_DATA);
+ return 0; // this shouldn't happen, if the driver does the right thing
+}
+
+static void events_write(void *x, target_phys_addr_t off, uint32_t val)
+{
+ events_state *s = (events_state *) x;
+ int offset = off - s->base;
+ if (offset == REG_SET_PAGE)
+ s->page = val;
+}
+
+static CPUReadMemoryFunc *events_readfn[] = {
+ events_read,
+ events_read,
+ events_read
+};
+
+static CPUWriteMemoryFunc *events_writefn[] = {
+ events_write,
+ events_write,
+ events_write
+};
+
+static void events_put_keycode(void *x, int keycode)
+{
+ events_state *s = (events_state *) x;
+
+ enqueue_event(s, EV_KEY, keycode&0x1ff, (keycode&0x200) ? 1 : 0);
+}
+
+static void events_put_mouse(void *opaque, int dx, int dy, int dz, int buttons_state)
+{
+ events_state *s = (events_state *) opaque;
+ if (dz == 0) {
+ enqueue_event(s, EV_ABS, ABS_X, dx);
+ enqueue_event(s, EV_ABS, ABS_Y, dy);
+ enqueue_event(s, EV_ABS, ABS_Z, dz);
+ enqueue_event(s, EV_KEY, BTN_TOUCH, buttons_state&1);
+ } else {
+ enqueue_event(s, EV_REL, REL_X, dx);
+ enqueue_event(s, EV_REL, REL_Y, dy);
+ }
+ enqueue_event(s, EV_SYN, 0, 0);
+}
+
+static void events_put_generic(void* opaque, int type, int code, int value)
+{
+ events_state *s = (events_state *) opaque;
+
+ enqueue_event(s, type, code, value);
+}
+
+static int events_set_bits(events_state *s, int type, int bitl, int bith)
+{
+ uint8_t *bits;
+ uint8_t maskl, maskh;
+ int il, ih;
+ il = bitl / 8;
+ ih = bith / 8;
+ if (ih >= s->ev_bits[type].len) {
+ bits = qemu_mallocz(ih + 1);
+ if (bits == NULL)
+ return -ENOMEM;
+ memcpy(bits, s->ev_bits[type].bits, s->ev_bits[type].len);
+ qemu_free(s->ev_bits[type].bits);
+ s->ev_bits[type].bits = bits;
+ s->ev_bits[type].len = ih + 1;
+ }
+ else
+ bits = s->ev_bits[type].bits;
+ maskl = 0xffU << (bitl & 7);
+ maskh = 0xffU >> (7 - (bith & 7));
+ if (il >= ih)
+ maskh &= maskl;
+ else {
+ bits[il] |= maskl;
+ while (++il < ih)
+ bits[il] = 0xff;
+ }
+ bits[ih] |= maskh;
+ return 0;
+}
+
+#if 0
+static int events_set_abs_info(events_state *s, int axis, int32_t min, int32_t max, int32_t fuzz, int32_t flat)
+{
+ int32_t *info;
+ if (axis * 4 >= s->abs_info_count) {
+ info = qemu_mallocz((axis + 1) * 4 * sizeof(int32_t));
+ if (info == NULL)
+ return -ENOMEM;
+ memcpy(info, s->abs_info, s->abs_info_count);
+ qemu_free(s->abs_info);
+ s->abs_info = info;
+ s->abs_info_count = (axis + 1) * 4;
+ }
+ else
+ info = s->abs_info;
+ info += axis * 4;
+ *info++ = min;
+ *info++ = max;
+ *info++ = fuzz;
+ *info++ = flat;
+}
+#endif
+
+void events_dev_init(uint32_t base, qemu_irq irq)
+{
+ events_state *s;
+ int iomemtype;
+
+ s = (events_state *) qemu_mallocz(sizeof(events_state));
+ s->name = android_skin_keycharmap;
+ events_set_bits(s, EV_SYN, EV_SYN, EV_ABS);
+ events_set_bits(s, EV_SYN, EV_SW, EV_SW);
+ events_set_bits(s, EV_KEY, 1, 0x1ff);
+ events_set_bits(s, EV_REL, REL_X, REL_Y);
+ events_set_bits(s, EV_ABS, ABS_X, ABS_Z);
+ events_set_bits(s, EV_SW, 0, 0);
+ iomemtype = cpu_register_io_memory(0, events_readfn, events_writefn, s);
+
+ cpu_register_physical_memory(base, 0xfff, iomemtype);
+
+ qemu_add_kbd_event_handler(events_put_keycode, s);
+ qemu_add_mouse_event_handler(events_put_mouse, s, 1);
+ qemu_add_generic_event_handler(events_put_generic, s);
+
+ s->base = base;
+ s->irq = irq;
+
+ s->first = 0;
+ s->last = 0;
+
+ register_savevm( "events_state", 0, EVENTS_STATE_SAVE_VERSION,
+ events_state_save, events_state_load, s );
+}
+
diff --git a/hw/goldfish_fb.c b/hw/goldfish_fb.c
new file mode 100644
index 0000000..71cede2
--- /dev/null
+++ b/hw/goldfish_fb.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "android/android.h"
+#include "goldfish_device.h"
+#include "framebuffer.h"
+
+enum {
+ FB_GET_WIDTH = 0x00,
+ FB_GET_HEIGHT = 0x04,
+ FB_INT_STATUS = 0x08,
+ FB_INT_ENABLE = 0x0c,
+ FB_SET_BASE = 0x10,
+ FB_SET_ROTATION = 0x14,
+ FB_SET_BLANK = 0x18,
+ FB_GET_PHYS_WIDTH = 0x1c,
+ FB_GET_PHYS_HEIGHT = 0x20,
+
+ FB_INT_VSYNC = 1U << 0,
+ FB_INT_BASE_UPDATE_DONE = 1U << 1
+};
+
+struct goldfish_fb_state {
+ struct goldfish_device dev;
+ QFrameBuffer* qfbuff;
+ uint32_t fb_base;
+ uint32_t base_valid : 1;
+ uint32_t need_update : 1;
+ uint32_t need_int : 1;
+ uint32_t set_rotation : 2;
+ uint32_t blank : 1;
+ uint32_t int_status;
+ uint32_t int_enable;
+ int rotation; /* 0, 1, 2 or 3 */
+};
+
+#define GOLDFISH_FB_SAVE_VERSION 1
+
+static void goldfish_fb_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_fb_state* s = opaque;
+
+ QFrameBuffer* q = s->qfbuff;
+
+ qemu_put_be32(f, q->width);
+ qemu_put_be32(f, q->height);
+ qemu_put_be32(f, q->pitch);
+ qemu_put_byte(f, q->rotation);
+
+ qemu_put_be32(f, s->fb_base);
+ qemu_put_byte(f, s->base_valid);
+ qemu_put_byte(f, s->need_update);
+ qemu_put_byte(f, s->need_int);
+ qemu_put_byte(f, s->set_rotation);
+ qemu_put_byte(f, s->blank);
+ qemu_put_be32(f, s->int_status);
+ qemu_put_be32(f, s->int_enable);
+ qemu_put_be32(f, s->rotation);
+}
+
+static int goldfish_fb_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_fb_state* s = opaque;
+
+ QFrameBuffer* q = s->qfbuff;
+ int ret = -1;
+ int ds_w, ds_h, ds_pitch, ds_rot;
+
+ if (version_id != GOLDFISH_FB_SAVE_VERSION)
+ goto Exit;
+
+ ds_w = qemu_get_be32(f);
+ ds_h = qemu_get_be32(f);
+ ds_pitch = qemu_get_be32(f);
+ ds_rot = qemu_get_byte(f);
+
+ if (q->width != ds_w ||
+ q->height != ds_h ||
+ q->pitch != ds_pitch ||
+ q->rotation != ds_rot )
+ {
+ /* XXX: We should be able to force a resize/rotation from here ? */
+ fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
+ goto Exit;
+ }
+
+ s->fb_base = qemu_get_be32(f);
+ s->base_valid = qemu_get_byte(f);
+ s->need_update = qemu_get_byte(f);
+ s->need_int = qemu_get_byte(f);
+ s->set_rotation = qemu_get_byte(f);
+ s->blank = qemu_get_byte(f);
+ s->int_status = qemu_get_be32(f);
+ s->int_enable = qemu_get_be32(f);
+ s->rotation = qemu_get_be32(f);
+
+ /* force a refresh */
+ s->need_update = 1;
+
+ ret = 0;
+Exit:
+ return ret;
+}
+
+
+#define STATS 0
+
+#if STATS
+static int stats_counter;
+static long stats_total;
+static int stats_full_updates;
+static long stats_total_full_updates;
+#endif
+
+static void goldfish_fb_update_display(void *opaque)
+{
+ struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+ uint32_t addr;
+ uint32_t base;
+
+ uint8_t* dst_line;
+ uint8_t* src_line;
+ int y_first, y_last = 0;
+ int full_update = 0;
+ int width, height, pitch;
+
+ base = s->fb_base;
+ if(base == 0)
+ return;
+
+ if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
+ s->int_status |= FB_INT_VSYNC;
+ goldfish_device_set_irq(&s->dev, 0, 1);
+ }
+
+ y_first = -1;
+ addr = base;
+ if(s->need_update) {
+ full_update = 1;
+ if(s->need_int) {
+ s->int_status |= FB_INT_BASE_UPDATE_DONE;
+ if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
+ goldfish_device_set_irq(&s->dev, 0, 1);
+ }
+ s->need_int = 0;
+ s->need_update = 0;
+ }
+
+ src_line = phys_ram_base + base;
+ dst_line = s->qfbuff->pixels;
+ pitch = s->qfbuff->pitch;
+ width = s->qfbuff->width;
+ height = s->qfbuff->height;
+
+#if STATS
+ if (full_update)
+ stats_full_updates += 1;
+ if (++stats_counter == 120) {
+ stats_total += stats_counter;
+ stats_total_full_updates += stats_full_updates;
+
+ printf( "full update stats: peak %.2f %% total %.2f %%\n",
+ stats_full_updates*100.0/stats_counter,
+ stats_total_full_updates*100.0/stats_total );
+
+ stats_counter = 0;
+ stats_full_updates = 0;
+ }
+#endif /* STATS */
+
+ if (s->blank)
+ {
+ memset( dst_line, 0, height*pitch );
+ y_first = 0;
+ y_last = height-1;
+ }
+ else if (full_update)
+ {
+ int yy;
+
+ for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
+ {
+ uint16_t* src = (uint16_t*) src_line;
+ uint16_t* dst = (uint16_t*) dst_line;
+ int nn;
+
+ for (nn = 0; nn < width; nn++) {
+ unsigned spix = src[nn];
+ unsigned dpix = dst[nn];
+#if WORDS_BIGENDIAN
+ spix = ((spix << 8) | (spix >> 8)) & 0xffff;
+#else
+ if (spix != dpix)
+ break;
+#endif
+ }
+
+ if (nn == width)
+ continue;
+
+#if WORDS_BIGENDIAN
+ for ( ; nn < width; nn++ ) {
+ unsigned spix = src[nn];
+ dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
+ }
+#else
+ memcpy( dst+nn, src+nn, (width-nn)*2 );
+#endif
+
+ y_first = (y_first < 0) ? yy : y_first;
+ y_last = yy;
+ }
+ }
+ else /* not a full update, should not happen very often with Android */
+ {
+ int yy;
+
+ for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
+ {
+ uint16_t* src = (uint16_t*) src_line;
+ uint16_t* dst = (uint16_t*) dst_line;
+ int len = width*2;
+#if WORDS_BIGENDIAN
+ int nn;
+#endif
+ int dirty = 0;
+
+ while (len > 0) {
+ int len2 = TARGET_PAGE_SIZE - (addr & (TARGET_PAGE_SIZE-1));
+
+ if (len2 > len)
+ len2 = len;
+
+ dirty |= cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
+ addr += len2;
+ len -= len2;
+ }
+
+ if (!dirty)
+ continue;
+
+#if WORDS_BIGENDIAN
+ for (nn = 0; nn < width; nn++ ) {
+ unsigned spix = src[nn];
+ dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
+ }
+#else
+ memcpy( dst, src, width*2 );
+#endif
+
+ y_first = (y_first < 0) ? yy : y_first;
+ y_last = yy;
+ }
+ }
+
+ if (y_first < 0)
+ return;
+
+ y_last += 1;
+ //printf("goldfish_fb_update_display %d %d, base %x\n", first, last, base);
+
+ cpu_physical_memory_reset_dirty(base + y_first * width * 2,
+ base + y_last * width * 2,
+ VGA_DIRTY_FLAG);
+
+ qframebuffer_update( s->qfbuff, 0, y_first, width, y_last-y_first );
+}
+
+static void goldfish_fb_invalidate_display(void * opaque)
+{
+ // is this called?
+ struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+ s->need_update = 1;
+}
+
+static void goldfish_fb_detach_display(void* opaque)
+{
+ struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+ s->qfbuff = NULL;
+}
+
+static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t ret;
+ struct goldfish_fb_state *s = opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case FB_GET_WIDTH:
+ ret = s->qfbuff->width;
+ //printf("FB_GET_WIDTH => %d\n", ret);
+ return ret;
+
+ case FB_GET_HEIGHT:
+ ret = s->qfbuff->height;
+ //printf( "FB_GET_HEIGHT = %d\n", ret);
+ return ret;
+
+ case FB_INT_STATUS:
+ ret = s->int_status & s->int_enable;
+ if(ret) {
+ s->int_status &= ~ret;
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ }
+ return ret;
+
+ case FB_GET_PHYS_WIDTH:
+ ret = s->qfbuff->phys_width_mm;
+ //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
+ return ret;
+
+ case FB_GET_PHYS_HEIGHT:
+ ret = s->qfbuff->phys_height_mm;
+ //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
+ return ret;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ struct goldfish_fb_state *s = opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case FB_INT_ENABLE:
+ s->int_enable = val;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+ case FB_SET_BASE: {
+ int need_resize = !s->base_valid;
+ s->fb_base = val;
+ s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
+ s->need_update = 1;
+ s->need_int = 1;
+ s->base_valid = 1;
+ if(s->set_rotation != s->rotation) {
+ //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
+ s->rotation = s->set_rotation;
+ need_resize = 1;
+ }
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ if (need_resize) {
+ //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
+ qframebuffer_rotate( s->qfbuff, s->rotation );
+ }
+ } break;
+ case FB_SET_ROTATION:
+ //printf( "FB_SET_ROTATION %d\n", val);
+ s->set_rotation = val;
+ break;
+ case FB_SET_BLANK:
+ s->blank = val;
+ s->need_update = 1;
+ break;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
+ goldfish_fb_read,
+ goldfish_fb_read,
+ goldfish_fb_read
+};
+
+static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
+ goldfish_fb_write,
+ goldfish_fb_write,
+ goldfish_fb_write
+};
+
+void goldfish_fb_init(DisplayState *ds, int id)
+{
+ struct goldfish_fb_state *s;
+
+ s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish_fb";
+ s->dev.id = id;
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+
+ s->qfbuff = qframebuffer_fifo_get();
+ qframebuffer_set_producer( s->qfbuff, s,
+ goldfish_fb_update_display,
+ goldfish_fb_invalidate_display,
+ goldfish_fb_detach_display );
+
+ goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
+
+ register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
+ goldfish_fb_save, goldfish_fb_load, s);
+}
+
diff --git a/hw/goldfish_interrupt.c b/hw/goldfish_interrupt.c
new file mode 100644
index 0000000..2cba649
--- /dev/null
+++ b/hw/goldfish_interrupt.c
@@ -0,0 +1,190 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+#include "irq.h"
+
+enum {
+ INTERRUPT_STATUS = 0x00, // number of pending interrupts
+ INTERRUPT_NUMBER = 0x04,
+ INTERRUPT_DISABLE_ALL = 0x08,
+ INTERRUPT_DISABLE = 0x0c,
+ INTERRUPT_ENABLE = 0x10
+};
+
+struct goldfish_int_state {
+ struct goldfish_device dev;
+ uint32_t level;
+ uint32_t pending_count;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+};
+
+#define GOLDFISH_INT_SAVE_VERSION 1
+
+#define QFIELD_STRUCT struct goldfish_int_state
+QFIELD_BEGIN(goldfish_int_fields)
+ QFIELD_INT32(level),
+ QFIELD_INT32(pending_count),
+ QFIELD_INT32(irq_enabled),
+ QFIELD_INT32(fiq_enabled),
+QFIELD_END
+
+static void goldfish_int_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_int_state* s = opaque;
+
+ qemu_put_struct(f, goldfish_int_fields, s);
+}
+
+static int goldfish_int_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_int_state* s = opaque;
+
+ if (version_id != GOLDFISH_INT_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, goldfish_int_fields, s);
+}
+
+static void goldfish_int_update(struct goldfish_int_state *s)
+{
+ uint32_t flags;
+
+ flags = (s->level & s->irq_enabled);
+ qemu_set_irq(s->parent_irq, flags != 0);
+
+ flags = (s->level & s->fiq_enabled);
+ qemu_set_irq(s->parent_fiq, flags != 0);
+}
+
+static void goldfish_int_set_irq(void *opaque, int irq, int level)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+ uint32_t mask = (1U << irq);
+
+ if(level) {
+ if(!(s->level & mask)) {
+ if(s->irq_enabled & mask)
+ s->pending_count++;
+ s->level |= mask;
+ }
+ }
+ else {
+ if(s->level & mask) {
+ if(s->irq_enabled & mask)
+ s->pending_count--;
+ s->level &= ~mask;
+ }
+ }
+ goldfish_int_update(s);
+}
+
+static uint32_t goldfish_int_read(void *opaque, target_phys_addr_t offset)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+ offset -= s->dev.base;
+
+ switch (offset) {
+ case INTERRUPT_STATUS: /* IRQ_STATUS */
+ return s->pending_count;
+ case INTERRUPT_NUMBER: {
+ int i;
+ uint32_t pending = s->level & s->irq_enabled;
+ for(i = 0; i < 32; i++) {
+ if(pending & (1U << i))
+ return i;
+ }
+ return 0;
+ }
+ default:
+ cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+ uint32_t mask = (1U << value);
+ offset -= s->dev.base;
+
+ switch (offset) {
+ case INTERRUPT_DISABLE_ALL:
+ s->pending_count = 0;
+ s->level = 0;
+ break;
+
+ case INTERRUPT_DISABLE:
+ if(s->irq_enabled & mask) {
+ if(s->level & mask)
+ s->pending_count--;
+ s->irq_enabled &= ~mask;
+ }
+ break;
+ case INTERRUPT_ENABLE:
+ if(!(s->irq_enabled & mask)) {
+ s->irq_enabled |= mask;
+ if(s->level & mask)
+ s->pending_count++;
+ }
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset);
+ return;
+ }
+ goldfish_int_update(s);
+}
+
+static CPUReadMemoryFunc *goldfish_int_readfn[] = {
+ goldfish_int_read,
+ goldfish_int_read,
+ goldfish_int_read
+};
+
+static CPUWriteMemoryFunc *goldfish_int_writefn[] = {
+ goldfish_int_write,
+ goldfish_int_write,
+ goldfish_int_write
+};
+
+qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
+{
+ int ret;
+ struct goldfish_int_state *s;
+ qemu_irq* qi;
+
+ s = qemu_mallocz(sizeof(*s));
+ qi = qemu_allocate_irqs(goldfish_int_set_irq, s, 32);
+ s->dev.name = "goldfish_interrupt_controller";
+ s->dev.id = -1;
+ s->dev.base = base;
+ s->dev.size = 0x1000;
+ s->parent_irq = parent_irq;
+ s->parent_fiq = parent_fiq;
+
+ ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
+ if(ret) {
+ qemu_free(s);
+ return NULL;
+ }
+
+ register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION,
+ goldfish_int_save, goldfish_int_load, s);
+
+ return qi;
+}
+
diff --git a/hw/goldfish_memlog.c b/hw/goldfish_memlog.c
new file mode 100644
index 0000000..98fcffc
--- /dev/null
+++ b/hw/goldfish_memlog.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "audio/audio.h"
+
+extern void dprint(const char* fmt, ...);
+
+int fd = -1;
+
+static uint32_t memlog_read(void *opaque, target_phys_addr_t offset)
+{
+ struct goldfish_device *dev = opaque;
+ offset -= dev->base;
+
+ return 0;
+}
+
+unsigned info[8];
+
+static void memlog_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ char buf[128];
+ struct goldfish_device *dev = opaque;
+ offset -= dev->base;
+
+ info[offset / 4] = val;
+
+ if (offset == 0) {
+ /* write PID and VADDR to logfile */
+ sprintf(buf,"%08x %08x\n", info[0], info[1]);
+ write(fd, buf, strlen(buf));
+ }
+}
+
+
+static CPUReadMemoryFunc *memlog_readfn[] = {
+ memlog_read,
+ memlog_read,
+ memlog_read
+};
+
+static CPUWriteMemoryFunc *memlog_writefn[] = {
+ memlog_write,
+ memlog_write,
+ memlog_write
+};
+
+struct goldfish_device memlog_dev;
+
+void goldfish_memlog_init(uint32_t base)
+{
+ struct goldfish_device *dev = &memlog_dev;
+
+ dev->name = "goldfish_memlog";
+ dev->id = 0;
+ dev->base = base;
+ dev->size = 0x1000;
+ dev->irq_count = 0;
+
+ fd = open("mem.log", /* O_CREAT | */ O_TRUNC | O_WRONLY, 0644);
+
+ goldfish_device_add(dev, memlog_readfn, memlog_writefn, dev);
+}
+
diff --git a/hw/goldfish_mmc.c b/hw/goldfish_mmc.c
new file mode 100644
index 0000000..272f403
--- /dev/null
+++ b/hw/goldfish_mmc.c
@@ -0,0 +1,468 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "mmc.h"
+#include "sd.h"
+#include "block.h"
+
+enum {
+ /* status register */
+ MMC_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ MMC_INT_ENABLE = 0x04,
+ /* set this to specify buffer address */
+ MMC_SET_BUFFER = 0x08,
+
+ /* MMC command number */
+ MMC_CMD = 0x0C,
+
+ /* MMC argument */
+ MMC_ARG = 0x10,
+
+ /* MMC response (or R2 bits 0 - 31) */
+ MMC_RESP_0 = 0x14,
+
+ /* MMC R2 response bits 32 - 63 */
+ MMC_RESP_1 = 0x18,
+
+ /* MMC R2 response bits 64 - 95 */
+ MMC_RESP_2 = 0x1C,
+
+ /* MMC R2 response bits 96 - 127 */
+ MMC_RESP_3 = 0x20,
+
+ MMC_BLOCK_LENGTH = 0x24,
+ MMC_BLOCK_COUNT = 0x28,
+
+ /* MMC state flags */
+ MMC_STATE = 0x2C,
+
+ /* MMC_INT_STATUS bits */
+
+ MMC_STAT_END_OF_CMD = 1U << 0,
+ MMC_STAT_END_OF_DATA = 1U << 1,
+ MMC_STAT_STATE_CHANGE = 1U << 2,
+
+ /* MMC_STATE bits */
+ MMC_STATE_INSERTED = 1U << 0,
+ MMC_STATE_READ_ONLY = 1U << 1,
+};
+
+
+struct goldfish_mmc_state {
+ struct goldfish_device dev;
+ BlockDriverState *bs;
+ // pointer to our buffer
+ uint8_t* buffer;
+ // offsets for read and write operations
+ uint32_t read_offset, write_offset;
+ // buffer status flags
+ uint32_t int_status;
+ // irq enable mask for int_status
+ uint32_t int_enable;
+
+ // MMC command argument
+ uint32_t arg;
+ uint32_t resp[4];
+
+ uint32_t block_length;
+ uint32_t block_count;
+ int is_SDHC;
+};
+
+#define GOLDFISH_MMC_SAVE_VERSION 1
+#define QFIELD_STRUCT struct goldfish_mmc_state
+QFIELD_BEGIN(goldfish_mmc_fields)
+ QFIELD_INT32(read_offset),
+ QFIELD_INT32(write_offset),
+ QFIELD_INT32(int_status),
+ QFIELD_INT32(int_enable),
+ QFIELD_INT32(arg),
+ QFIELD_INT32(resp[0]),
+ QFIELD_INT32(resp[1]),
+ QFIELD_INT32(resp[2]),
+ QFIELD_INT32(resp[3]),
+ QFIELD_INT32(block_length),
+ QFIELD_INT32(block_count),
+ QFIELD_INT32(is_SDHC),
+QFIELD_END
+
+static void goldfish_mmc_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_mmc_state* s = opaque;
+
+ qemu_put_be32(f, s->buffer - phys_ram_base);
+ qemu_put_struct(f, goldfish_mmc_fields, s);
+}
+
+static int goldfish_mmc_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_mmc_state* s = opaque;
+
+ if (version_id != GOLDFISH_MMC_SAVE_VERSION)
+ return -1;
+
+ s->buffer = qemu_get_be32(f) + phys_ram_base;
+ return qemu_get_struct(f, goldfish_mmc_fields, s);
+}
+
+struct mmc_opcode {
+ const char* name;
+ int cmd;
+} mmc_opcodes[] = {
+ { "MMC_GO_IDLE_STATE", 0 },
+ { "MMC_SEND_OP_COND", 1 },
+ { "MMC_ALL_SEND_CID", 2 },
+ { "MMC_SET_RELATIVE_ADDR", 3 },
+ { "MMC_SET_DSR", 4 },
+ { "MMC_SWITCH", 6 },
+ { "MMC_SELECT_CARD", 7 },
+ { "MMC_SEND_EXT_CSD", 8 },
+ { "MMC_SEND_CSD", 9 },
+ { "MMC_SEND_CID", 10 },
+ { "MMC_READ_DAT_UNTIL_STOP", 11 },
+ { "MMC_STOP_TRANSMISSION", 12 },
+ { "MMC_SEND_STATUS", 13 },
+ { "MMC_GO_INACTIVE_STATE", 15 },
+ { "MMC_SET_BLOCKLEN", 16 },
+ { "MMC_READ_SINGLE_BLOCK", 17 },
+ { "MMC_READ_MULTIPLE_BLOCK", 18 },
+ { "MMC_WRITE_DAT_UNTIL_STOP", 20 },
+ { "MMC_SET_BLOCK_COUNT", 23 },
+ { "MMC_WRITE_BLOCK", 24 },
+ { "MMC_WRITE_MULTIPLE_BLOCK", 25 },
+ { "MMC_PROGRAM_CID", 26 },
+ { "MMC_PROGRAM_CSD", 27 },
+ { "MMC_SET_WRITE_PROT", 28 },
+ { "MMC_CLR_WRITE_PROT", 29 },
+ { "MMC_SEND_WRITE_PROT", 30 },
+ { "MMC_ERASE_GROUP_START", 35 },
+ { "MMC_ERASE_GROUP_END", 36 },
+ { "MMC_ERASE", 38 },
+ { "MMC_FAST_IO", 39 },
+ { "MMC_GO_IRQ_STATE", 40 },
+ { "MMC_LOCK_UNLOCK", 42 },
+ { "MMC_APP_CMD", 55 },
+ { "MMC_GEN_CMD", 56 },
+ { "SD_APP_OP_COND", 41 },
+ { "SD_APP_SEND_SCR", 51 },
+ { "UNKNOWN", -1 }
+};
+
+#if 0
+static const char* get_command_name(int command)
+{
+ struct mmc_opcode* opcode = mmc_opcodes;
+
+ while (opcode->cmd != command && opcode->cmd != -1) opcode++;
+ return opcode->name;
+}
+#endif
+
+static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd, uint32_t arg)
+{
+ int result;
+ int new_status = MMC_STAT_END_OF_CMD;
+ int opcode = cmd & 63;
+
+// fprintf(stderr, "goldfish_mmc_do_command opcode: %s (0x%04X), arg: %d\n", get_command_name(opcode), cmd, arg);
+
+ s->resp[0] = 0;
+ s->resp[1] = 0;
+ s->resp[2] = 0;
+ s->resp[3] = 0;
+
+#define SET_R1_CURRENT_STATE(s) ((s << 9) & 0x00001E00) /* sx, b (4 bits) */
+
+ switch (opcode) {
+ case MMC_SEND_CSD: {
+ int64_t sector_count = 0;
+ uint64_t capacity;
+ uint8_t exponent;
+ uint32_t m;
+
+ bdrv_get_geometry(s->bs, (uint64_t*)&sector_count);
+ capacity = sector_count * 512;
+ if (capacity > 2147483648U) {
+ // if storages is > 2 gig, then emulate SDHC card
+ s->is_SDHC = 1;
+
+ // CSD bits borrowed from a real SDHC card, with capacity bits zeroed out
+ s->resp[3] = 0x400E0032;
+ s->resp[2] = 0x5B590000;
+ s->resp[1] = 0x00007F80;
+ s->resp[0] = 0x0A4040DF;
+
+ // stuff in the real capacity
+ // m = UNSTUFF_BITS(resp, 48, 22);
+ m = (uint32_t)(capacity / (512*1024)) - 1;
+ // m must fit into 22 bits
+ if (m & 0xFFC00000) {
+ fprintf(stderr, "SD card too big (%lld bytes). Maximum SDHC card size is 128 gigabytes.\n", capacity);
+ abort();
+ }
+
+ // low 16 bits go in high end of resp[1]
+ s->resp[1] |= ((m & 0x0000FFFF) << 16);
+ // high 6 bits go in low end of resp[2]
+ s->resp[2] |= (m >> 16);
+ } else {
+ // emulate standard SD card
+ s->is_SDHC = 0;
+
+ // CSD bits borrowed from a real SD card, with capacity bits zeroed out
+ s->resp[3] = 0x00260032;
+ s->resp[2] = 0x5F5A8000;
+ s->resp[1] = 0x3EF84FFF;
+ s->resp[0] = 0x928040CB;
+
+ // stuff in the real capacity
+ // e = UNSTUFF_BITS(resp, 47, 3);
+ // m = UNSTUFF_BITS(resp, 62, 12);
+ // csd->capacity = (1 + m) << (e + 2);
+ // need to reverse the formula and calculate e and m
+ exponent = 0;
+ capacity = sector_count * 512;
+ if (capacity > 2147483648U) {
+ fprintf(stderr, "SD card too big (%lld bytes). Maximum SD card size is 2 gigabytes.\n", capacity);
+ abort();
+ }
+ capacity >>= 10; // convert to Kbytes
+ while (capacity > 4096) {
+ // (capacity - 1) must fit into 12 bits
+ exponent++;
+ capacity >>= 1;
+ }
+ capacity -= 1;
+ exponent -= 2;
+ if (exponent > 7)
+ cpu_abort(cpu_single_env, "exponent %d too big\n", exponent);
+
+ s->resp[2] |= (((uint32_t)capacity >> 2) & 0x3FF); // high 10 bits to bottom of resp[2]
+ s->resp[1] |= (((uint32_t)capacity & 3) << 30); // low 2 bits to top of resp[1]
+ s->resp[1] |= (exponent << (47 - 32));
+ }
+ break;
+ }
+
+ case MMC_SEND_EXT_CSD:
+ s->resp[0] = arg;
+ break;
+
+ case MMC_APP_CMD:
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+ break;
+
+ case SD_APP_OP_COND:
+ s->resp[0] = 0x80FF8000;
+ break;
+
+ case SD_APP_SEND_SCR:
+ {
+ uint32_t* scr = (uint32_t*)s->buffer;
+ scr[0] = 0x00002502;
+ scr[1] = 0x00000000;
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+ new_status |= MMC_STAT_END_OF_DATA;
+ break;
+ }
+ case MMC_SET_RELATIVE_ADDR:
+ s->resp[0] = -518519520;
+ break;
+
+ case MMC_ALL_SEND_CID:
+ s->resp[3] = 55788627;
+ s->resp[2] = 1429221959;
+ s->resp[1] = -2147479692;
+ s->resp[0] = -436179883;
+ break;
+
+ case MMC_SELECT_CARD:
+ s->resp[0] = SET_R1_CURRENT_STATE(3) | R1_READY_FOR_DATA; // 1792
+ break;
+
+ case MMC_SWITCH:
+ if (arg == 0x00FFFFF1 || arg == 0x80FFFFF1) {
+ uint8_t* switchbuf = s->buffer;
+ memset(switchbuf, 0, 64);
+ switchbuf[13] = 2;
+ new_status |= MMC_STAT_END_OF_DATA;
+ }
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+ break;
+
+ case MMC_SET_BLOCKLEN:
+ s->block_length = arg;
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+ break;
+
+ case MMC_READ_SINGLE_BLOCK:
+ s->block_count = 1;
+ // fall through
+ case MMC_READ_MULTIPLE_BLOCK: {
+ if (s->is_SDHC) {
+ // arg is block offset
+ } else {
+ // arg is byte offset
+ if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when reading\n", arg);
+ arg /= s->block_length;
+ }
+ result = bdrv_read(s->bs, arg, s->buffer, s->block_count);
+ new_status |= MMC_STAT_END_OF_DATA;
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+ break;
+ }
+
+ case MMC_WRITE_BLOCK:
+ s->block_count = 1;
+ // fall through
+ case MMC_WRITE_MULTIPLE_BLOCK: {
+ if (s->is_SDHC) {
+ // arg is block offset
+ } else {
+ // arg is byte offset
+ if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when writing\n", arg);
+ arg /= s->block_length;
+ }
+ // arg is byte offset
+ result = bdrv_write(s->bs, arg, s->buffer, s->block_count);
+// bdrv_flush(s->bs);
+ new_status |= MMC_STAT_END_OF_DATA;
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+ break;
+ }
+
+ case MMC_STOP_TRANSMISSION:
+ s->resp[0] = SET_R1_CURRENT_STATE(5) | R1_READY_FOR_DATA; // 2816
+ break;
+
+ case MMC_SEND_STATUS:
+ s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+ break;
+ }
+
+ s->int_status |= new_status;
+
+ if ((s->int_status & s->int_enable)) {
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ }
+}
+
+static uint32_t goldfish_mmc_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t ret;
+ struct goldfish_mmc_state *s = opaque;
+
+ offset -= s->dev.base;
+ switch(offset) {
+ case MMC_INT_STATUS:
+ // return current buffer status flags
+ return s->int_status & s->int_enable;
+ case MMC_RESP_0:
+ return s->resp[0];
+ case MMC_RESP_1:
+ return s->resp[1];
+ case MMC_RESP_2:
+ return s->resp[2];
+ case MMC_RESP_3:
+ return s->resp[3];
+ case MMC_STATE: {
+ ret = MMC_STATE_INSERTED;
+ if (bdrv_is_read_only(s->bs)) {
+ ret |= MMC_STATE_READ_ONLY;
+ }
+ return ret;
+ }
+ default:
+ cpu_abort(cpu_single_env, "goldfish_mmc_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_mmc_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ struct goldfish_mmc_state *s = opaque;
+ int status, old_status;
+
+ offset -= s->dev.base;
+
+ switch(offset) {
+
+ case MMC_INT_STATUS:
+ status = s->int_status;
+ old_status = status;
+ status &= ~val;
+ s->int_status = status;
+ if(status != old_status) {
+ goldfish_device_set_irq(&s->dev, 0, status);
+ }
+ break;
+
+ case MMC_INT_ENABLE:
+ /* enable buffer interrupts */
+ s->int_enable = val;
+ s->int_status = 0;
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+ case MMC_SET_BUFFER:
+ /* save pointer to buffer 1 */
+ s->buffer = phys_ram_base + val;
+ break;
+ case MMC_CMD:
+ goldfish_mmc_do_command(s, val, s->arg);
+ break;
+ case MMC_ARG:
+ s->arg = val;
+ break;
+ case MMC_BLOCK_LENGTH:
+ s->block_length = val + 1;
+ break;
+ case MMC_BLOCK_COUNT:
+ s->block_count = val + 1;
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_mmc_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_mmc_readfn[] = {
+ goldfish_mmc_read,
+ goldfish_mmc_read,
+ goldfish_mmc_read
+};
+
+static CPUWriteMemoryFunc *goldfish_mmc_writefn[] = {
+ goldfish_mmc_write,
+ goldfish_mmc_write,
+ goldfish_mmc_write
+};
+
+void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs)
+{
+ struct goldfish_mmc_state *s;
+
+ s = (struct goldfish_mmc_state *)qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish_mmc";
+ s->dev.id = id;
+ s->dev.base = base;
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+ s->bs = bs;
+
+ goldfish_device_add(&s->dev, goldfish_mmc_readfn, goldfish_mmc_writefn, s);
+
+ register_savevm( "goldfish_mmc", 0, GOLDFISH_MMC_SAVE_VERSION,
+ goldfish_mmc_save, goldfish_mmc_load, s);
+}
+
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
new file mode 100644
index 0000000..61b075e
--- /dev/null
+++ b/hw/goldfish_nand.c
@@ -0,0 +1,636 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "goldfish_nand_reg.h"
+#include "goldfish_nand.h"
+#include "android/utils/tempfile.h"
+#include "qemu_debug.h"
+#include "android/android.h"
+
+#define DEBUG 1
+#if DEBUG
+# define D(...) VERBOSE_PRINT(nand,__VA_ARGS__)
+# define D_ACTIVE VERBOSE_CHECK(nand)
+# define T(...) VERBOSE_PRINT(nand_limits,__VA_ARGS__)
+# define T_ACTIVE VERBOSE_CHECK(nand_limits)
+#else
+# define D(...) ((void)0)
+# define D_ACTIVE 0
+# define T(...) ((void)0)
+# define T_ACTIVE 0
+#endif
+
+/* lseek uses 64-bit offsets on Darwin. */
+/* prefer lseek64 on Linux */
+#ifdef __APPLE__
+# define llseek lseek
+#elif defined(__linux__)
+# define llseek lseek64
+#endif
+
+#define XLOG xlog
+
+static void
+xlog( const char* format, ... )
+{
+ va_list args;
+ va_start(args, format);
+ fprintf(stderr, "NAND: ");
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+typedef struct {
+ char* devname;
+ size_t devname_len;
+ char* data;
+ int fd;
+ uint32_t flags;
+ uint32_t page_size;
+ uint32_t extra_size;
+ uint32_t erase_size;
+ uint64_t size;
+} nand_dev;
+
+nand_threshold android_nand_write_threshold;
+nand_threshold android_nand_read_threshold;
+
+#ifdef CONFIG_NAND_THRESHOLD
+
+/* update a threshold, return 1 if limit is hit, 0 otherwise */
+static void
+nand_threshold_update( nand_threshold* t, uint32_t len )
+{
+ if (t->counter < t->limit) {
+ uint64_t avail = t->limit - t->counter;
+ if (avail > len)
+ avail = len;
+
+ if (t->counter == 0) {
+ T("%s: starting threshold counting to %lld",
+ __FUNCTION__, t->limit);
+ }
+ t->counter += avail;
+ if (t->counter >= t->limit) {
+ /* threshold reach, send a signal to an external process */
+ T( "%s: sending signal %d to pid %d !",
+ __FUNCTION__, t->signal, t->pid );
+
+ kill( t->pid, t->signal );
+ }
+ }
+ return;
+}
+
+#define NAND_UPDATE_READ_THRESHOLD(len) \
+ nand_threshold_update( &android_nand_read_threshold, (uint32_t)(len) )
+
+#define NAND_UPDATE_WRITE_THRESHOLD(len) \
+ nand_threshold_update( &android_nand_write_threshold, (uint32_t)(len) )
+
+#else /* !NAND_THRESHOLD */
+
+#define NAND_UPDATE_READ_THRESHOLD(len) \
+ do {} while (0)
+
+#define NAND_UPDATE_WRITE_THRESHOLD(len) \
+ do {} while (0)
+
+#endif /* !NAND_THRESHOLD */
+
+static nand_dev *nand_devs = NULL;
+static uint32_t nand_dev_count = 0;
+
+typedef struct {
+ uint32_t base;
+
+ // register state
+ uint32_t dev;
+ uint32_t addr_low;
+ uint32_t addr_high;
+ uint32_t transfer_size;
+ uint32_t data;
+ uint32_t result;
+} nand_dev_state;
+
+/* update this everytime you change the nand_dev_state structure */
+#define NAND_DEV_STATE_SAVE_VERSION 1
+
+#define QFIELD_STRUCT nand_dev_state
+QFIELD_BEGIN(nand_dev_state_fields)
+ QFIELD_INT32(dev),
+ QFIELD_INT32(addr_low),
+ QFIELD_INT32(addr_high),
+ QFIELD_INT32(transfer_size),
+ QFIELD_INT32(data),
+ QFIELD_INT32(result),
+QFIELD_END
+
+static void nand_dev_state_save(QEMUFile* f, void* opaque)
+{
+ nand_dev_state* s = opaque;
+
+ qemu_put_struct(f, nand_dev_state_fields, s);
+}
+
+static int nand_dev_state_load(QEMUFile* f, void* opaque, int version_id)
+{
+ nand_dev_state* s = opaque;
+
+ if (version_id != NAND_DEV_STATE_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, nand_dev_state_fields, s);
+}
+
+
+static int do_read(int fd, void* buf, size_t size)
+{
+ int ret;
+ do {
+ ret = read(fd, buf, size);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static int do_write(int fd, const void* buf, size_t size)
+{
+ int ret;
+ do {
+ ret = write(fd, buf, size);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
+{
+ uint32_t len = total_len;
+ size_t read_len = dev->erase_size;
+ int eof = 0;
+
+ NAND_UPDATE_READ_THRESHOLD(total_len);
+
+ lseek(dev->fd, addr, SEEK_SET);
+ while(len > 0) {
+ if(read_len < dev->erase_size) {
+ memset(dev->data, 0xff, dev->erase_size);
+ read_len = dev->erase_size;
+ eof = 1;
+ }
+ if(len < read_len)
+ read_len = len;
+ if(!eof) {
+ read_len = do_read(dev->fd, dev->data, read_len);
+ }
+ pmemcpy(data, dev->data, read_len);
+ data += read_len;
+ len -= read_len;
+ }
+ return total_len;
+}
+
+static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
+{
+ uint32_t len = total_len;
+ size_t write_len = dev->erase_size;
+ int ret;
+
+ NAND_UPDATE_WRITE_THRESHOLD(total_len);
+
+ lseek(dev->fd, addr, SEEK_SET);
+ while(len > 0) {
+ if(len < write_len)
+ write_len = len;
+ vmemcpy(data, dev->data, write_len);
+ ret = do_write(dev->fd, dev->data, write_len);
+ if(ret < write_len) {
+ XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno));
+ break;
+ }
+ data += write_len;
+ len -= write_len;
+ }
+ return total_len - len;
+}
+
+static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total_len)
+{
+ uint32_t len = total_len;
+ size_t write_len = dev->erase_size;
+ int ret;
+
+ lseek(dev->fd, addr, SEEK_SET);
+ memset(dev->data, 0xff, dev->erase_size);
+ while(len > 0) {
+ if(len < write_len)
+ write_len = len;
+ ret = do_write(dev->fd, dev->data, write_len);
+ if(ret < write_len) {
+ XLOG( "nand_dev_write_file, write failed: %s\n", strerror(errno));
+ break;
+ }
+ len -= write_len;
+ }
+ return total_len - len;
+}
+
+/* this is a huge hack required to make the PowerPC emulator binary usable
+ * on Mac OS X. If you define this function as 'static', the emulated kernel
+ * will panic when attempting to mount the /data partition.
+ *
+ * worse, if you do *not* define the function as static on Linux-x86, the
+ * emulated kernel will also panic !?
+ *
+ * I still wonder if this is a compiler bug, or due to some nasty thing the
+ * emulator does with CPU registers during execution of the translated code.
+ */
+#if !(defined __APPLE__ && defined __powerpc__)
+static
+#endif
+uint32_t nand_dev_do_cmd(nand_dev_state *s, uint32_t cmd)
+{
+ uint32_t size;
+ uint64_t addr;
+ nand_dev *dev;
+
+ addr = s->addr_low | ((uint64_t)s->addr_high << 32);
+ size = s->transfer_size;
+ if(s->dev >= nand_dev_count)
+ return 0;
+ dev = nand_devs + s->dev;
+
+ switch(cmd) {
+ case NAND_CMD_GET_DEV_NAME:
+ if(size > dev->devname_len)
+ size = dev->devname_len;
+ pmemcpy(s->data, dev->devname, size);
+ return size;
+ case NAND_CMD_READ:
+ if(addr >= dev->size)
+ return 0;
+ if(size + addr > dev->size)
+ size = dev->size - addr;
+ if(dev->fd >= 0)
+ return nand_dev_read_file(dev, s->data, addr, size);
+ pmemcpy(s->data, &dev->data[addr], size);
+ return size;
+ case NAND_CMD_WRITE:
+ if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+ return 0;
+ if(addr >= dev->size)
+ return 0;
+ if(size + addr > dev->size)
+ size = dev->size - addr;
+ if(dev->fd >= 0)
+ return nand_dev_write_file(dev, s->data, addr, size);
+ vmemcpy(s->data, &dev->data[addr], size);
+ return size;
+ case NAND_CMD_ERASE:
+ if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+ return 0;
+ if(addr >= dev->size)
+ return 0;
+ if(size + addr > dev->size)
+ size = dev->size - addr;
+ if(dev->fd >= 0)
+ return nand_dev_erase_file(dev, addr, size);
+ memset(&dev->data[addr], 0xff, size);
+ return size;
+ case NAND_CMD_BLOCK_BAD_GET: // no bad block support
+ return 0;
+ case NAND_CMD_BLOCK_BAD_SET:
+ if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+ return 0;
+ return 0;
+ default:
+ cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad command %x\n", cmd);
+ return 0;
+ }
+}
+
+/* I/O write */
+static void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ nand_dev_state *s = (nand_dev_state *)opaque;
+
+ offset -= s->base;
+ switch (offset) {
+ case NAND_DEV:
+ s->dev = value;
+ if(s->dev >= nand_dev_count) {
+ cpu_abort(cpu_single_env, "nand_dev_write: Bad dev %x\n", value);
+ }
+ break;
+ case NAND_ADDR_HIGH:
+ s->addr_high = value;
+ break;
+ case NAND_ADDR_LOW:
+ s->addr_low = value;
+ break;
+ case NAND_TRANSFER_SIZE:
+ s->transfer_size = value;
+ break;
+ case NAND_DATA:
+ s->data = value;
+ break;
+ case NAND_COMMAND:
+ s->result = nand_dev_do_cmd(s, value);
+ break;
+ default:
+ cpu_abort(cpu_single_env, "nand_dev_write: Bad offset %x\n", offset);
+ break;
+ }
+}
+
+/* I/O read */
+static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset)
+{
+ nand_dev_state *s = (nand_dev_state *)opaque;
+ nand_dev *dev;
+
+ offset -= s->base;
+ switch (offset) {
+ case NAND_VERSION:
+ return NAND_VERSION_CURRENT;
+ case NAND_NUM_DEV:
+ return nand_dev_count;
+ case NAND_RESULT:
+ return s->result;
+ }
+
+ if(s->dev >= nand_dev_count)
+ return 0;
+
+ dev = nand_devs + s->dev;
+
+ switch (offset) {
+ case NAND_DEV_FLAGS:
+ return dev->flags;
+
+ case NAND_DEV_NAME_LEN:
+ return dev->devname_len;
+
+ case NAND_DEV_PAGE_SIZE:
+ return dev->page_size;
+
+ case NAND_DEV_EXTRA_SIZE:
+ return dev->extra_size;
+
+ case NAND_DEV_ERASE_SIZE:
+ return dev->erase_size;
+
+ case NAND_DEV_SIZE_LOW:
+ return (uint32_t)dev->size;
+
+ case NAND_DEV_SIZE_HIGH:
+ return (uint32_t)(dev->size >> 32);
+
+ default:
+ cpu_abort(cpu_single_env, "nand_dev_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static CPUReadMemoryFunc *nand_dev_readfn[] = {
+ nand_dev_read,
+ nand_dev_read,
+ nand_dev_read
+};
+
+static CPUWriteMemoryFunc *nand_dev_writefn[] = {
+ nand_dev_write,
+ nand_dev_write,
+ nand_dev_write
+};
+
+/* initialize the QFB device */
+void nand_dev_init(uint32_t base)
+{
+ int iomemtype;
+ static int instance_id = 0;
+ nand_dev_state *s;
+
+ s = (nand_dev_state *)qemu_mallocz(sizeof(nand_dev_state));
+ iomemtype = cpu_register_io_memory(0, nand_dev_readfn, nand_dev_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+
+ register_savevm( "nand_dev", instance_id++, NAND_DEV_STATE_SAVE_VERSION,
+ nand_dev_state_save, nand_dev_state_load, s);
+}
+
+static int arg_match(const char *a, const char *b, size_t b_len)
+{
+ while(*a && b_len--) {
+ if(*a++ != *b++)
+ return 0;
+ }
+ return b_len == 0;
+}
+
+void nand_add_dev(const char *arg)
+{
+ uint64_t dev_size = 0;
+ const char *next_arg;
+ const char *value;
+ size_t arg_len, value_len;
+ nand_dev *new_devs, *dev;
+ char *devname = NULL;
+ size_t devname_len = 0;
+ char *initfilename = NULL;
+ char *rwfilename = NULL;
+ int initfd = -1;
+ int rwfd = -1;
+ int read_only = 0;
+ int pad;
+ ssize_t read_size;
+ uint32_t page_size = 2048;
+ uint32_t extra_size = 64;
+ uint32_t erase_pages = 64;
+
+ while(arg) {
+ next_arg = strchr(arg, ',');
+ value = strchr(arg, '=');
+ if(next_arg != NULL) {
+ arg_len = next_arg - arg;
+ next_arg++;
+ if(value >= next_arg)
+ value = NULL;
+ }
+ else
+ arg_len = strlen(arg);
+ if(value != NULL) {
+ size_t new_arg_len = value - arg;
+ value_len = arg_len - new_arg_len - 1;
+ arg_len = new_arg_len;
+ value++;
+ }
+ else
+ value_len = 0;
+
+ if(devname == NULL) {
+ if(value != NULL)
+ goto bad_arg_and_value;
+ devname_len = arg_len;
+ devname = malloc(arg_len);
+ if(devname == NULL)
+ goto out_of_memory;
+ memcpy(devname, arg, arg_len);
+ }
+ else if(value == NULL) {
+ if(arg_match("readonly", arg, arg_len)) {
+ read_only = 1;
+ }
+ else {
+ XLOG("bad arg: %.*s\n", arg_len, arg);
+ exit(1);
+ }
+ }
+ else {
+ if(arg_match("size", arg, arg_len)) {
+ char *ep;
+ dev_size = strtoull(value, &ep, 0);
+ if(ep != value + value_len)
+ goto bad_arg_and_value;
+ }
+ else if(arg_match("pagesize", arg, arg_len)) {
+ char *ep;
+ page_size = strtoul(value, &ep, 0);
+ if(ep != value + value_len)
+ goto bad_arg_and_value;
+ }
+ else if(arg_match("extrasize", arg, arg_len)) {
+ char *ep;
+ extra_size = strtoul(value, &ep, 0);
+ if(ep != value + value_len)
+ goto bad_arg_and_value;
+ }
+ else if(arg_match("erasepages", arg, arg_len)) {
+ char *ep;
+ erase_pages = strtoul(value, &ep, 0);
+ if(ep != value + value_len)
+ goto bad_arg_and_value;
+ }
+ else if(arg_match("initfile", arg, arg_len)) {
+ initfilename = malloc(value_len + 1);
+ if(initfilename == NULL)
+ goto out_of_memory;
+ memcpy(initfilename, value, value_len);
+ initfilename[value_len] = '\0';
+ }
+ else if(arg_match("file", arg, arg_len)) {
+ rwfilename = malloc(value_len + 1);
+ if(rwfilename == NULL)
+ goto out_of_memory;
+ memcpy(rwfilename, value, value_len);
+ rwfilename[value_len] = '\0';
+ }
+ else {
+ goto bad_arg_and_value;
+ }
+ }
+
+ arg = next_arg;
+ }
+
+ if (rwfilename == NULL) {
+ /* we create a temporary file to store everything */
+ TempFile* tmp = tempfile_create();
+
+ if (tmp == NULL) {
+ XLOG("could not create temp file for %.*s NAND disk image: %s",
+ devname_len, devname, strerror(errno));
+ exit(1);
+ }
+ rwfilename = (char*) tempfile_path(tmp);
+ if (VERBOSE_CHECK(init))
+ dprint( "mapping '%.*s' NAND image to %s", devname_len, devname, rwfilename);
+ }
+
+ if(rwfilename) {
+ rwfd = open(rwfilename, O_BINARY | (read_only ? O_RDONLY : O_RDWR));
+ if(rwfd < 0 && read_only) {
+ XLOG("could not open file %s, %s\n", rwfilename, strerror(errno));
+ exit(1);
+ }
+ /* this could be a writable temporary file. use atexit_close_fd to ensure
+ * that it is properly cleaned up at exit on Win32
+ */
+ if (!read_only)
+ atexit_close_fd(rwfd);
+ }
+
+ if(initfilename) {
+ initfd = open(initfilename, O_BINARY | O_RDONLY);
+ if(initfd < 0) {
+ XLOG("could not open file %s, %s\n", initfilename, strerror(errno));
+ exit(1);
+ }
+ if(dev_size == 0) {
+ dev_size = lseek(initfd, 0, SEEK_END);
+ lseek(initfd, 0, SEEK_SET);
+ }
+ }
+
+ new_devs = realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1));
+ if(new_devs == NULL)
+ goto out_of_memory;
+ nand_devs = new_devs;
+ dev = &new_devs[nand_dev_count];
+
+ dev->page_size = page_size;
+ dev->extra_size = extra_size;
+ dev->erase_size = erase_pages * (page_size + extra_size);
+ pad = dev_size % dev->erase_size;
+ if (pad != 0) {
+ dev_size += (dev->erase_size - pad);
+ XLOG("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
+ }
+ dev->devname = devname;
+ dev->devname_len = devname_len;
+ dev->size = dev_size;
+ dev->data = malloc(dev->erase_size);
+ if(dev->data == NULL)
+ goto out_of_memory;
+ dev->flags = read_only ? NAND_DEV_FLAG_READ_ONLY : 0;
+
+ if (initfd >= 0) {
+ do {
+ read_size = do_read(initfd, dev->data, dev->erase_size);
+ if(read_size < 0) {
+ XLOG("could not read file %s, %s\n", initfilename, strerror(errno));
+ exit(1);
+ }
+ if(do_write(rwfd, dev->data, read_size) != read_size) {
+ XLOG("could not write file %s, %s\n", initfilename, strerror(errno));
+ exit(1);
+ }
+ } while(read_size == dev->erase_size);
+ close(initfd);
+ }
+ dev->fd = rwfd;
+
+ nand_dev_count++;
+
+ return;
+
+out_of_memory:
+ XLOG("out of memory\n");
+ exit(1);
+
+bad_arg_and_value:
+ XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value);
+ exit(1);
+}
+
diff --git a/hw/goldfish_nand.h b/hw/goldfish_nand.h
new file mode 100644
index 0000000..dcc59d8
--- /dev/null
+++ b/hw/goldfish_nand.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#ifndef NAND_DEVICE_H
+#define NAND_DEVICE_H
+
+void nand_dev_init(uint32_t base);
+void nand_add_dev(const char *arg);
+
+typedef struct {
+ uint64_t limit;
+ uint64_t counter;
+ int pid;
+ int signal;
+} nand_threshold;
+
+extern nand_threshold android_nand_read_threshold;
+extern nand_threshold android_nand_write_threshold;
+
+#endif
diff --git a/hw/goldfish_nand_reg.h b/hw/goldfish_nand_reg.h
new file mode 100644
index 0000000..ea91461
--- /dev/null
+++ b/hw/goldfish_nand_reg.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#ifndef NAND_DEVICE_REG_H
+#define NAND_DEVICE_REG_H
+
+enum nand_cmd {
+ NAND_CMD_GET_DEV_NAME, // Write device name for NAND_DEV to NAND_DATA (vaddr)
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+ NAND_CMD_ERASE,
+ NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
+ NAND_CMD_BLOCK_BAD_SET
+};
+
+enum nand_dev_flags {
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ // Global
+ NAND_VERSION = 0x000,
+ NAND_NUM_DEV = 0x004,
+ NAND_DEV = 0x008,
+
+ // Dev info
+ NAND_DEV_FLAGS = 0x010,
+ NAND_DEV_NAME_LEN = 0x014,
+ NAND_DEV_PAGE_SIZE = 0x018,
+ NAND_DEV_EXTRA_SIZE = 0x01c,
+ NAND_DEV_ERASE_SIZE = 0x020,
+ NAND_DEV_SIZE_LOW = 0x028,
+ NAND_DEV_SIZE_HIGH = 0x02c,
+
+ // Command
+ NAND_RESULT = 0x040,
+ NAND_COMMAND = 0x044,
+ NAND_DATA = 0x048,
+ NAND_TRANSFER_SIZE = 0x04c,
+ NAND_ADDR_LOW = 0x050,
+ NAND_ADDR_HIGH = 0x054,
+};
+
+#endif
diff --git a/hw/goldfish_switch.c b/hw/goldfish_switch.c
new file mode 100644
index 0000000..8a12d66
--- /dev/null
+++ b/hw/goldfish_switch.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+
+enum {
+ SW_NAME_LEN = 0x00,
+ SW_NAME_PTR = 0x04,
+ SW_FLAGS = 0x08,
+ SW_STATE = 0x0c,
+ SW_INT_STATUS = 0x10,
+ SW_INT_ENABLE = 0x14,
+
+ SW_FLAGS_OUTPUT = 1U << 0
+};
+
+
+struct switch_state {
+ struct goldfish_device dev;
+ char *name;
+ uint32_t state;
+ uint32_t state_changed : 1;
+ uint32_t int_enable : 1;
+ uint32_t (*writefn)(void *opaque, uint32_t state);
+ void *writeopaque;
+};
+
+#define GOLDFISH_SWITCH_SAVE_VERSION 1
+
+static void goldfish_switch_save(QEMUFile* f, void* opaque)
+{
+ struct switch_state* s = opaque;
+
+ qemu_put_be32(f, s->state);
+ qemu_put_byte(f, s->state_changed);
+ qemu_put_byte(f, s->int_enable);
+}
+
+static int goldfish_switch_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct switch_state* s = opaque;
+
+ if (version_id != GOLDFISH_SWITCH_SAVE_VERSION)
+ return -1;
+
+ s->state = qemu_get_be32(f);
+ s->state_changed = qemu_get_byte(f);
+ s->int_enable = qemu_get_byte(f);
+
+ return 0;
+}
+
+static uint32_t goldfish_switch_read(void *opaque, target_phys_addr_t offset)
+{
+ struct switch_state *s = (struct switch_state *)opaque;
+ offset -= s->dev.base;
+
+ //printf("goldfish_switch_read %x %x\n", offset, size);
+
+ switch (offset) {
+ case SW_NAME_LEN:
+ return strlen(s->name);
+ case SW_FLAGS:
+ return s->writefn ? SW_FLAGS_OUTPUT : 0;
+ case SW_STATE:
+ return s->state;
+ case SW_INT_STATUS:
+ if(s->state_changed && s->int_enable) {
+ s->state_changed = 0;
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ return 1;
+ }
+ return 0;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_switch_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_switch_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct switch_state *s = (struct switch_state *)opaque;
+ offset -= s->dev.base;
+
+ //printf("goldfish_switch_read %x %x %x\n", offset, value, size);
+
+ switch(offset) {
+ case SW_NAME_PTR:
+ pmemcpy(value, s->name, strlen(s->name));
+ break;
+
+ case SW_STATE:
+ if(s->writefn) {
+ uint32_t new_state;
+ new_state = s->writefn(s->writeopaque, value);
+ if(new_state != s->state) {
+ goldfish_switch_set_state(s, new_state);
+ }
+ }
+ else
+ cpu_abort (cpu_single_env, "goldfish_switch_write: write to SW_STATE on input\n");
+ break;
+
+ case SW_INT_ENABLE:
+ value &= 1;
+ if(s->state_changed && s->int_enable != value)
+ goldfish_device_set_irq(&s->dev, 0, value);
+ s->int_enable = value;
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_switch_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_switch_readfn[] = {
+ goldfish_switch_read,
+ goldfish_switch_read,
+ goldfish_switch_read
+};
+
+static CPUWriteMemoryFunc *goldfish_switch_writefn[] = {
+ goldfish_switch_write,
+ goldfish_switch_write,
+ goldfish_switch_write
+};
+
+void goldfish_switch_set_state(void *opaque, uint32_t state)
+{
+ struct switch_state *s = opaque;
+ s->state_changed = 1;
+ s->state = state;
+ if(s->int_enable)
+ goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id)
+{
+ int ret;
+ struct switch_state *s;
+
+ s = qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish-switch";
+ s->dev.id = id;
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+ s->name = name;
+ s->writefn = writefn;
+ s->writeopaque = writeopaque;
+
+
+ ret = goldfish_device_add(&s->dev, goldfish_switch_readfn, goldfish_switch_writefn, s);
+ if(ret) {
+ qemu_free(s);
+ return NULL;
+ }
+
+ register_savevm( "goldfish_switch", 0, GOLDFISH_SWITCH_SAVE_VERSION,
+ goldfish_switch_save, goldfish_switch_load, s);
+
+ return s;
+}
+
diff --git a/hw/goldfish_timer.c b/hw/goldfish_timer.c
new file mode 100644
index 0000000..73f1455
--- /dev/null
+++ b/hw/goldfish_timer.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu-timer.h"
+#include "cpu.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+
+enum {
+ TIMER_TIME_LOW = 0x00, // get low bits of current time and update TIMER_TIME_HIGH
+ TIMER_TIME_HIGH = 0x04, // get high bits of time at last TIMER_TIME_LOW read
+ TIMER_ALARM_LOW = 0x08, // set low bits of alarm and activate it
+ TIMER_ALARM_HIGH = 0x0c, // set high bits of next alarm
+ TIMER_CLEAR_INTERRUPT = 0x10,
+ TIMER_CLEAR_ALARM = 0x14
+};
+
+struct timer_state {
+ struct goldfish_device dev;
+ uint32_t alarm_low;
+ int32_t alarm_high;
+ int64_t now;
+ int armed;
+ QEMUTimer *timer;
+};
+
+#define GOLDFISH_TIMER_SAVE_VERSION 1
+
+static void goldfish_timer_save(QEMUFile* f, void* opaque)
+{
+ struct timer_state* s = opaque;
+
+ qemu_put_be64(f, s->now); /* in case the kernel is in the middle of a timer read */
+ qemu_put_byte(f, s->armed);
+ if (s->armed) {
+ int64_t now = qemu_get_clock(vm_clock);
+ int64_t alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, ticks_per_sec, 1000000000);
+ qemu_put_be64(f, alarm-now);
+ }
+}
+
+static int goldfish_timer_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct timer_state* s = opaque;
+
+ if (version_id != GOLDFISH_TIMER_SAVE_VERSION)
+ return -1;
+
+ s->now = qemu_get_be64(f);
+ s->armed = qemu_get_byte(f);
+ if (s->armed) {
+ int64_t now = qemu_get_clock(vm_clock);
+ int64_t diff = qemu_get_be64(f);
+ int64_t alarm = now + diff;
+
+ if (alarm <= now) {
+ goldfish_device_set_irq(&s->dev, 0, 1);
+ s->armed = 0;
+ } else {
+ qemu_mod_timer(s->timer, alarm);
+ }
+ }
+ return 0;
+}
+
+static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ struct timer_state *s = (struct timer_state *)opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case TIMER_TIME_LOW:
+ s->now = muldiv64(qemu_get_clock(vm_clock), 1000000000, ticks_per_sec);
+ return s->now;
+ case TIMER_TIME_HIGH:
+ return s->now >> 32;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_timer_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct timer_state *s = (struct timer_state *)opaque;
+ int64_t alarm, now;
+ offset -= s->dev.base;
+ switch(offset) {
+ case TIMER_ALARM_LOW:
+ s->alarm_low = value;
+ alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, ticks_per_sec, 1000000000);
+ now = qemu_get_clock(vm_clock);
+ if (alarm <= now) {
+ goldfish_device_set_irq(&s->dev, 0, 1);
+ } else {
+ qemu_mod_timer(s->timer, alarm);
+ s->armed = 1;
+ }
+ break;
+ case TIMER_ALARM_HIGH:
+ s->alarm_high = value;
+ //printf("alarm_high %d\n", s->alarm_high);
+ break;
+ case TIMER_CLEAR_ALARM:
+ qemu_del_timer(s->timer);
+ s->armed = 0;
+ /* fall through */
+ case TIMER_CLEAR_INTERRUPT:
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ break;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_timer_write: Bad offset %x\n", offset);
+ }
+}
+
+static void goldfish_timer_tick(void *opaque)
+{
+ struct timer_state *s = (struct timer_state *)opaque;
+
+ s->armed = 0;
+ goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+struct rtc_state {
+ struct goldfish_device dev;
+ uint32_t alarm_low;
+ int32_t alarm_high;
+ int64_t now;
+};
+
+/* we save the RTC for the case where the kernel is in the middle of a rtc_read
+ * (i.e. it has read the low 32-bit of s->now, but not the high 32-bits yet */
+#define GOLDFISH_RTC_SAVE_VERSION 1
+
+static void goldfish_rtc_save(QEMUFile* f, void* opaque)
+{
+ struct rtc_state* s = opaque;
+
+ qemu_put_be64(f, s->now);
+}
+
+static int goldfish_rtc_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct rtc_state* s = opaque;
+
+ if (version_id != GOLDFISH_RTC_SAVE_VERSION)
+ return -1;
+
+ /* this is an old value that is not correct. but that's ok anyway */
+ s->now = qemu_get_be64(f);
+ return 0;
+}
+
+static uint32_t goldfish_rtc_read(void *opaque, target_phys_addr_t offset)
+{
+ struct rtc_state *s = (struct rtc_state *)opaque;
+ offset -= s->dev.base;
+ switch(offset) {
+ case 0x0:
+ s->now = (int64_t)time(NULL) * 1000000000;
+ return s->now;
+ case 0x4:
+ return s->now >> 32;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_rtc_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct rtc_state *s = (struct rtc_state *)opaque;
+ int64_t alarm;
+ offset -= s->dev.base;
+ switch(offset) {
+ case 0x8:
+ s->alarm_low = value;
+ alarm = s->alarm_low | (int64_t)s->alarm_high << 32;
+ //printf("next alarm at %lld, tps %lld\n", alarm, ticks_per_sec);
+ //qemu_mod_timer(s->timer, alarm);
+ break;
+ case 0xc:
+ s->alarm_high = value;
+ //printf("alarm_high %d\n", s->alarm_high);
+ break;
+ case 0x10:
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ break;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_rtc_write: Bad offset %x\n", offset);
+ }
+}
+
+static struct timer_state timer_state = {
+ .dev = {
+ .name = "goldfish_timer",
+ .id = -1,
+ .size = 0x1000,
+ .irq_count = 1,
+ }
+};
+
+static struct timer_state rtc_state = {
+ .dev = {
+ .name = "goldfish_rtc",
+ .id = -1,
+ .size = 0x1000,
+ .irq_count = 1,
+ }
+};
+
+static CPUReadMemoryFunc *goldfish_timer_readfn[] = {
+ goldfish_timer_read,
+ goldfish_timer_read,
+ goldfish_timer_read
+};
+
+static CPUWriteMemoryFunc *goldfish_timer_writefn[] = {
+ goldfish_timer_write,
+ goldfish_timer_write,
+ goldfish_timer_write
+};
+
+static CPUReadMemoryFunc *goldfish_rtc_readfn[] = {
+ goldfish_rtc_read,
+ goldfish_rtc_read,
+ goldfish_rtc_read
+};
+
+static CPUWriteMemoryFunc *goldfish_rtc_writefn[] = {
+ goldfish_rtc_write,
+ goldfish_rtc_write,
+ goldfish_rtc_write
+};
+
+void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq)
+{
+ timer_state.dev.base = timerbase;
+ timer_state.dev.irq = timerirq;
+ timer_state.timer = qemu_new_timer(vm_clock, goldfish_timer_tick, &timer_state);
+ goldfish_device_add(&timer_state.dev, goldfish_timer_readfn, goldfish_timer_writefn, &timer_state);
+ register_savevm( "goldfish_timer", 0, GOLDFISH_TIMER_SAVE_VERSION,
+ goldfish_timer_save, goldfish_timer_load, &timer_state);
+
+ goldfish_device_add(&rtc_state.dev, goldfish_rtc_readfn, goldfish_rtc_writefn, &rtc_state);
+ register_savevm( "goldfish_rtc", 0, GOLDFISH_RTC_SAVE_VERSION,
+ goldfish_rtc_save, goldfish_rtc_load, &rtc_state);
+}
+
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
new file mode 100644
index 0000000..ad0eba5
--- /dev/null
+++ b/hw/goldfish_trace.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+/*
+ * Virtual hardware for bridging the FUSE kernel module
+ * in the emulated OS and outside file system
+ */
+#include "qemu_file.h"
+#include "goldfish_trace.h"
+
+//#define DEBUG 1
+
+extern void cpu_loop_exit(void);
+
+extern int tracing;
+
+/* for execve */
+static char path[CLIENT_PAGE_SIZE];
+static char arg[CLIENT_PAGE_SIZE];
+static unsigned long vstart; // VM start
+static unsigned long vend; // VM end
+static unsigned long eoff; // offset in EXE file
+static unsigned cmdlen; // cmdline length
+static unsigned pid; // PID (really thread id)
+static unsigned tgid; // thread group id (really process id)
+static unsigned long dsaddr; // dynamic symbol address
+static unsigned long unmap_start; // start address to unmap
+
+/* for context switch */
+//static unsigned long cs_pid; // context switch PID
+
+/* I/O write */
+static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ trace_dev_state *s = (trace_dev_state *)opaque;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case TRACE_DEV_REG_SWITCH: // context switch, switch to pid
+ trace_switch(value);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, context switch %u\n", value);
+#endif
+ break;
+ case TRACE_DEV_REG_TGID: // save the tgid for the following fork/clone
+ tgid = value;
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, tgid %u\n", value);
+#endif
+ break;
+ case TRACE_DEV_REG_FORK: // fork, fork new pid
+ trace_fork(tgid, value);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, fork %u\n", value);
+#endif
+ break;
+ case TRACE_DEV_REG_CLONE: // fork, clone new pid (i.e. thread)
+ trace_clone(tgid, value);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, clone %u\n", value);
+#endif
+ break;
+ case TRACE_DEV_REG_EXECVE_VMSTART: // execve, vstart
+ vstart = value;
+ break;
+ case TRACE_DEV_REG_EXECVE_VMEND: // execve, vend
+ vend = value;
+ break;
+ case TRACE_DEV_REG_EXECVE_OFFSET: // execve, offset in EXE
+ eoff = value;
+ break;
+ case TRACE_DEV_REG_EXECVE_EXEPATH: // init exec, path of EXE
+ vstrcpy(value, path, CLIENT_PAGE_SIZE);
+ trace_init_exec(vstart, vend, eoff, path);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path);
+#endif
+ path[0] = 0;
+ break;
+ case TRACE_DEV_REG_CMDLINE_LEN: // execve, process cmdline length
+ cmdlen = value;
+ break;
+ case TRACE_DEV_REG_CMDLINE: // execve, process cmdline
+ vmemcpy(value, arg, cmdlen);
+ trace_execve(arg, cmdlen);
+#ifdef DEBUG
+ {
+ int i;
+ for (i = 0; i < cmdlen; i ++)
+ if (i != cmdlen - 1 && arg[i] == 0)
+ arg[i] = ' ';
+ printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen);
+ }
+#endif
+ arg[0] = 0;
+ break;
+ case TRACE_DEV_REG_EXIT: // exit, exit current process with exit code
+ trace_exit(value);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, exit %x\n", value);
+#endif
+ break;
+ case TRACE_DEV_REG_NAME: // record thread name
+ vstrcpy(value, path, CLIENT_PAGE_SIZE);
+
+ // Remove the trailing newline if it exists
+ int len = strlen(path);
+ if (path[len - 1] == '\n') {
+ path[len - 1] = 0;
+ }
+ trace_name(path);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, name %s\n", path);
+#endif
+ break;
+ case TRACE_DEV_REG_MMAP_EXEPATH: // mmap, path of EXE, the others are same as execve
+ vstrcpy(value, path, CLIENT_PAGE_SIZE);
+ trace_mmap(vstart, vend, eoff, path);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path);
+#endif
+ path[0] = 0;
+ break;
+ case TRACE_DEV_REG_INIT_PID: // init, name the pid that starts before device registered
+ pid = value;
+ break;
+ case TRACE_DEV_REG_INIT_NAME: // init, the comm of the init pid
+ vstrcpy(value, path, CLIENT_PAGE_SIZE);
+ trace_init_name(tgid, pid, path);
+#ifdef DEBUG
+ printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path);
+#endif
+ path[0] = 0;
+ break;
+
+ case TRACE_DEV_REG_DYN_SYM_ADDR: // dynamic symbol address
+ dsaddr = value;
+ break;
+ case TRACE_DEV_REG_DYN_SYM: // add dynamic symbol
+ vstrcpy(value, arg, CLIENT_PAGE_SIZE);
+ trace_dynamic_symbol_add(dsaddr, arg);
+#ifdef DEBUG
+ printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg);
+#endif
+ arg[0] = 0;
+ break;
+ case TRACE_DEV_REG_REMOVE_ADDR: // remove dynamic symbol addr
+ trace_dynamic_symbol_remove(value);
+#ifdef DEBUG
+ printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
+#endif
+ arg[0] = 0;
+ break;
+
+ case TRACE_DEV_REG_PRINT_STR: // print string
+ vstrcpy(value, arg, CLIENT_PAGE_SIZE);
+ printf("%s", arg);
+ arg[0] = 0;
+ break;
+ case TRACE_DEV_REG_PRINT_NUM_DEC: // print number in decimal
+ printf("%d", value);
+ break;
+ case TRACE_DEV_REG_PRINT_NUM_HEX: // print number in hexical
+ printf("%x", value);
+ break;
+
+ case TRACE_DEV_REG_STOP_EMU: // stop the VM execution
+ // To ensure that the number of instructions executed in this
+ // block is correct, we pretend that there was an exception.
+ trace_exception(0);
+
+ cpu_single_env->exception_index = EXCP_HLT;
+ cpu_single_env->halted = 1;
+ qemu_system_shutdown_request();
+ cpu_loop_exit();
+ break;
+
+ case TRACE_DEV_REG_ENABLE: // tracing enable: 0 = stop, 1 = start
+ if (value == 1)
+ start_tracing();
+ else if (value == 0) {
+ stop_tracing();
+
+ // To ensure that the number of instructions executed in this
+ // block is correct, we pretend that there was an exception.
+ trace_exception(0);
+ }
+ break;
+
+ case TRACE_DEV_REG_UNMAP_START:
+ unmap_start = value;
+ break;
+ case TRACE_DEV_REG_UNMAP_END:
+ trace_munmap(unmap_start, value);
+ break;
+
+ default:
+ cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
+ break;
+ }
+}
+
+/* I/O read */
+static uint32_t trace_dev_read(void *opaque, target_phys_addr_t offset)
+{
+ trace_dev_state *s = (trace_dev_state *)opaque;
+
+ offset -= s->base;
+ switch (offset >> 2) {
+ case TRACE_DEV_REG_ENABLE: // tracing enable
+ return tracing;
+ default:
+ cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
+ return 0;
+ }
+ return 0;
+}
+
+static CPUReadMemoryFunc *trace_dev_readfn[] = {
+ trace_dev_read,
+ trace_dev_read,
+ trace_dev_read
+};
+
+static CPUWriteMemoryFunc *trace_dev_writefn[] = {
+ trace_dev_write,
+ trace_dev_write,
+ trace_dev_write
+};
+
+/* initialize the trace device */
+void trace_dev_init(uint32_t base)
+{
+ int iomemtype;
+ trace_dev_state *s;
+
+ s = (trace_dev_state *)qemu_mallocz(sizeof(trace_dev_state));
+ iomemtype = cpu_register_io_memory(0, trace_dev_readfn, trace_dev_writefn, s);
+ cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+ s->base = base;
+
+ path[0] = arg[0] = '\0';
+}
diff --git a/hw/goldfish_trace.h b/hw/goldfish_trace.h
new file mode 100644
index 0000000..44190ee
--- /dev/null
+++ b/hw/goldfish_trace.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#ifndef _TRACE_DEV_H_
+#define _TRACE_DEV_H_
+
+#define CLIENT_PAGE_SIZE 4096
+
+/* trace device registers */
+#define TRACE_DEV_REG_SWITCH 0
+#define TRACE_DEV_REG_FORK 1
+#define TRACE_DEV_REG_EXECVE_PID 2
+#define TRACE_DEV_REG_EXECVE_VMSTART 3
+#define TRACE_DEV_REG_EXECVE_VMEND 4
+#define TRACE_DEV_REG_EXECVE_OFFSET 5
+#define TRACE_DEV_REG_EXECVE_EXEPATH 6
+#define TRACE_DEV_REG_EXIT 7
+#define TRACE_DEV_REG_CMDLINE 8
+#define TRACE_DEV_REG_CMDLINE_LEN 9
+#define TRACE_DEV_REG_MMAP_EXEPATH 10
+#define TRACE_DEV_REG_INIT_PID 11
+#define TRACE_DEV_REG_INIT_NAME 12
+#define TRACE_DEV_REG_CLONE 13
+#define TRACE_DEV_REG_UNMAP_START 14
+#define TRACE_DEV_REG_UNMAP_END 15
+#define TRACE_DEV_REG_NAME 16
+#define TRACE_DEV_REG_TGID 17
+#define TRACE_DEV_REG_DYN_SYM 50
+#define TRACE_DEV_REG_DYN_SYM_ADDR 51
+#define TRACE_DEV_REG_REMOVE_ADDR 52
+#define TRACE_DEV_REG_PRINT_STR 60
+#define TRACE_DEV_REG_PRINT_NUM_DEC 61
+#define TRACE_DEV_REG_PRINT_NUM_HEX 62
+#define TRACE_DEV_REG_STOP_EMU 90
+#define TRACE_DEV_REG_ENABLE 100
+
+/* the virtual trace device state */
+typedef struct {
+ uint32_t base;
+} trace_dev_state;
+
+/*
+ * interfaces for copy from virtual space
+ * from target-arm/op_helper.c
+ */
+extern target_phys_addr_t v2p(target_ulong ptr, int is_user);
+extern void vmemcpy(target_ulong ptr, char *buf, int size);
+extern void pmemcpy(target_ulong ptr, const char* buf, int size);
+extern void vstrcpy(target_ulong ptr, char *buf, int max);
+
+/*
+ * interfaces to trace module to signal kernel events
+ */
+extern void trace_switch(int pid);
+extern void trace_fork(int tgid, int pid);
+extern void trace_clone(int tgid, int pid);
+extern void trace_execve(const char *arg, int len);
+extern void trace_exit(int exitcode);
+extern void trace_mmap(unsigned long vstart, unsigned long vend,
+ unsigned long offset, const char *path);
+extern void trace_munmap(unsigned long vstart, unsigned long vend);
+extern void trace_dynamic_symbol_add(unsigned long vaddr, const char *name);
+extern void trace_dynamic_symbol_remove(unsigned long vaddr);
+extern void trace_init_name(int tgid, int pid, const char *name);
+extern void trace_init_exec(unsigned long start, unsigned long end,
+ unsigned long offset, const char *exe);
+extern void start_tracing(void);
+extern void stop_tracing(void);
+extern void trace_exception(uint32 target_pc);
+
+#endif
diff --git a/hw/goldfish_tty.c b/hw/goldfish_tty.c
new file mode 100644
index 0000000..aa62d75
--- /dev/null
+++ b/hw/goldfish_tty.c
@@ -0,0 +1,226 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "qemu_file.h"
+#include "qemu-char.h"
+#include "goldfish_device.h"
+
+enum {
+ TTY_PUT_CHAR = 0x00,
+ TTY_BYTES_READY = 0x04,
+ TTY_CMD = 0x08,
+
+ TTY_DATA_PTR = 0x10,
+ TTY_DATA_LEN = 0x14,
+
+ TTY_CMD_INT_DISABLE = 0,
+ TTY_CMD_INT_ENABLE = 1,
+ TTY_CMD_WRITE_BUFFER = 2,
+ TTY_CMD_READ_BUFFER = 3,
+};
+
+struct tty_state {
+ struct goldfish_device dev;
+ CharDriverState *cs;
+ uint32_t ptr;
+ uint32_t ptr_len;
+ uint32_t ready;
+ uint8_t data[128];
+ uint32_t data_count;
+};
+
+#define GOLDFISH_TTY_SAVE_VERSION 1
+
+static void goldfish_tty_save(QEMUFile* f, void* opaque)
+{
+ struct tty_state* s = opaque;
+
+ qemu_put_be32( f, s->ptr );
+ qemu_put_be32( f, s->ptr_len );
+ qemu_put_byte( f, s->ready );
+ qemu_put_byte( f, s->data_count );
+ qemu_put_buffer( f, s->data, s->data_count );
+}
+
+static int goldfish_tty_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct tty_state* s = opaque;
+
+ if (version_id != GOLDFISH_TTY_SAVE_VERSION)
+ return -1;
+
+ s->ptr = qemu_get_be32(f);
+ s->ptr_len = qemu_get_be32(f);
+ s->ready = qemu_get_byte(f);
+ s->data_count = qemu_get_byte(f);
+ qemu_get_buffer(f, s->data, s->data_count);
+
+ return 0;
+}
+
+static uint32_t goldfish_tty_read(void *opaque, target_phys_addr_t offset)
+{
+ struct tty_state *s = (struct tty_state *)opaque;
+ offset -= s->dev.base;
+
+ //printf("goldfish_tty_read %x %x\n", offset, size);
+
+ switch (offset) {
+ case TTY_BYTES_READY:
+ return s->data_count;
+ default:
+ cpu_abort (cpu_single_env, "goldfish_tty_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_tty_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ struct tty_state *s = (struct tty_state *)opaque;
+ offset -= s->dev.base;
+
+ //printf("goldfish_tty_read %x %x %x\n", offset, value, size);
+
+ switch(offset) {
+ case TTY_PUT_CHAR: {
+ uint8_t ch = value;
+ if(s->cs)
+ qemu_chr_write(s->cs, &ch, 1);
+ } break;
+
+ case TTY_CMD:
+ switch(value) {
+ case TTY_CMD_INT_DISABLE:
+ if(s->ready) {
+ if(s->data_count > 0)
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ s->ready = 0;
+ }
+ break;
+
+ case TTY_CMD_INT_ENABLE:
+ if(!s->ready) {
+ if(s->data_count > 0)
+ goldfish_device_set_irq(&s->dev, 0, 1);
+ s->ready = 1;
+ }
+ break;
+
+ case TTY_CMD_WRITE_BUFFER:
+ if(s->cs) {
+ int len;
+ target_ulong buf;
+
+ buf = s->ptr;
+ len = s->ptr_len;
+
+ while(len) {
+ int page_remain = TARGET_PAGE_SIZE - (buf & ~TARGET_PAGE_MASK);
+ int to_write = len;
+ uint8_t *phys = (uint8_t *)v2p(buf, 0);
+ if(to_write > page_remain)
+ to_write = page_remain;
+ qemu_chr_write(s->cs, phys, to_write);
+ buf += to_write;
+ len -= to_write;
+ }
+ //printf("goldfish_tty_write: got %d bytes from %x\n", s->ptr_len, s->ptr);
+ }
+ break;
+
+ case TTY_CMD_READ_BUFFER:
+ if(s->ptr_len > s->data_count)
+ cpu_abort (cpu_single_env, "goldfish_tty_write: reading more data than available %d %d\n", s->ptr_len, s->data_count);
+ pmemcpy(s->ptr, s->data, s->ptr_len);
+ //printf("goldfish_tty_write: read %d bytes to %x\n", s->ptr_len, s->ptr);
+ if(s->data_count > s->ptr_len)
+ memmove(s->data, s->data + s->ptr_len, s->data_count - s->ptr_len);
+ s->data_count -= s->ptr_len;
+ if(s->data_count == 0 && s->ready)
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_tty_write: Bad command %x\n", value);
+ };
+ break;
+
+ case TTY_DATA_PTR:
+ s->ptr = value;
+ break;
+
+ case TTY_DATA_LEN:
+ s->ptr_len = value;
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_tty_write: Bad offset %x\n", offset);
+ }
+}
+
+static int tty_can_receive(void *opaque)
+{
+ struct tty_state *s = opaque;
+
+ return (sizeof(s->data) - s->data_count);
+}
+
+static void tty_receive(void *opaque, const uint8_t *buf, int size)
+{
+ struct tty_state *s = opaque;
+
+ memcpy(s->data + s->data_count, buf, size);
+ s->data_count += size;
+ if(s->data_count > 0 && s->ready)
+ goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+static CPUReadMemoryFunc *goldfish_tty_readfn[] = {
+ goldfish_tty_read,
+ goldfish_tty_read,
+ goldfish_tty_read
+};
+
+static CPUWriteMemoryFunc *goldfish_tty_writefn[] = {
+ goldfish_tty_write,
+ goldfish_tty_write,
+ goldfish_tty_write
+};
+
+int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq)
+{
+ int ret;
+ struct tty_state *s;
+ static int instance_id = 0;
+
+ s = qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish_tty";
+ s->dev.id = id;
+ s->dev.base = base;
+ s->dev.size = 0x1000;
+ s->dev.irq = irq;
+ s->dev.irq_count = 1;
+ s->cs = cs;
+
+ if(cs) {
+ qemu_chr_add_handlers(cs, tty_can_receive, tty_receive, NULL, s);
+ }
+
+ ret = goldfish_device_add(&s->dev, goldfish_tty_readfn, goldfish_tty_writefn, s);
+ if(ret) {
+ qemu_free(s);
+ } else {
+ register_savevm( "goldfish_tty", instance_id++, GOLDFISH_TTY_SAVE_VERSION,
+ goldfish_tty_save, goldfish_tty_load, s);
+ }
+ return ret;
+}
+
diff --git a/hw/hw.h b/hw/hw.h
new file mode 100644
index 0000000..06e24cb
--- /dev/null
+++ b/hw/hw.h
@@ -0,0 +1,110 @@
+/* Declarations for use by hardware emulation. */
+#ifndef QEMU_HW_H
+#define QEMU_HW_H
+
+#include "qemu-common.h"
+#include "irq.h"
+#include "cpu.h"
+
+/* VM Load/Save */
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode);
+void qemu_fflush(QEMUFile *f);
+void qemu_fclose(QEMUFile *f);
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
+void qemu_put_byte(QEMUFile *f, int v);
+void qemu_put_be16(QEMUFile *f, unsigned int v);
+void qemu_put_be32(QEMUFile *f, unsigned int v);
+void qemu_put_be64(QEMUFile *f, uint64_t v);
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+int qemu_get_byte(QEMUFile *f);
+unsigned int qemu_get_be16(QEMUFile *f);
+unsigned int qemu_get_be32(QEMUFile *f);
+uint64_t qemu_get_be64(QEMUFile *f);
+
+static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
+{
+ qemu_put_be64(f, *pv);
+}
+
+static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv)
+{
+ qemu_put_be32(f, *pv);
+}
+
+static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv)
+{
+ qemu_put_be16(f, *pv);
+}
+
+static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv)
+{
+ qemu_put_byte(f, *pv);
+}
+
+static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv)
+{
+ *pv = qemu_get_be64(f);
+}
+
+static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv)
+{
+ *pv = qemu_get_be32(f);
+}
+
+static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv)
+{
+ *pv = qemu_get_be16(f);
+}
+
+static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv)
+{
+ *pv = qemu_get_byte(f);
+}
+
+#ifdef NEED_CPU_H
+#if TARGET_LONG_BITS == 64
+#define qemu_put_betl qemu_put_be64
+#define qemu_get_betl qemu_get_be64
+#define qemu_put_betls qemu_put_be64s
+#define qemu_get_betls qemu_get_be64s
+#else
+#define qemu_put_betl qemu_put_be32
+#define qemu_get_betl qemu_get_be32
+#define qemu_put_betls qemu_put_be32s
+#define qemu_get_betls qemu_get_be32s
+#endif
+#endif
+
+int64_t qemu_ftell(QEMUFile *f);
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+int register_savevm(const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+typedef void QEMUResetHandler(void *opaque);
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque);
+
+/* handler to set the boot_device for a specific type of QEMUMachine */
+/* return 0 if success */
+typedef int QEMUBootSetHandler(const char *boot_device);
+extern QEMUBootSetHandler *qemu_boot_set_handler;
+void qemu_register_boot_set(QEMUBootSetHandler *func);
+
+/* These should really be in isa.h, but are here to make pc.h happy. */
+typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
+typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
+
+
+/* ANDROID: copy memory from the QEMU buffer to simulated virtual space */
+extern void pmemcpy(target_ulong ptr, const char *buf, int size);
+
+#endif
diff --git a/hw/irq.c b/hw/irq.c
new file mode 100644
index 0000000..eca707d
--- /dev/null
+++ b/hw/irq.c
@@ -0,0 +1,71 @@
+/*
+ * QEMU IRQ/GPIO common code.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "irq.h"
+
+struct IRQState {
+ qemu_irq_handler handler;
+ void *opaque;
+ int n;
+};
+
+void qemu_set_irq(qemu_irq irq, int level)
+{
+ if (!irq)
+ return;
+
+ irq->handler(irq->opaque, irq->n, level);
+}
+
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+ qemu_irq *s;
+ struct IRQState *p;
+ int i;
+
+ s = (qemu_irq *)qemu_mallocz(sizeof(qemu_irq) * n);
+ p = (struct IRQState *)qemu_mallocz(sizeof(struct IRQState) * n);
+ for (i = 0; i < n; i++) {
+ p->handler = handler;
+ p->opaque = opaque;
+ p->n = i;
+ s[i] = p;
+ p++;
+ }
+ return s;
+}
+
+static void qemu_notirq(void *opaque, int line, int level)
+{
+ struct IRQState *irq = opaque;
+
+ irq->handler(irq->opaque, irq->n, !level);
+}
+
+qemu_irq qemu_irq_invert(qemu_irq irq)
+{
+ /* The default state for IRQs is low, so raise the output now. */
+ qemu_irq_raise(irq);
+ return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
+}
diff --git a/hw/irq.h b/hw/irq.h
new file mode 100644
index 0000000..0880ad2
--- /dev/null
+++ b/hw/irq.h
@@ -0,0 +1,34 @@
+#ifndef QEMU_IRQ_H
+#define QEMU_IRQ_H
+
+/* Generic IRQ/GPIO pin infrastructure. */
+
+/* FIXME: Rmove one of these. */
+typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
+typedef void SetIRQFunc(void *opaque, int irq_num, int level);
+
+void qemu_set_irq(qemu_irq irq, int level);
+
+static inline void qemu_irq_raise(qemu_irq irq)
+{
+ qemu_set_irq(irq, 1);
+}
+
+static inline void qemu_irq_lower(qemu_irq irq)
+{
+ qemu_set_irq(irq, 0);
+}
+
+static inline void qemu_irq_pulse(qemu_irq irq)
+{
+ qemu_set_irq(irq, 1);
+ qemu_set_irq(irq, 0);
+}
+
+/* Returns an array of N IRQs. */
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+
+/* Returns a new IRQ with opposite polarity. */
+qemu_irq qemu_irq_invert(qemu_irq irq);
+
+#endif
diff --git a/hw/isa.h b/hw/isa.h
new file mode 100644
index 0000000..222e4f3
--- /dev/null
+++ b/hw/isa.h
@@ -0,0 +1,27 @@
+#ifndef HW_ISA_H
+#define HW_ISA_H
+/* ISA bus */
+
+extern target_phys_addr_t isa_mem_base;
+
+int register_ioport_read(int start, int length, int size,
+ IOPortReadFunc *func, void *opaque);
+int register_ioport_write(int start, int length, int size,
+ IOPortWriteFunc *func, void *opaque);
+void isa_unassign_ioport(int start, int length);
+
+void isa_mmio_init(target_phys_addr_t base, target_phys_addr_t size);
+
+/* dma.c */
+int DMA_get_channel_mode (int nchan);
+int DMA_read_memory (int nchan, void *buf, int pos, int size);
+int DMA_write_memory (int nchan, void *buf, int pos, int size);
+void DMA_hold_DREQ (int nchan);
+void DMA_release_DREQ (int nchan);
+void DMA_schedule(int nchan);
+void DMA_run (void);
+void DMA_init (int high_page_enable);
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque);
+#endif
diff --git a/hw/mmc.h b/hw/mmc.h
new file mode 100644
index 0000000..3ae3ea9
--- /dev/null
+++ b/hw/mmc.h
@@ -0,0 +1,214 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date : $Date: 2002/06/18 12:37:30 $
+ *
+ * Author: Andrew Christian
+ * 15 May 2002
+ */
+
+#ifndef MMC_MMC_H
+#define MMC_MMC_H
+
+/* Standard MMC commands (4.1) type argument response */
+ /* class 1 */
+#define MMC_GO_IDLE_STATE 0 /* bc */
+#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */
+#define MMC_ALL_SEND_CID 2 /* bcr R2 */
+#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
+#define MMC_SET_DSR 4 /* bc [31:16] RCA */
+#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
+#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
+#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
+#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */
+#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */
+#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
+#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
+#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
+#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
+
+ /* class 2 */
+#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
+#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
+#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
+
+ /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
+
+ /* class 4 */
+#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */
+#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */
+#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */
+#define MMC_PROGRAM_CID 26 /* adtc R1 */
+#define MMC_PROGRAM_CSD 27 /* adtc R1 */
+
+ /* class 6 */
+#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */
+#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */
+#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */
+
+ /* class 5 */
+#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */
+#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */
+#define MMC_ERASE 38 /* ac R1b */
+
+ /* class 9 */
+#define MMC_FAST_IO 39 /* ac <Complex> R4 */
+#define MMC_GO_IRQ_STATE 40 /* bcr R5 */
+
+ /* class 7 */
+#define MMC_LOCK_UNLOCK 42 /* adtc R1b */
+
+ /* class 8 */
+#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
+#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
+
+/*
+ * MMC_SWITCH argument format:
+ *
+ * [31:26] Always 0
+ * [25:24] Access Mode
+ * [23:16] Location of target Byte in EXT_CSD
+ * [15:08] Value Byte
+ * [07:03] Always 0
+ * [02:00] Command Set
+ */
+
+/*
+ MMC status in R1
+ Type
+ e : error bit
+ s : status bit
+ r : detected and set for the actual command response
+ x : detected and set during command execution. the host must poll
+ the card by sending status command in order to read these bits.
+ Clear condition
+ a : according to the card state
+ b : always related to the previous command. Reception of
+ a valid command will clear it (with a delay of one command)
+ c : clear by read
+ */
+
+#define R1_OUT_OF_RANGE (1 << 31) /* er, c */
+#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */
+#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */
+#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */
+#define R1_ERASE_PARAM (1 << 27) /* ex, c */
+#define R1_WP_VIOLATION (1 << 26) /* erx, c */
+#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */
+#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */
+#define R1_COM_CRC_ERROR (1 << 23) /* er, b */
+#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */
+#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */
+#define R1_CC_ERROR (1 << 20) /* erx, c */
+#define R1_ERROR (1 << 19) /* erx, c */
+#define R1_UNDERRUN (1 << 18) /* ex, c */
+#define R1_OVERRUN (1 << 17) /* ex, c */
+#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
+#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
+#define R1_ERASE_RESET (1 << 13) /* sr, c */
+#define R1_STATUS(x) (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
+#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
+#define R1_APP_CMD (1 << 5) /* sr, c */
+
+
+/*
+ * OCR bits are mostly in host.h
+ */
+#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
+
+/*
+ * Card Command Classes (CCC)
+ */
+#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */
+ /* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */
+ /* (CMD11) */
+#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */
+ /* (CMD16,17,18) */
+#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */
+ /* (CMD20) */
+#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */
+ /* (CMD16,24,25,26,27) */
+#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */
+ /* (CMD32,33,34,35,36,37,38,39) */
+#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */
+ /* (CMD28,29,30) */
+#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */
+ /* (CMD16,CMD42) */
+#define CCC_APP_SPEC (1<<8) /* (8) Application specific */
+ /* (CMD55,56,57,ACMD*) */
+#define CCC_IO_MODE (1<<9) /* (9) I/O mode */
+ /* (CMD5,39,40,52,53) */
+#define CCC_SWITCH (1<<10) /* (10) High speed switch */
+ /* (CMD6,34,35,36,37,50) */
+ /* (11) Reserved */
+ /* (CMD?) */
+
+/*
+ * CSD field definitions
+ */
+
+#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */
+#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */
+
+#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */
+#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */
+
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL (1<<0)
+#define EXT_CSD_CMD_SET_SECURE (1<<1)
+#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
+
+#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
+
+#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
+
+/*
+ * MMC_SWITCH access modes
+ */
+
+#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */
+#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
+#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
+
+#endif /* MMC_MMC_PROTOCOL_H */
+
diff --git a/hw/pc.h b/hw/pc.h
new file mode 100644
index 0000000..2862849
--- /dev/null
+++ b/hw/pc.h
@@ -0,0 +1,148 @@
+#ifndef HW_PC_H
+#define HW_PC_H
+/* PC-style peripherals (also used by other machines). */
+
+/* serial.c */
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+ CharDriverState *chr);
+SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
+ qemu_irq irq, int baudbase,
+ CharDriverState *chr, int ioregister);
+uint32_t serial_mm_readb (void *opaque, target_phys_addr_t addr);
+void serial_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value);
+uint32_t serial_mm_readw (void *opaque, target_phys_addr_t addr);
+void serial_mm_writew (void *opaque, target_phys_addr_t addr, uint32_t value);
+uint32_t serial_mm_readl (void *opaque, target_phys_addr_t addr);
+void serial_mm_writel (void *opaque, target_phys_addr_t addr, uint32_t value);
+
+/* parallel.c */
+
+typedef struct ParallelState ParallelState;
+ParallelState *parallel_init(int base, qemu_irq irq, CharDriverState *chr);
+ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr);
+
+/* i8259.c */
+
+typedef struct PicState2 PicState2;
+extern PicState2 *isa_pic;
+void pic_set_irq(int irq, int level);
+void pic_set_irq_new(void *opaque, int irq, int level);
+qemu_irq *i8259_init(qemu_irq parent_irq);
+void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
+ void *alt_irq_opaque);
+int pic_read_irq(PicState2 *s);
+void pic_update_irq(PicState2 *s);
+uint32_t pic_intack_read(PicState2 *s);
+void pic_info(void);
+void irq_info(void);
+
+/* APIC */
+typedef struct IOAPICState IOAPICState;
+
+int apic_init(CPUState *env);
+int apic_accept_pic_intr(CPUState *env);
+void apic_deliver_pic_intr(CPUState *env, int level);
+int apic_get_interrupt(CPUState *env);
+IOAPICState *ioapic_init(void);
+void ioapic_set_irq(void *opaque, int vector, int level);
+
+/* i8254.c */
+
+#define PIT_FREQ 1193182
+
+typedef struct PITState PITState;
+
+PITState *pit_init(int base, qemu_irq irq);
+void pit_set_gate(PITState *pit, int channel, int val);
+int pit_get_gate(PITState *pit, int channel);
+int pit_get_initial_count(PITState *pit, int channel);
+int pit_get_mode(PITState *pit, int channel);
+int pit_get_out(PITState *pit, int channel, int64_t current_time);
+
+/* vmport.c */
+void vmport_init(void);
+void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque);
+
+/* vmmouse.c */
+void *vmmouse_init(void *m);
+
+/* pckbd.c */
+
+void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base);
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+ target_phys_addr_t base, int it_shift);
+
+/* mc146818rtc.c */
+
+typedef struct RTCState RTCState;
+
+RTCState *rtc_init(int base, qemu_irq irq);
+RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq);
+void rtc_set_memory(RTCState *s, int addr, int val);
+void rtc_set_date(RTCState *s, const struct tm *tm);
+
+/* pc.c */
+extern int fd_bootchk;
+
+void ioport_set_a20(int enable);
+int ioport_get_a20(void);
+
+/* acpi.c */
+extern int acpi_enabled;
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+ qemu_irq sci_irq);
+void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
+void acpi_bios_init(void);
+
+/* pcspk.c */
+void pcspk_init(PITState *);
+int pcspk_audio_init(AudioState *, qemu_irq *pic);
+
+/* piix_pci.c */
+PCIBus *i440fx_init(PCIDevice **pi440fx_state, qemu_irq *pic);
+void i440fx_set_smm(PCIDevice *d, int val);
+int piix3_init(PCIBus *bus, int devfn);
+void i440fx_init_memory_mappings(PCIDevice *d);
+
+int piix4_init(PCIBus *bus, int devfn);
+
+/* vga.c */
+
+#ifndef TARGET_SPARC
+#define VGA_RAM_SIZE (8192 * 1024)
+#else
+#define VGA_RAM_SIZE (9 * 1024 * 1024)
+#endif
+
+int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size,
+ unsigned long vga_bios_offset, int vga_bios_size);
+int isa_vga_mm_init(DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size,
+ target_phys_addr_t vram_base, target_phys_addr_t ctrl_base,
+ int it_shift);
+
+/* cirrus_vga.c */
+void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+
+/* ide.c */
+void isa_ide_init(int iobase, int iobase2, qemu_irq irq,
+ BlockDriverState *hd0, BlockDriverState *hd1);
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+ int secondary_ide_enabled);
+void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
+ qemu_irq *pic);
+void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
+ qemu_irq *pic);
+
+/* ne2000.c */
+
+void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
+
+#endif
diff --git a/hw/pci.c b/hw/pci.c
new file mode 100644
index 0000000..5f7004a
--- /dev/null
+++ b/hw/pci.c
@@ -0,0 +1,701 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "console.h"
+#include "net.h"
+
+//#define DEBUG_PCI
+
+struct PCIBus {
+ int bus_num;
+ int devfn_min;
+ pci_set_irq_fn set_irq;
+ pci_map_irq_fn map_irq;
+ uint32_t config_reg; /* XXX: suppress */
+ /* low level pic */
+ SetIRQFunc *low_set_irq;
+ qemu_irq *irq_opaque;
+ PCIDevice *devices[256];
+ PCIDevice *parent_dev;
+ PCIBus *next;
+ /* The bus IRQ state is the logical OR of the connected devices.
+ Keep a count of the number of devices with raised IRQs. */
+ int nirq;
+ int irq_count[];
+};
+
+static void pci_update_mappings(PCIDevice *d);
+static void pci_set_irq(void *opaque, int irq_num, int level);
+
+target_phys_addr_t pci_mem_base;
+static int pci_irq_index;
+static PCIBus *first_bus;
+
+static void pcibus_save(QEMUFile *f, void *opaque)
+{
+ PCIBus *bus = (PCIBus *)opaque;
+ int i;
+
+ qemu_put_be32(f, bus->nirq);
+ for (i = 0; i < bus->nirq; i++)
+ qemu_put_be32(f, bus->irq_count[i]);
+}
+
+static int pcibus_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PCIBus *bus = (PCIBus *)opaque;
+ int i, nirq;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ nirq = qemu_get_be32(f);
+ if (bus->nirq != nirq) {
+ fprintf(stderr, "pcibus_load: nirq mismatch: src=%d dst=%d\n",
+ nirq, bus->nirq);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nirq; i++)
+ bus->irq_count[i] = qemu_get_be32(f);
+
+ return 0;
+}
+
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ qemu_irq *pic, int devfn_min, int nirq)
+{
+ PCIBus *bus;
+ static int nbus = 0;
+
+ bus = qemu_mallocz(sizeof(PCIBus) + (nirq * sizeof(int)));
+ bus->set_irq = set_irq;
+ bus->map_irq = map_irq;
+ bus->irq_opaque = pic;
+ bus->devfn_min = devfn_min;
+ bus->nirq = nirq;
+ first_bus = bus;
+ register_savevm("PCIBUS", nbus++, 1, pcibus_save, pcibus_load, bus);
+ return bus;
+}
+
+static PCIBus *pci_register_secondary_bus(PCIDevice *dev, pci_map_irq_fn map_irq)
+{
+ PCIBus *bus;
+ bus = qemu_mallocz(sizeof(PCIBus));
+ bus->map_irq = map_irq;
+ bus->parent_dev = dev;
+ bus->next = dev->bus->next;
+ dev->bus->next = bus;
+ return bus;
+}
+
+int pci_bus_num(PCIBus *s)
+{
+ return s->bus_num;
+}
+
+void pci_device_save(PCIDevice *s, QEMUFile *f)
+{
+ int i;
+
+ qemu_put_be32(f, 2); /* PCI device version */
+ qemu_put_buffer(f, s->config, 256);
+ for (i = 0; i < 4; i++)
+ qemu_put_be32(f, s->irq_state[i]);
+}
+
+int pci_device_load(PCIDevice *s, QEMUFile *f)
+{
+ uint32_t version_id;
+ int i;
+
+ version_id = qemu_get_be32(f);
+ if (version_id > 2)
+ return -EINVAL;
+ qemu_get_buffer(f, s->config, 256);
+ pci_update_mappings(s);
+
+ if (version_id >= 2)
+ for (i = 0; i < 4; i ++)
+ s->irq_state[i] = qemu_get_be32(f);
+
+ return 0;
+}
+
+/* -1 for devfn means auto assign */
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write)
+{
+ PCIDevice *pci_dev;
+
+ if (pci_irq_index >= PCI_DEVICES_MAX)
+ return NULL;
+
+ if (devfn < 0) {
+ for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
+ if (!bus->devices[devfn])
+ goto found;
+ }
+ return NULL;
+ found: ;
+ }
+ pci_dev = qemu_mallocz(instance_size);
+ if (!pci_dev)
+ return NULL;
+ pci_dev->bus = bus;
+ pci_dev->devfn = devfn;
+ pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
+ memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state));
+
+ if (!config_read)
+ config_read = pci_default_read_config;
+ if (!config_write)
+ config_write = pci_default_write_config;
+ pci_dev->config_read = config_read;
+ pci_dev->config_write = config_write;
+ pci_dev->irq_index = pci_irq_index++;
+ bus->devices[devfn] = pci_dev;
+ pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, 4);
+ return pci_dev;
+}
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+ uint32_t size, int type,
+ PCIMapIORegionFunc *map_func)
+{
+ PCIIORegion *r;
+ uint32_t addr;
+
+ if ((unsigned int)region_num >= PCI_NUM_REGIONS)
+ return;
+ r = &pci_dev->io_regions[region_num];
+ r->addr = -1;
+ r->size = size;
+ r->type = type;
+ r->map_func = map_func;
+ if (region_num == PCI_ROM_SLOT) {
+ addr = 0x30;
+ } else {
+ addr = 0x10 + region_num * 4;
+ }
+ *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
+}
+
+static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+{
+ return addr + pci_mem_base;
+}
+
+static void pci_update_mappings(PCIDevice *d)
+{
+ PCIIORegion *r;
+ int cmd, i;
+ uint32_t last_addr, new_addr, config_ofs;
+
+ cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND));
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (i == PCI_ROM_SLOT) {
+ config_ofs = 0x30;
+ } else {
+ config_ofs = 0x10 + i * 4;
+ }
+ if (r->size != 0) {
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ if (cmd & PCI_COMMAND_IO) {
+ new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+ config_ofs));
+ new_addr = new_addr & ~(r->size - 1);
+ last_addr = new_addr + r->size - 1;
+ /* NOTE: we have only 64K ioports on PC */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr >= 0x10000) {
+ new_addr = -1;
+ }
+ } else {
+ new_addr = -1;
+ }
+ } else {
+ if (cmd & PCI_COMMAND_MEMORY) {
+ new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+ config_ofs));
+ /* the ROM slot has a specific enable bit */
+ if (i == PCI_ROM_SLOT && !(new_addr & 1))
+ goto no_mem_map;
+ new_addr = new_addr & ~(r->size - 1);
+ last_addr = new_addr + r->size - 1;
+ /* NOTE: we do not support wrapping */
+ /* XXX: as we cannot support really dynamic
+ mappings, we handle specific values as invalid
+ mappings. */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr == -1) {
+ new_addr = -1;
+ }
+ } else {
+ no_mem_map:
+ new_addr = -1;
+ }
+ }
+ /* now do the real mapping */
+ if (new_addr != r->addr) {
+ if (r->addr != -1) {
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ int class;
+ /* NOTE: specific hack for IDE in PC case:
+ only one byte must be mapped. */
+ class = d->config[0x0a] | (d->config[0x0b] << 8);
+ if (class == 0x0101 && r->size == 4) {
+ isa_unassign_ioport(r->addr + 2, 1);
+ } else {
+ isa_unassign_ioport(r->addr, r->size);
+ }
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
+ r->size,
+ IO_MEM_UNASSIGNED);
+ }
+ }
+ r->addr = new_addr;
+ if (r->addr != -1) {
+ r->map_func(d, i, r->addr, r->size, r->type);
+ }
+ }
+ }
+ }
+}
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len)
+{
+ uint32_t val;
+
+ switch(len) {
+ default:
+ case 4:
+ if (address <= 0xfc) {
+ val = le32_to_cpu(*(uint32_t *)(d->config + address));
+ break;
+ }
+ /* fall through */
+ case 2:
+ if (address <= 0xfe) {
+ val = le16_to_cpu(*(uint16_t *)(d->config + address));
+ break;
+ }
+ /* fall through */
+ case 1:
+ val = d->config[address];
+ break;
+ }
+ return val;
+}
+
+void pci_default_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ int can_write, i;
+ uint32_t end, addr;
+
+ if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) ||
+ (address >= 0x30 && address < 0x34))) {
+ PCIIORegion *r;
+ int reg;
+
+ if ( address >= 0x30 ) {
+ reg = PCI_ROM_SLOT;
+ }else{
+ reg = (address - 0x10) >> 2;
+ }
+ r = &d->io_regions[reg];
+ if (r->size == 0)
+ goto default_config;
+ /* compute the stored value */
+ if (reg == PCI_ROM_SLOT) {
+ /* keep ROM enable bit */
+ val &= (~(r->size - 1)) | 1;
+ } else {
+ val &= ~(r->size - 1);
+ val |= r->type;
+ }
+ *(uint32_t *)(d->config + address) = cpu_to_le32(val);
+ pci_update_mappings(d);
+ return;
+ }
+ default_config:
+ /* not efficient, but simple */
+ addr = address;
+ for(i = 0; i < len; i++) {
+ /* default read/write accesses */
+ switch(d->config[0x0e]) {
+ case 0x00:
+ case 0x80:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x10 ... 0x27: /* base */
+ case 0x30 ... 0x33: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ default:
+ case 0x01:
+ switch(addr) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0e:
+ case 0x38 ... 0x3b: /* rom */
+ case 0x3d:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+ break;
+ }
+ break;
+ }
+ if (can_write) {
+ d->config[addr] = val;
+ }
+ if (++addr > 0xff)
+ break;
+ val >>= 8;
+ }
+
+ end = address + len;
+ if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) {
+ /* if the command register is modified, we must modify the mappings */
+ pci_update_mappings(d);
+ }
+}
+
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ int config_addr, bus_num;
+
+#if defined(DEBUG_PCI) && 0
+ printf("pci_data_write: addr=%08x val=%08x len=%d\n",
+ addr, val, len);
+#endif
+ bus_num = (addr >> 16) & 0xff;
+ while (s && s->bus_num != bus_num)
+ s = s->next;
+ if (!s)
+ return;
+ pci_dev = s->devices[(addr >> 8) & 0xff];
+ if (!pci_dev)
+ return;
+ config_addr = addr & 0xff;
+#if defined(DEBUG_PCI)
+ printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n",
+ pci_dev->name, config_addr, val, len);
+#endif
+ pci_dev->config_write(pci_dev, config_addr, val, len);
+}
+
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ int config_addr, bus_num;
+ uint32_t val;
+
+ bus_num = (addr >> 16) & 0xff;
+ while (s && s->bus_num != bus_num)
+ s= s->next;
+ if (!s)
+ goto fail;
+ pci_dev = s->devices[(addr >> 8) & 0xff];
+ if (!pci_dev) {
+ fail:
+ switch(len) {
+ case 1:
+ val = 0xff;
+ break;
+ case 2:
+ val = 0xffff;
+ break;
+ default:
+ case 4:
+ val = 0xffffffff;
+ break;
+ }
+ goto the_end;
+ }
+ config_addr = addr & 0xff;
+ val = pci_dev->config_read(pci_dev, config_addr, len);
+#if defined(DEBUG_PCI)
+ printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n",
+ pci_dev->name, config_addr, val, len);
+#endif
+ the_end:
+#if defined(DEBUG_PCI) && 0
+ printf("pci_data_read: addr=%08x val=%08x len=%d\n",
+ addr, val, len);
+#endif
+ return val;
+}
+
+/***********************************************************/
+/* generic PCI irq support */
+
+/* 0 <= irq_num <= 3. level must be 0 or 1 */
+static void pci_set_irq(void *opaque, int irq_num, int level)
+{
+ PCIDevice *pci_dev = (PCIDevice *)opaque;
+ PCIBus *bus;
+ int change;
+
+ change = level - pci_dev->irq_state[irq_num];
+ if (!change)
+ return;
+
+ pci_dev->irq_state[irq_num] = level;
+ for (;;) {
+ bus = pci_dev->bus;
+ irq_num = bus->map_irq(pci_dev, irq_num);
+ if (bus->set_irq)
+ break;
+ pci_dev = bus->parent_dev;
+ }
+ bus->irq_count[irq_num] += change;
+ bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
+}
+
+/***********************************************************/
+/* monitor info on PCI */
+
+typedef struct {
+ uint16_t class;
+ const char *desc;
+} pci_class_desc;
+
+static pci_class_desc pci_class_descriptions[] =
+{
+ { 0x0100, "SCSI controller"},
+ { 0x0101, "IDE controller"},
+ { 0x0102, "Floppy controller"},
+ { 0x0103, "IPI controller"},
+ { 0x0104, "RAID controller"},
+ { 0x0106, "SATA controller"},
+ { 0x0107, "SAS controller"},
+ { 0x0180, "Storage controller"},
+ { 0x0200, "Ethernet controller"},
+ { 0x0201, "Token Ring controller"},
+ { 0x0202, "FDDI controller"},
+ { 0x0203, "ATM controller"},
+ { 0x0280, "Network controller"},
+ { 0x0300, "VGA controller"},
+ { 0x0301, "XGA controller"},
+ { 0x0302, "3D controller"},
+ { 0x0380, "Display controller"},
+ { 0x0400, "Video controller"},
+ { 0x0401, "Audio controller"},
+ { 0x0402, "Phone"},
+ { 0x0480, "Multimedia controller"},
+ { 0x0500, "RAM controller"},
+ { 0x0501, "Flash controller"},
+ { 0x0580, "Memory controller"},
+ { 0x0600, "Host bridge"},
+ { 0x0601, "ISA bridge"},
+ { 0x0602, "EISA bridge"},
+ { 0x0603, "MC bridge"},
+ { 0x0604, "PCI bridge"},
+ { 0x0605, "PCMCIA bridge"},
+ { 0x0606, "NUBUS bridge"},
+ { 0x0607, "CARDBUS bridge"},
+ { 0x0608, "RACEWAY bridge"},
+ { 0x0680, "Bridge"},
+ { 0x0c03, "USB controller"},
+ { 0, NULL}
+};
+
+static void pci_info_device(PCIDevice *d)
+{
+ int i, class;
+ PCIIORegion *r;
+ pci_class_desc *desc;
+
+ term_printf(" Bus %2d, device %3d, function %d:\n",
+ d->bus->bus_num, d->devfn >> 3, d->devfn & 7);
+ class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE)));
+ term_printf(" ");
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class)
+ desc++;
+ if (desc->desc) {
+ term_printf("%s", desc->desc);
+ } else {
+ term_printf("Class %04x", class);
+ }
+ term_printf(": PCI device %04x:%04x\n",
+ le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))),
+ le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID))));
+
+ if (d->config[PCI_INTERRUPT_PIN] != 0) {
+ term_printf(" IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]);
+ }
+ if (class == 0x0604) {
+ term_printf(" BUS %d.\n", d->config[0x19]);
+ }
+ for(i = 0;i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (r->size != 0) {
+ term_printf(" BAR%d: ", i);
+ if (r->type & PCI_ADDRESS_SPACE_IO) {
+ term_printf("I/O at 0x%04x [0x%04x].\n",
+ r->addr, r->addr + r->size - 1);
+ } else {
+ term_printf("32 bit memory at 0x%08x [0x%08x].\n",
+ r->addr, r->addr + r->size - 1);
+ }
+ }
+ }
+ if (class == 0x0604 && d->config[0x19] != 0) {
+ pci_for_each_device(d->config[0x19], pci_info_device);
+ }
+}
+
+void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d))
+{
+ PCIBus *bus = first_bus;
+ PCIDevice *d;
+ int devfn;
+
+ while (bus && bus->bus_num != bus_num)
+ bus = bus->next;
+ if (bus) {
+ for(devfn = 0; devfn < 256; devfn++) {
+ d = bus->devices[devfn];
+ if (d)
+ fn(d);
+ }
+ }
+}
+
+void pci_info(void)
+{
+ pci_for_each_device(0, pci_info_device);
+}
+
+/* Initialize a PCI NIC. */
+void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
+{
+#if 0
+ if (strcmp(nd->model, "ne2k_pci") == 0) {
+ pci_ne2000_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "i82551") == 0) {
+ pci_i82551_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "i82557b") == 0) {
+ pci_i82557b_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "i82559er") == 0) {
+ pci_i82559er_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "rtl8139") == 0) {
+ pci_rtl8139_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "e1000") == 0) {
+ pci_e1000_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "pcnet") == 0) {
+ pci_pcnet_init(bus, nd, devfn);
+ } else if (strcmp(nd->model, "?") == 0) {
+ fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
+ " ne2k_pci pcnet rtl8139 e1000\n");
+ exit (1);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit (1);
+ }
+#endif
+}
+
+typedef struct {
+ PCIDevice dev;
+ PCIBus *bus;
+} PCIBridge;
+
+static void pci_bridge_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCIBridge *s = (PCIBridge *)d;
+
+ if (address == 0x19 || (address == 0x18 && len > 1)) {
+ if (address == 0x19)
+ s->bus->bus_num = val & 0xff;
+ else
+ s->bus->bus_num = (val >> 8) & 0xff;
+#if defined(DEBUG_PCI)
+ printf ("pci-bridge: %s: Assigned bus %d\n", d->name, s->bus->bus_num);
+#endif
+ }
+ pci_default_write_config(d, address, val, len);
+}
+
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
+ pci_map_irq_fn map_irq, const char *name)
+{
+ PCIBridge *s;
+ s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge),
+ devfn, NULL, pci_bridge_write_config);
+ s->dev.config[0x00] = id >> 16;
+ s->dev.config[0x01] = id >> 24;
+ s->dev.config[0x02] = id; // device_id
+ s->dev.config[0x03] = id >> 8;
+ s->dev.config[0x04] = 0x06; // command = bus master, pci mem
+ s->dev.config[0x05] = 0x00;
+ s->dev.config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
+ s->dev.config[0x07] = 0x00; // status = fast devsel
+ s->dev.config[0x08] = 0x00; // revision
+ s->dev.config[0x09] = 0x00; // programming i/f
+ s->dev.config[0x0A] = 0x04; // class_sub = PCI to PCI bridge
+ s->dev.config[0x0B] = 0x06; // class_base = PCI_bridge
+ s->dev.config[0x0D] = 0x10; // latency_timer
+ s->dev.config[0x0E] = 0x81; // header_type
+ s->dev.config[0x1E] = 0xa0; // secondary status
+
+ s->bus = pci_register_secondary_bus(&s->dev, map_irq);
+ return s->bus;
+}
diff --git a/hw/pci.h b/hw/pci.h
new file mode 100644
index 0000000..e870987
--- /dev/null
+++ b/hw/pci.h
@@ -0,0 +1,142 @@
+#ifndef QEMU_PCI_H
+#define QEMU_PCI_H
+
+/* PCI includes legacy ISA access. */
+#include "isa.h"
+
+/* PCI bus */
+
+extern target_phys_addr_t pci_mem_base;
+
+typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
+ uint32_t address, uint32_t data, int len);
+typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
+ uint32_t address, int len);
+typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type);
+
+#define PCI_ADDRESS_SPACE_MEM 0x00
+#define PCI_ADDRESS_SPACE_IO 0x01
+#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08
+
+typedef struct PCIIORegion {
+ uint32_t addr; /* current PCI mapping address. -1 means not mapped */
+ uint32_t size;
+ uint8_t type;
+ PCIMapIORegionFunc *map_func;
+} PCIIORegion;
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+#define PCI_DEVICES_MAX 64
+
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+#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 */
+
+struct PCIDevice {
+ /* PCI config space */
+ uint8_t config[256];
+
+ /* the following fields are read only */
+ PCIBus *bus;
+ int devfn;
+ char name[64];
+ PCIIORegion io_regions[PCI_NUM_REGIONS];
+
+ /* do not access the following fields */
+ PCIConfigReadFunc *config_read;
+ PCIConfigWriteFunc *config_write;
+ /* ??? This is a PC-specific hack, and should be removed. */
+ int irq_index;
+
+ /* IRQ objects for the INTA-INTD pins. */
+ qemu_irq *irq;
+
+ /* Current IRQ levels. Used internally by the generic PCI code. */
+ int irq_state[4];
+};
+
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write);
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+ uint32_t size, int type,
+ PCIMapIORegionFunc *map_func);
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len);
+void pci_default_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len);
+void pci_device_save(PCIDevice *s, QEMUFile *f);
+int pci_device_load(PCIDevice *s, QEMUFile *f);
+
+typedef void (*pci_set_irq_fn)(qemu_irq *pic, int irq_num, int level);
+typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ qemu_irq *pic, int devfn_min, int nirq);
+
+void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len);
+int pci_bus_num(PCIBus *s);
+void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d));
+
+void pci_info(void);
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
+ pci_map_irq_fn map_irq, const char *name);
+
+/* lsi53c895a.c */
+#define LSI_MAX_DEVS 7
+void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id);
+void *lsi_scsi_init(PCIBus *bus, int devfn);
+
+/* vmware_vga.c */
+void pci_vmsvga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+ unsigned long vga_ram_offset, int vga_ram_size);
+
+/* usb-uhci.c */
+void usb_uhci_piix3_init(PCIBus *bus, int devfn);
+void usb_uhci_piix4_init(PCIBus *bus, int devfn);
+
+/* usb-ohci.c */
+void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn);
+
+/* eepro100.c */
+
+void pci_i82551_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_i82557b_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_i82559er_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* ne2000.c */
+
+void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* rtl8139.c */
+
+void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* e1000.c */
+void pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* pcnet.c */
+void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* prep_pci.c */
+PCIBus *pci_prep_init(qemu_irq *pic);
+
+/* apb_pci.c */
+PCIBus *pci_apb_init(target_phys_addr_t special_base, target_phys_addr_t mem_base,
+ qemu_irq *pic);
+
+#endif
diff --git a/hw/pci_host.h b/hw/pci_host.h
new file mode 100644
index 0000000..49a0c59
--- /dev/null
+++ b/hw/pci_host.h
@@ -0,0 +1,93 @@
+/*
+ * QEMU Common PCI Host bridge configuration data space access routines.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Worker routines for a PCI host controller that uses an {address,data}
+ register pair to access PCI configuration space. */
+
+typedef struct {
+ uint32_t config_reg;
+ PCIBus *bus;
+} PCIHostState;
+
+static void pci_host_data_writeb(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1);
+}
+
+static void pci_host_data_writew(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2);
+}
+
+static void pci_host_data_writel(void* opaque, pci_addr_t addr, uint32_t val)
+{
+ PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg, val, 4);
+}
+
+static uint32_t pci_host_data_readb(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xff;
+ return pci_data_read(s->bus, s->config_reg | (addr & 3), 1);
+}
+
+static uint32_t pci_host_data_readw(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ uint32_t val;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xffff;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t pci_host_data_readl(void* opaque, pci_addr_t addr)
+{
+ PCIHostState *s = opaque;
+ uint32_t val;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xffffffff;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
diff --git a/hw/pcmcia.h b/hw/pcmcia.h
new file mode 100644
index 0000000..bfa23ba
--- /dev/null
+++ b/hw/pcmcia.h
@@ -0,0 +1,50 @@
+/* PCMCIA/Cardbus */
+
+struct pcmcia_socket_s {
+ qemu_irq irq;
+ int attached;
+ const char *slot_string;
+ const char *card_string;
+};
+
+void pcmcia_socket_register(struct pcmcia_socket_s *socket);
+void pcmcia_socket_unregister(struct pcmcia_socket_s *socket);
+void pcmcia_info(void);
+
+struct pcmcia_card_s {
+ void *state;
+ struct pcmcia_socket_s *slot;
+ int (*attach)(void *state);
+ int (*detach)(void *state);
+ const uint8_t *cis;
+ int cis_len;
+
+ /* Only valid if attached */
+ uint8_t (*attr_read)(void *state, uint32_t address);
+ void (*attr_write)(void *state, uint32_t address, uint8_t value);
+ uint16_t (*common_read)(void *state, uint32_t address);
+ void (*common_write)(void *state, uint32_t address, uint16_t value);
+ uint16_t (*io_read)(void *state, uint32_t address);
+ void (*io_write)(void *state, uint32_t address, uint16_t value);
+};
+
+#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */
+#define CISTPL_NO_LINK 0x14 /* No Link Tuple */
+#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */
+#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */
+#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */
+#define CISTPL_CONFIG 0x1a /* Configuration Tuple */
+#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */
+#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */
+#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */
+#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */
+#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */
+#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */
+#define CISTPL_FUNCID 0x21 /* Function ID Tuple */
+#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */
+#define CISTPL_END 0xff /* Tuple End */
+#define CISTPL_ENDMARK 0xff
+
+/* dscm1xxxx.c */
+struct pcmcia_card_s *dscm1xxxx_init(BlockDriverState *bdrv);
+
diff --git a/hw/power_supply.h b/hw/power_supply.h
new file mode 100644
index 0000000..b85edc7
--- /dev/null
+++ b/hw/power_supply.h
@@ -0,0 +1,109 @@
+/*
+ * Universal power supply monitor class
+ *
+ * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
+ * Copyright © 2004 Szabolcs Gyurko
+ * Copyright © 2003 Ian Molton <spyro@f2s.com>
+ *
+ * Modified: 2004, Oct Szabolcs Gyurko
+ *
+ * You may use this code as per GPL version 2
+ */
+
+#ifndef __LINUX_POWER_SUPPLY_H__
+#define __LINUX_POWER_SUPPLY_H__
+
+/*
+ * All voltages, currents, charges, energies, time and temperatures in uV,
+ * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
+ * stated. It's driver's job to convert its raw values to units in which
+ * this class operates.
+ */
+
+/*
+ * For systems where the charger determines the maximum battery capacity
+ * the min and max fields should be used to present these values to user
+ * space. Unused/unknown fields will not appear in sysfs.
+ */
+
+enum {
+ POWER_SUPPLY_STATUS_UNKNOWN = 0,
+ POWER_SUPPLY_STATUS_CHARGING,
+ POWER_SUPPLY_STATUS_DISCHARGING,
+ POWER_SUPPLY_STATUS_NOT_CHARGING,
+ POWER_SUPPLY_STATUS_FULL,
+};
+
+enum {
+ POWER_SUPPLY_HEALTH_UNKNOWN = 0,
+ POWER_SUPPLY_HEALTH_GOOD,
+ POWER_SUPPLY_HEALTH_OVERHEAT,
+ POWER_SUPPLY_HEALTH_DEAD,
+ POWER_SUPPLY_HEALTH_OVERVOLTAGE,
+ POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
+};
+
+enum {
+ POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
+ POWER_SUPPLY_TECHNOLOGY_NiMH,
+ POWER_SUPPLY_TECHNOLOGY_LION,
+ POWER_SUPPLY_TECHNOLOGY_LIPO,
+ POWER_SUPPLY_TECHNOLOGY_LiFe,
+ POWER_SUPPLY_TECHNOLOGY_NiCd,
+};
+
+enum {
+ POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
+ POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
+ POWER_SUPPLY_CAPACITY_LEVEL_LOW,
+ POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
+ POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
+ POWER_SUPPLY_CAPACITY_LEVEL_FULL,
+};
+
+enum power_supply_property {
+ /* Properties of type `int' */
+ POWER_SUPPLY_PROP_STATUS = 0,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_AVG,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_EMPTY,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_ENERGY_AVG,
+ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+ /* Properties of type `const char *' */
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+enum power_supply_type {
+ POWER_SUPPLY_TYPE_BATTERY = 0,
+ POWER_SUPPLY_TYPE_UPS,
+ POWER_SUPPLY_TYPE_MAINS,
+ POWER_SUPPLY_TYPE_USB,
+};
+
+#endif /* __LINUX_POWER_SUPPLY_H__ */
diff --git a/hw/pxa.h b/hw/pxa.h
new file mode 100644
index 0000000..16a68d9
--- /dev/null
+++ b/hw/pxa.h
@@ -0,0 +1,227 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#ifndef PXA_H
+# define PXA_H "pxa.h"
+
+/* Interrupt numbers */
+# define PXA2XX_PIC_SSP3 0
+# define PXA2XX_PIC_USBH2 2
+# define PXA2XX_PIC_USBH1 3
+# define PXA2XX_PIC_KEYPAD 4
+# define PXA2XX_PIC_PWRI2C 6
+# define PXA25X_PIC_HWUART 7
+# define PXA27X_PIC_OST_4_11 7
+# define PXA2XX_PIC_GPIO_0 8
+# define PXA2XX_PIC_GPIO_1 9
+# define PXA2XX_PIC_GPIO_X 10
+# define PXA2XX_PIC_I2S 13
+# define PXA26X_PIC_ASSP 15
+# define PXA25X_PIC_NSSP 16
+# define PXA27X_PIC_SSP2 16
+# define PXA2XX_PIC_LCD 17
+# define PXA2XX_PIC_I2C 18
+# define PXA2XX_PIC_ICP 19
+# define PXA2XX_PIC_STUART 20
+# define PXA2XX_PIC_BTUART 21
+# define PXA2XX_PIC_FFUART 22
+# define PXA2XX_PIC_MMC 23
+# define PXA2XX_PIC_SSP 24
+# define PXA2XX_PIC_DMA 25
+# define PXA2XX_PIC_OST_0 26
+# define PXA2XX_PIC_RTC1HZ 30
+# define PXA2XX_PIC_RTCALARM 31
+
+/* DMA requests */
+# define PXA2XX_RX_RQ_I2S 2
+# define PXA2XX_TX_RQ_I2S 3
+# define PXA2XX_RX_RQ_BTUART 4
+# define PXA2XX_TX_RQ_BTUART 5
+# define PXA2XX_RX_RQ_FFUART 6
+# define PXA2XX_TX_RQ_FFUART 7
+# define PXA2XX_RX_RQ_SSP1 13
+# define PXA2XX_TX_RQ_SSP1 14
+# define PXA2XX_RX_RQ_SSP2 15
+# define PXA2XX_TX_RQ_SSP2 16
+# define PXA2XX_RX_RQ_ICP 17
+# define PXA2XX_TX_RQ_ICP 18
+# define PXA2XX_RX_RQ_STUART 19
+# define PXA2XX_TX_RQ_STUART 20
+# define PXA2XX_RX_RQ_MMCI 21
+# define PXA2XX_TX_RQ_MMCI 22
+# define PXA2XX_USB_RQ(x) ((x) + 24)
+# define PXA2XX_RX_RQ_SSP3 66
+# define PXA2XX_TX_RQ_SSP3 67
+
+# define PXA2XX_SDRAM_BASE 0xa0000000
+# define PXA2XX_INTERNAL_BASE 0x5c000000
+# define PXA2XX_INTERNAL_SIZE 0x40000
+
+/* pxa2xx_pic.c */
+qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env);
+
+/* pxa2xx_timer.c */
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs);
+void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4);
+
+/* pxa2xx_gpio.c */
+struct pxa2xx_gpio_info_s;
+struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base,
+ CPUState *env, qemu_irq *pic, int lines);
+qemu_irq *pxa2xx_gpio_in_get(struct pxa2xx_gpio_info_s *s);
+void pxa2xx_gpio_out_set(struct pxa2xx_gpio_info_s *s,
+ int line, qemu_irq handler);
+void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s, qemu_irq handler);
+
+/* pxa2xx_dma.c */
+struct pxa2xx_dma_state_s;
+struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base,
+ qemu_irq irq);
+struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base,
+ qemu_irq irq);
+void pxa2xx_dma_request(struct pxa2xx_dma_state_s *s, int req_num, int on);
+
+/* pxa2xx_lcd.c */
+struct pxa2xx_lcdc_s;
+struct pxa2xx_lcdc_s *pxa2xx_lcdc_init(target_phys_addr_t base,
+ qemu_irq irq, DisplayState *ds);
+void pxa2xx_lcd_vsync_notifier(struct pxa2xx_lcdc_s *s, qemu_irq handler);
+void pxa2xx_lcdc_oritentation(void *opaque, int angle);
+
+/* pxa2xx_mmci.c */
+struct pxa2xx_mmci_s;
+struct pxa2xx_mmci_s *pxa2xx_mmci_init(target_phys_addr_t base,
+ BlockDriverState *bd, qemu_irq irq, void *dma);
+void pxa2xx_mmci_handlers(struct pxa2xx_mmci_s *s, qemu_irq readonly,
+ qemu_irq coverswitch);
+
+/* pxa2xx_pcmcia.c */
+struct pxa2xx_pcmcia_s;
+struct pxa2xx_pcmcia_s *pxa2xx_pcmcia_init(target_phys_addr_t base);
+int pxa2xx_pcmcia_attach(void *opaque, struct pcmcia_card_s *card);
+int pxa2xx_pcmcia_dettach(void *opaque);
+void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq);
+
+/* pxa2xx_keypad.c */
+struct keymap {
+ int column;
+ int row;
+};
+struct pxa2xx_keypad_s;
+struct pxa2xx_keypad_s *pxa27x_keypad_init(target_phys_addr_t base,
+ qemu_irq irq);
+void pxa27x_register_keypad(struct pxa2xx_keypad_s *kp, struct keymap *map,
+ int size);
+
+/* pxa2xx.c */
+struct pxa2xx_ssp_s;
+void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port,
+ uint32_t (*readfn)(void *opaque),
+ void (*writefn)(void *opaque, uint32_t value), void *opaque);
+
+struct pxa2xx_i2c_s;
+struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base,
+ qemu_irq irq, uint32_t page_size);
+i2c_bus *pxa2xx_i2c_bus(struct pxa2xx_i2c_s *s);
+
+struct pxa2xx_i2s_s;
+struct pxa2xx_fir_s;
+
+struct pxa2xx_state_s {
+ CPUState *env;
+ qemu_irq *pic;
+ qemu_irq reset;
+ struct pxa2xx_dma_state_s *dma;
+ struct pxa2xx_gpio_info_s *gpio;
+ struct pxa2xx_lcdc_s *lcd;
+ struct pxa2xx_ssp_s **ssp;
+ struct pxa2xx_i2c_s *i2c[2];
+ struct pxa2xx_mmci_s *mmc;
+ struct pxa2xx_pcmcia_s *pcmcia[2];
+ struct pxa2xx_i2s_s *i2s;
+ struct pxa2xx_fir_s *fir;
+ struct pxa2xx_keypad_s *kp;
+
+ /* Power management */
+ target_phys_addr_t pm_base;
+ uint32_t pm_regs[0x40];
+
+ /* Clock management */
+ target_phys_addr_t cm_base;
+ uint32_t cm_regs[4];
+ uint32_t clkcfg;
+
+ /* Memory management */
+ target_phys_addr_t mm_base;
+ uint32_t mm_regs[0x1a];
+
+ /* Performance monitoring */
+ uint32_t pmnc;
+
+ /* Real-Time clock */
+ target_phys_addr_t rtc_base;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t rdar1;
+ uint32_t rdar2;
+ uint32_t ryar1;
+ uint32_t ryar2;
+ uint32_t swar1;
+ uint32_t swar2;
+ uint32_t piar;
+ uint32_t last_rcnr;
+ uint32_t last_rdcr;
+ uint32_t last_rycr;
+ uint32_t last_swcr;
+ uint32_t last_rtcpicr;
+ int64_t last_hz;
+ int64_t last_sw;
+ int64_t last_pi;
+ QEMUTimer *rtc_hz;
+ QEMUTimer *rtc_rdal1;
+ QEMUTimer *rtc_rdal2;
+ QEMUTimer *rtc_swal1;
+ QEMUTimer *rtc_swal2;
+ QEMUTimer *rtc_pi;
+};
+
+struct pxa2xx_i2s_s {
+ target_phys_addr_t base;
+ qemu_irq irq;
+ struct pxa2xx_dma_state_s *dma;
+ void (*data_req)(void *, int, int);
+
+ uint32_t control[2];
+ uint32_t status;
+ uint32_t mask;
+ uint32_t clk;
+
+ int enable;
+ int rx_len;
+ int tx_len;
+ void (*codec_out)(void *, uint32_t);
+ uint32_t (*codec_in)(void *);
+ void *opaque;
+
+ int fifo_len;
+ uint32_t fifo[16];
+};
+
+# define PA_FMT "0x%08lx"
+# define REG_FMT "0x" TARGET_FMT_plx
+
+struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, DisplayState *ds,
+ const char *revision);
+struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size, DisplayState *ds);
+
+/* usb-ohci.c */
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+ qemu_irq irq);
+
+#endif /* PXA_H */
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
new file mode 100644
index 0000000..16b3215
--- /dev/null
+++ b/hw/scsi-disk.c
@@ -0,0 +1,809 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Note that this file only handles the SCSI architecture model and device
+ * commands. Emulation of interface/link layer protocols is handled by
+ * the host adapter emulator.
+ */
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, args...) \
+do { printf("scsi-disk: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
+
+#include "qemu-common.h"
+#include "block.h"
+#include "scsi-disk.h"
+
+#define SENSE_NO_SENSE 0
+#define SENSE_NOT_READY 2
+#define SENSE_HARDWARE_ERROR 4
+#define SENSE_ILLEGAL_REQUEST 5
+
+#define SCSI_DMA_BUF_SIZE 65536
+
+typedef struct SCSIRequest {
+ SCSIDeviceState *dev;
+ uint32_t tag;
+ /* ??? We should probably keep track of whether the data trasfer is
+ a read or a write. Currently we rely on the host getting it right. */
+ /* Both sector and sector_count are in terms of qemu 512 byte blocks. */
+ int sector;
+ int sector_count;
+ /* The amounnt of data in the buffer. */
+ int buf_len;
+ uint8_t *dma_buf;
+ BlockDriverAIOCB *aiocb;
+ struct SCSIRequest *next;
+} SCSIRequest;
+
+struct SCSIDeviceState
+{
+ BlockDriverState *bdrv;
+ SCSIRequest *requests;
+ /* The qemu block layer uses a fixed 512 byte sector size.
+ This is the number of 512 byte blocks in a single scsi sector. */
+ int cluster_size;
+ int sense;
+ int tcq;
+ /* Completion functions may be called from either scsi_{read,write}_data
+ or from the AIO completion routines. */
+ scsi_completionfn completion;
+ void *opaque;
+};
+
+/* Global pool of SCSIRequest structures. */
+static SCSIRequest *free_requests = NULL;
+
+static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+{
+ SCSIRequest *r;
+
+ if (free_requests) {
+ r = free_requests;
+ free_requests = r->next;
+ } else {
+ r = qemu_malloc(sizeof(SCSIRequest));
+ r->dma_buf = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
+ }
+ r->dev = s;
+ r->tag = tag;
+ r->sector_count = 0;
+ r->buf_len = 0;
+ r->aiocb = NULL;
+
+ r->next = s->requests;
+ s->requests = r;
+ return r;
+}
+
+static void scsi_remove_request(SCSIRequest *r)
+{
+ SCSIRequest *last;
+ SCSIDeviceState *s = r->dev;
+
+ if (s->requests == r) {
+ s->requests = r->next;
+ } else {
+ last = s->requests;
+ while (last && last->next != r)
+ last = last->next;
+ if (last) {
+ last->next = r->next;
+ } else {
+ BADF("Orphaned request\n");
+ }
+ }
+ r->next = free_requests;
+ free_requests = r;
+}
+
+static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+{
+ SCSIRequest *r;
+
+ r = s->requests;
+ while (r && r->tag != tag)
+ r = r->next;
+
+ return r;
+}
+
+/* Helper function for command completion. */
+static void scsi_command_complete(SCSIRequest *r, int sense)
+{
+ SCSIDeviceState *s = r->dev;
+ uint32_t tag;
+ DPRINTF("Command complete tag=0x%x sense=%d\n", r->tag, sense);
+ s->sense = sense;
+ tag = r->tag;
+ scsi_remove_request(r);
+ s->completion(s->opaque, SCSI_REASON_DONE, tag, sense);
+}
+
+/* Cancel a pending data transfer. */
+static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDeviceState *s = d->state;
+ SCSIRequest *r;
+ DPRINTF("Cancel tag=0x%x\n", tag);
+ r = scsi_find_request(s, tag);
+ if (r) {
+ if (r->aiocb)
+ bdrv_aio_cancel(r->aiocb);
+ r->aiocb = NULL;
+ scsi_remove_request(r);
+ }
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+ SCSIRequest *r = (SCSIRequest *)opaque;
+ SCSIDeviceState *s = r->dev;
+
+ if (ret) {
+ DPRINTF("IO error\n");
+ scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ return;
+ }
+ DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, r->buf_len);
+
+ s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+}
+
+/* Read more data from scsi device into buffer. */
+static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDeviceState *s = d->state;
+ SCSIRequest *r;
+ uint32_t n;
+
+ r = scsi_find_request(s, tag);
+ if (!r) {
+ BADF("Bad read tag 0x%x\n", tag);
+ /* ??? This is the wrong error. */
+ scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ return;
+ }
+ if (r->sector_count == (uint32_t)-1) {
+ DPRINTF("Read buf_len=%d\n", r->buf_len);
+ r->sector_count = 0;
+ s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+ return;
+ }
+ DPRINTF("Read sector_count=%d\n", r->sector_count);
+ if (r->sector_count == 0) {
+ scsi_command_complete(r, SENSE_NO_SENSE);
+ return;
+ }
+
+ n = r->sector_count;
+ if (n > SCSI_DMA_BUF_SIZE / 512)
+ n = SCSI_DMA_BUF_SIZE / 512;
+
+ r->buf_len = n * 512;
+ r->aiocb = bdrv_aio_read(s->bdrv, r->sector, r->dma_buf, n,
+ scsi_read_complete, r);
+ if (r->aiocb == NULL)
+ scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ r->sector += n;
+ r->sector_count -= n;
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIRequest *r = (SCSIRequest *)opaque;
+ SCSIDeviceState *s = r->dev;
+ uint32_t len;
+
+ if (ret) {
+ fprintf(stderr, "scsi-disc: IO write error\n");
+ exit(1);
+ }
+
+ r->aiocb = NULL;
+ if (r->sector_count == 0) {
+ scsi_command_complete(r, SENSE_NO_SENSE);
+ } else {
+ len = r->sector_count * 512;
+ if (len > SCSI_DMA_BUF_SIZE) {
+ len = SCSI_DMA_BUF_SIZE;
+ }
+ r->buf_len = len;
+ DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
+ s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+ }
+}
+
+/* Write data to a scsi device. Returns nonzero on failure.
+ The transfer may complete asynchronously. */
+static int scsi_write_data(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDeviceState *s = d->state;
+ SCSIRequest *r;
+ uint32_t n;
+
+ DPRINTF("Write data tag=0x%x\n", tag);
+ r = scsi_find_request(s, tag);
+ if (!r) {
+ BADF("Bad write tag 0x%x\n", tag);
+ scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ return 1;
+ }
+ if (r->aiocb)
+ BADF("Data transfer already in progress\n");
+ n = r->buf_len / 512;
+ if (n) {
+ r->aiocb = bdrv_aio_write(s->bdrv, r->sector, r->dma_buf, n,
+ scsi_write_complete, r);
+ if (r->aiocb == NULL)
+ scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+ r->sector += n;
+ r->sector_count -= n;
+ } else {
+ /* Invoke completion routine to fetch data from host. */
+ scsi_write_complete(r, 0);
+ }
+
+ return 0;
+}
+
+/* Return a pointer to the data buffer. */
+static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDeviceState *s = d->state;
+ SCSIRequest *r;
+
+ r = scsi_find_request(s, tag);
+ if (!r) {
+ BADF("Bad buffer tag 0x%x\n", tag);
+ return NULL;
+ }
+ return r->dma_buf;
+}
+
+/* Execute a scsi command. Returns the length of the data expected by the
+ command. This will be Positive for data transfers from the device
+ (eg. disk reads), negative for transfers to the device (eg. disk writes),
+ and zero if the command does not transfer any data. */
+
+static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
+ uint8_t *buf, int lun)
+{
+ SCSIDeviceState *s = d->state;
+ uint64_t nb_sectors;
+ uint32_t lba;
+ uint32_t len;
+ int cmdlen;
+ int is_write;
+ uint8_t command;
+ uint8_t *outbuf;
+ SCSIRequest *r;
+
+ command = buf[0];
+ r = scsi_find_request(s, tag);
+ if (r) {
+ BADF("Tag 0x%x already in use\n", tag);
+ scsi_cancel_io(d, tag);
+ }
+ /* ??? Tags are not unique for different luns. We only implement a
+ single lun, so this should not matter. */
+ r = scsi_new_request(s, tag);
+ outbuf = r->dma_buf;
+ is_write = 0;
+ DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+ switch (command >> 5) {
+ case 0:
+ lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
+ len = buf[4];
+ cmdlen = 6;
+ break;
+ case 1:
+ case 2:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[8] | (buf[7] << 8);
+ cmdlen = 10;
+ break;
+ case 4:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
+ cmdlen = 16;
+ break;
+ case 5:
+ lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+ len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
+ cmdlen = 12;
+ break;
+ default:
+ BADF("Unsupported command length, command %x\n", command);
+ goto fail;
+ }
+#ifdef DEBUG_SCSI
+ {
+ int i;
+ for (i = 1; i < cmdlen; i++) {
+ printf(" 0x%02x", buf[i]);
+ }
+ printf("\n");
+ }
+#endif
+ if (lun || buf[1] >> 5) {
+ /* Only LUN 0 supported. */
+ DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+ goto fail;
+ }
+ switch (command) {
+ case 0x0:
+ DPRINTF("Test Unit Ready\n");
+ break;
+ case 0x03:
+ DPRINTF("Request Sense (len %d)\n", len);
+ if (len < 4)
+ goto fail;
+ memset(outbuf, 0, 4);
+ outbuf[0] = 0xf0;
+ outbuf[1] = 0;
+ outbuf[2] = s->sense;
+ r->buf_len = 4;
+ break;
+ case 0x12:
+ DPRINTF("Inquiry (len %d)\n", len);
+ if (buf[1] & 0x2) {
+ /* Command support data - optional, not implemented */
+ BADF("optional INQUIRY command support request not implemented\n");
+ goto fail;
+ }
+ else if (buf[1] & 0x1) {
+ /* Vital product data */
+ uint8_t page_code = buf[2];
+ if (len < 4) {
+ BADF("Error: Inquiry (EVPD[%02X]) buffer size %d is "
+ "less than 4\n", page_code, len);
+ goto fail;
+ }
+
+ switch (page_code) {
+ case 0x00:
+ {
+ /* Supported page codes, mandatory */
+ DPRINTF("Inquiry EVPD[Supported pages] "
+ "buffer size %d\n", len);
+
+ r->buf_len = 0;
+
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->buf_len++] = 5;
+ } else {
+ outbuf[r->buf_len++] = 0;
+ }
+
+ outbuf[r->buf_len++] = 0x00; // this page
+ outbuf[r->buf_len++] = 0x00;
+ outbuf[r->buf_len++] = 3; // number of pages
+ outbuf[r->buf_len++] = 0x00; // list of supported pages (this page)
+ outbuf[r->buf_len++] = 0x80; // unit serial number
+ outbuf[r->buf_len++] = 0x83; // device identification
+ }
+ break;
+ case 0x80:
+ {
+ /* Device serial number, optional */
+ if (len < 4) {
+ BADF("Error: EVPD[Serial number] Inquiry buffer "
+ "size %d too small, %d needed\n", len, 4);
+ goto fail;
+ }
+
+ DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len);
+
+ r->buf_len = 0;
+
+ /* Supported page codes */
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->buf_len++] = 5;
+ } else {
+ outbuf[r->buf_len++] = 0;
+ }
+
+ outbuf[r->buf_len++] = 0x80; // this page
+ outbuf[r->buf_len++] = 0x00;
+ outbuf[r->buf_len++] = 0x01; // 1 byte data follow
+
+ outbuf[r->buf_len++] = '0'; // 1 byte data follow
+ }
+
+ break;
+ case 0x83:
+ {
+ /* Device identification page, mandatory */
+ int max_len = 255 - 8;
+ int id_len = strlen(bdrv_get_device_name(s->bdrv));
+ if (id_len > max_len)
+ id_len = max_len;
+
+ DPRINTF("Inquiry EVPD[Device identification] "
+ "buffer size %d\n", len);
+ r->buf_len = 0;
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[r->buf_len++] = 5;
+ } else {
+ outbuf[r->buf_len++] = 0;
+ }
+
+ outbuf[r->buf_len++] = 0x83; // this page
+ outbuf[r->buf_len++] = 0x00;
+ outbuf[r->buf_len++] = 3 + id_len;
+
+ outbuf[r->buf_len++] = 0x2; // ASCII
+ outbuf[r->buf_len++] = 0; // not officially assigned
+ outbuf[r->buf_len++] = 0; // reserved
+ outbuf[r->buf_len++] = id_len; // length of data following
+
+ memcpy(&outbuf[r->buf_len],
+ bdrv_get_device_name(s->bdrv), id_len);
+ r->buf_len += id_len;
+ }
+ break;
+ default:
+ BADF("Error: unsupported Inquiry (EVPD[%02X]) "
+ "buffer size %d\n", page_code, len);
+ goto fail;
+ }
+ /* done with EVPD */
+ break;
+ }
+ else {
+ /* Standard INQUIRY data */
+ if (buf[2] != 0) {
+ BADF("Error: Inquiry (STANDARD) page or code "
+ "is non-zero [%02X]\n", buf[2]);
+ goto fail;
+ }
+
+ /* PAGE CODE == 0 */
+ if (len < 5) {
+ BADF("Error: Inquiry (STANDARD) buffer size %d "
+ "is less than 5\n", len);
+ goto fail;
+ }
+
+ if (len < 36) {
+ BADF("Error: Inquiry (STANDARD) buffer size %d "
+ "is less than 36 (TODO: only 5 required)\n", len);
+ }
+ }
+ memset(outbuf, 0, 36);
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[0] = 5;
+ outbuf[1] = 0x80;
+ memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
+ } else {
+ outbuf[0] = 0;
+ memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
+ }
+ memcpy(&outbuf[8], "QEMU ", 8);
+ memcpy(&outbuf[32], QEMU_VERSION, 4);
+ /* Identify device as SCSI-3 rev 1.
+ Some later commands are also implemented. */
+ outbuf[2] = 3;
+ outbuf[3] = 2; /* Format 2 */
+ outbuf[4] = 31;
+ /* Sync data transfer and TCQ. */
+ outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
+ r->buf_len = 36;
+ break;
+ case 0x16:
+ DPRINTF("Reserve(6)\n");
+ if (buf[1] & 1)
+ goto fail;
+ break;
+ case 0x17:
+ DPRINTF("Release(6)\n");
+ if (buf[1] & 1)
+ goto fail;
+ break;
+ case 0x1a:
+ case 0x5a:
+ {
+ uint8_t *p;
+ int page;
+
+ page = buf[2] & 0x3f;
+ DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
+ p = outbuf;
+ memset(p, 0, 4);
+ outbuf[1] = 0; /* Default media type. */
+ outbuf[3] = 0; /* Block descriptor length. */
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ outbuf[2] = 0x80; /* Readonly. */
+ }
+ p += 4;
+ if (page == 4) {
+ int cylinders, heads, secs;
+
+ /* Rigid disk device geometry page. */
+ p[0] = 4;
+ p[1] = 0x16;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+ p[2] = (cylinders >> 16) & 0xff;
+ p[3] = (cylinders >> 8) & 0xff;
+ p[4] = cylinders & 0xff;
+ p[5] = heads & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[6] = (cylinders >> 16) & 0xff;
+ p[7] = (cylinders >> 8) & 0xff;
+ p[8] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[9] = (cylinders >> 16) & 0xff;
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Device step rate [ns], 200ns */
+ p[12] = 0;
+ p[13] = 200;
+ /* Landing zone cylinder */
+ p[14] = 0xff;
+ p[15] = 0xff;
+ p[16] = 0xff;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[20] = (5400 >> 8) & 0xff;
+ p[21] = 5400 & 0xff;
+ p += 0x16;
+ } else if (page == 5) {
+ int cylinders, heads, secs;
+
+ /* Flexible disk device geometry page. */
+ p[0] = 5;
+ p[1] = 0x1e;
+ /* Transfer rate [kbit/s], 5Mbit/s */
+ p[2] = 5000 >> 8;
+ p[3] = 5000 & 0xff;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+ p[4] = heads & 0xff;
+ p[5] = secs & 0xff;
+ p[6] = s->cluster_size * 2;
+ p[8] = (cylinders >> 8) & 0xff;
+ p[9] = cylinders & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[12] = (cylinders >> 8) & 0xff;
+ p[13] = cylinders & 0xff;
+ /* Device step rate [100us], 100us */
+ p[14] = 0;
+ p[15] = 1;
+ /* Device step pulse width [us], 1us */
+ p[16] = 1;
+ /* Device head settle delay [100us], 100us */
+ p[17] = 0;
+ p[18] = 1;
+ /* Motor on delay [0.1s], 0.1s */
+ p[19] = 1;
+ /* Motor off delay [0.1s], 0.1s */
+ p[20] = 1;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[28] = (5400 >> 8) & 0xff;
+ p[29] = 5400 & 0xff;
+ p += 0x1e;
+ } else if ((page == 8 || page == 0x3f)) {
+ /* Caching page. */
+ memset(p,0,20);
+ p[0] = 8;
+ p[1] = 0x12;
+ p[2] = 4; /* WCE */
+ p += 20;
+ }
+ if ((page == 0x3f || page == 0x2a)
+ && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+ /* CD Capabilities and Mechanical Status page. */
+ p[0] = 0x2a;
+ p[1] = 0x14;
+ p[2] = 3; // CD-R & CD-RW read
+ p[3] = 0; // Writing not supported
+ p[4] = 0x7f; /* Audio, composite, digital out,
+ mode 2 form 1&2, multi session */
+ p[5] = 0xff; /* CD DA, DA accurate, RW supported,
+ RW corrected, C2 errors, ISRC,
+ UPC, Bar code */
+ p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+ /* Locking supported, jumper present, eject, tray */
+ p[7] = 0; /* no volume & mute control, no
+ changer */
+ p[8] = (50 * 176) >> 8; // 50x read speed
+ p[9] = (50 * 176) & 0xff;
+ p[10] = 0 >> 8; // No volume
+ p[11] = 0 & 0xff;
+ p[12] = 2048 >> 8; // 2M buffer
+ p[13] = 2048 & 0xff;
+ p[14] = (16 * 176) >> 8; // 16x read speed current
+ p[15] = (16 * 176) & 0xff;
+ p[18] = (16 * 176) >> 8; // 16x write speed
+ p[19] = (16 * 176) & 0xff;
+ p[20] = (16 * 176) >> 8; // 16x write speed current
+ p[21] = (16 * 176) & 0xff;
+ p += 22;
+ }
+ r->buf_len = p - outbuf;
+ outbuf[0] = r->buf_len - 4;
+ if (r->buf_len > len)
+ r->buf_len = len;
+ }
+ break;
+ case 0x1b:
+ DPRINTF("Start Stop Unit\n");
+ break;
+ case 0x1e:
+ DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
+ bdrv_set_locked(s->bdrv, buf[4] & 1);
+ break;
+ case 0x25:
+ DPRINTF("Read Capacity\n");
+ /* The normal LEN field for this command is zero. */
+ memset(outbuf, 0, 8);
+ bdrv_get_geometry(s->bdrv, &nb_sectors);
+ /* Returned value is the address of the last sector. */
+ if (nb_sectors) {
+ nb_sectors--;
+ outbuf[0] = (nb_sectors >> 24) & 0xff;
+ outbuf[1] = (nb_sectors >> 16) & 0xff;
+ outbuf[2] = (nb_sectors >> 8) & 0xff;
+ outbuf[3] = nb_sectors & 0xff;
+ outbuf[4] = 0;
+ outbuf[5] = 0;
+ outbuf[6] = s->cluster_size * 2;
+ outbuf[7] = 0;
+ r->buf_len = 8;
+ } else {
+ scsi_command_complete(r, SENSE_NOT_READY);
+ return 0;
+ }
+ break;
+ case 0x08:
+ case 0x28:
+ DPRINTF("Read (sector %d, count %d)\n", lba, len);
+ r->sector = lba * s->cluster_size;
+ r->sector_count = len * s->cluster_size;
+ break;
+ case 0x0a:
+ case 0x2a:
+ DPRINTF("Write (sector %d, count %d)\n", lba, len);
+ r->sector = lba * s->cluster_size;
+ r->sector_count = len * s->cluster_size;
+ is_write = 1;
+ break;
+ case 0x35:
+ DPRINTF("Synchronise cache (sector %d, count %d)\n", lba, len);
+ bdrv_flush(s->bdrv);
+ break;
+ case 0x43:
+ {
+ int start_track, format, msf, toclen;
+
+ msf = buf[1] & 2;
+ format = buf[2] & 0xf;
+ start_track = buf[6];
+ bdrv_get_geometry(s->bdrv, &nb_sectors);
+ DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+ switch(format) {
+ case 0:
+ toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
+ break;
+ case 1:
+ /* multi session : only a single session defined */
+ toclen = 12;
+ memset(outbuf, 0, 12);
+ outbuf[1] = 0x0a;
+ outbuf[2] = 0x01;
+ outbuf[3] = 0x01;
+ break;
+ case 2:
+ toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
+ break;
+ default:
+ goto error_cmd;
+ }
+ if (toclen > 0) {
+ if (len > toclen)
+ len = toclen;
+ r->buf_len = len;
+ break;
+ }
+ error_cmd:
+ DPRINTF("Read TOC error\n");
+ goto fail;
+ }
+ case 0x46:
+ DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
+ memset(outbuf, 0, 8);
+ /* ??? This should probably return much more information. For now
+ just return the basic header indicating the CD-ROM profile. */
+ outbuf[7] = 8; // CD-ROM
+ r->buf_len = 8;
+ break;
+ case 0x56:
+ DPRINTF("Reserve(10)\n");
+ if (buf[1] & 3)
+ goto fail;
+ break;
+ case 0x57:
+ DPRINTF("Release(10)\n");
+ if (buf[1] & 3)
+ goto fail;
+ break;
+ case 0xa0:
+ DPRINTF("Report LUNs (len %d)\n", len);
+ if (len < 16)
+ goto fail;
+ memset(outbuf, 0, 16);
+ outbuf[3] = 8;
+ r->buf_len = 16;
+ break;
+ default:
+ DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+ fail:
+ scsi_command_complete(r, SENSE_ILLEGAL_REQUEST);
+ return 0;
+ }
+ if (r->sector_count == 0 && r->buf_len == 0) {
+ scsi_command_complete(r, SENSE_NO_SENSE);
+ }
+ len = r->sector_count * 512 + r->buf_len;
+ if (is_write) {
+ return -len;
+ } else {
+ if (!r->sector_count)
+ r->sector_count = -1;
+ return len;
+ }
+}
+
+static void scsi_destroy(SCSIDevice *d)
+{
+ qemu_free(d->state);
+ qemu_free(d);
+}
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
+ scsi_completionfn completion, void *opaque)
+{
+ SCSIDevice *d;
+ SCSIDeviceState *s;
+
+ s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
+ s->bdrv = bdrv;
+ s->tcq = tcq;
+ s->completion = completion;
+ s->opaque = opaque;
+ if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ s->cluster_size = 4;
+ } else {
+ s->cluster_size = 1;
+ }
+
+ d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
+ d->state = s;
+ d->destroy = scsi_destroy;
+ d->send_command = scsi_send_command;
+ d->read_data = scsi_read_data;
+ d->write_data = scsi_write_data;
+ d->cancel_io = scsi_cancel_io;
+ d->get_buf = scsi_get_buf;
+
+ return d;
+}
diff --git a/hw/scsi-disk.h b/hw/scsi-disk.h
new file mode 100644
index 0000000..f42212b
--- /dev/null
+++ b/hw/scsi-disk.h
@@ -0,0 +1,36 @@
+#ifndef SCSI_DISK_H
+#define SCSI_DISK_H
+
+/* scsi-disk.c */
+enum scsi_reason {
+ SCSI_REASON_DONE, /* Command complete. */
+ SCSI_REASON_DATA /* Transfer complete, more data required. */
+};
+
+typedef struct SCSIDeviceState SCSIDeviceState;
+typedef struct SCSIDevice SCSIDevice;
+typedef void (*scsi_completionfn)(void *opaque, int reason, uint32_t tag,
+ uint32_t arg);
+
+struct SCSIDevice
+{
+ SCSIDeviceState *state;
+ void (*destroy)(SCSIDevice *s);
+ int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
+ int lun);
+ void (*read_data)(SCSIDevice *s, uint32_t tag);
+ int (*write_data)(SCSIDevice *s, uint32_t tag);
+ void (*cancel_io)(SCSIDevice *s, uint32_t tag);
+ uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
+};
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
+ scsi_completionfn completion, void *opaque);
+SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
+ scsi_completionfn completion, void *opaque);
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+#endif
diff --git a/hw/sd.h b/hw/sd.h
new file mode 100644
index 0000000..f310062
--- /dev/null
+++ b/hw/sd.h
@@ -0,0 +1,83 @@
+/*
+ * include/linux/mmc/sd.h
+ *
+ * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+ *
+ * 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 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef MMC_SD_H
+#define MMC_SD_H
+
+/* SD commands type argument response */
+ /* class 0 */
+/* This is basically the same command as for MMC with some quirks. */
+#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
+#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
+
+ /* class 10 */
+#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
+
+ /* Application commands */
+#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
+#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
+#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
+#define SD_APP_SEND_SCR 51 /* adtc R1 */
+
+/*
+ * SD_SWITCH argument format:
+ *
+ * [31] Check (0) or switch (1)
+ * [30:24] Reserved (0)
+ * [23:20] Function group 6
+ * [19:16] Function group 5
+ * [15:12] Function group 4
+ * [11:8] Function group 3
+ * [7:4] Function group 2
+ * [3:0] Function group 1
+ */
+
+/*
+ * SD_SEND_IF_COND argument format:
+ *
+ * [31:12] Reserved (0)
+ * [11:8] Host Voltage Supply Flags
+ * [7:0] Check Pattern (0xAA)
+ */
+
+/*
+ * SCR field definitions
+ */
+
+#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
+#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
+#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
+
+/*
+ * SD bus widths
+ */
+#define SD_BUS_WIDTH_1 0
+#define SD_BUS_WIDTH_4 2
+
+/*
+ * SD_SWITCH mode
+ */
+#define SD_SWITCH_CHECK 0
+#define SD_SWITCH_SET 1
+
+/*
+ * SD_SWITCH function groups
+ */
+#define SD_SWITCH_GRP_ACCESS 0
+
+/*
+ * SD_SWITCH access modes
+ */
+#define SD_SWITCH_ACCESS_DEF 0
+#define SD_SWITCH_ACCESS_HS 1
+
+#endif
+
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
new file mode 100644
index 0000000..410051d
--- /dev/null
+++ b/hw/smc91c111.c
@@ -0,0 +1,715 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "hw.h"
+#include "net.h"
+#include "devices.h"
+/* For crc32 */
+#include <zlib.h>
+
+/* Number of 2k memory pages available. */
+#define NUM_PACKETS 4
+
+typedef struct {
+ uint32_t base;
+ VLANClientState *vc;
+ uint16_t tcr;
+ uint16_t rcr;
+ uint16_t cr;
+ uint16_t ctr;
+ uint16_t gpr;
+ uint16_t ptr;
+ uint16_t ercv;
+ qemu_irq irq;
+ int bank;
+ int packet_num;
+ int tx_alloc;
+ /* Bitmask of allocated packets. */
+ int allocated;
+ int tx_fifo_len;
+ int tx_fifo[NUM_PACKETS];
+ int rx_fifo_len;
+ int rx_fifo[NUM_PACKETS];
+ int tx_fifo_done_len;
+ int tx_fifo_done[NUM_PACKETS];
+ /* Packet buffer memory. */
+ uint8_t data[NUM_PACKETS][2048];
+ uint8_t int_level;
+ uint8_t int_mask;
+ uint8_t macaddr[6];
+} smc91c111_state;
+
+#define RCR_SOFT_RST 0x8000
+#define RCR_STRIP_CRC 0x0200
+#define RCR_RXEN 0x0100
+
+#define TCR_EPH_LOOP 0x2000
+#define TCR_NOCRC 0x0100
+#define TCR_PAD_EN 0x0080
+#define TCR_FORCOL 0x0004
+#define TCR_LOOP 0x0002
+#define TCR_TXEN 0x0001
+
+#define INT_MD 0x80
+#define INT_ERCV 0x40
+#define INT_EPH 0x20
+#define INT_RX_OVRN 0x10
+#define INT_ALLOC 0x08
+#define INT_TX_EMPTY 0x04
+#define INT_TX 0x02
+#define INT_RCV 0x01
+
+#define CTR_AUTO_RELEASE 0x0800
+#define CTR_RELOAD 0x0002
+#define CTR_STORE 0x0001
+
+#define RS_ALGNERR 0x8000
+#define RS_BRODCAST 0x4000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+
+/* Update interrupt status. */
+static void smc91c111_update(smc91c111_state *s)
+{
+ int level;
+
+ if (s->tx_fifo_len == 0)
+ s->int_level |= INT_TX_EMPTY;
+ if (s->tx_fifo_done_len != 0)
+ s->int_level |= INT_TX;
+ level = (s->int_level & s->int_mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+/* Try to allocate a packet. Returns 0x80 on failure. */
+static int smc91c111_allocate_packet(smc91c111_state *s)
+{
+ int i;
+ if (s->allocated == (1 << NUM_PACKETS) - 1) {
+ return 0x80;
+ }
+
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if ((s->allocated & (1 << i)) == 0)
+ break;
+ }
+ s->allocated |= 1 << i;
+ return i;
+}
+
+
+/* Process a pending TX allocate. */
+static void smc91c111_tx_alloc(smc91c111_state *s)
+{
+ s->tx_alloc = smc91c111_allocate_packet(s);
+ if (s->tx_alloc == 0x80)
+ return;
+ s->int_level |= INT_ALLOC;
+ smc91c111_update(s);
+}
+
+/* Remove and item from the RX FIFO. */
+static void smc91c111_pop_rx_fifo(smc91c111_state *s)
+{
+ int i;
+
+ s->rx_fifo_len--;
+ if (s->rx_fifo_len) {
+ for (i = 0; i < s->rx_fifo_len; i++)
+ s->rx_fifo[i] = s->rx_fifo[i + 1];
+ s->int_level |= INT_RCV;
+ } else {
+ s->int_level &= ~INT_RCV;
+ }
+ smc91c111_update(s);
+}
+
+/* Remove an item from the TX completion FIFO. */
+static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
+{
+ int i;
+
+ if (s->tx_fifo_done_len == 0)
+ return;
+ s->tx_fifo_done_len--;
+ for (i = 0; i < s->tx_fifo_done_len; i++)
+ s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
+}
+
+/* Release the memory allocated to a packet. */
+static void smc91c111_release_packet(smc91c111_state *s, int packet)
+{
+ s->allocated &= ~(1 << packet);
+ if (s->tx_alloc == 0x80)
+ smc91c111_tx_alloc(s);
+}
+
+/* Flush the TX FIFO. */
+static void smc91c111_do_tx(smc91c111_state *s)
+{
+ int i;
+ int len;
+ int control;
+ int add_crc;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->tcr & TCR_TXEN) == 0)
+ return;
+ if (s->tx_fifo_len == 0)
+ return;
+ for (i = 0; i < s->tx_fifo_len; i++) {
+ packetnum = s->tx_fifo[i];
+ p = &s->data[packetnum][0];
+ /* Set status word. */
+ *(p++) = 0x01;
+ *(p++) = 0x40;
+ len = *(p++);
+ len |= ((int)*(p++)) << 8;
+ len -= 6;
+ control = p[len + 1];
+ if (control & 0x20)
+ len++;
+ /* ??? This overwrites the data following the buffer.
+ Don't know what real hardware does. */
+ if (len < 64 && (s->tcr & TCR_PAD_EN)) {
+ memset(p + len, 0, 64 - len);
+ len = 64;
+ }
+#if 0
+ /* The card is supposed to append the CRC to the frame. However
+ none of the other network traffic has the CRC appended.
+ Suspect this is low level ethernet detail we don't need to worry
+ about. */
+ add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
+ if (add_crc) {
+ uint32_t crc;
+
+ crc = crc32(~0, p, len);
+ memcpy(p + len, &crc, 4);
+ len += 4;
+ }
+#else
+ add_crc = 0;
+#endif
+ if (s->ctr & CTR_AUTO_RELEASE)
+ /* Race? */
+ smc91c111_release_packet(s, packetnum);
+ else if (s->tx_fifo_done_len < NUM_PACKETS)
+ s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
+ qemu_send_packet(s->vc, p, len);
+ }
+ s->tx_fifo_len = 0;
+ smc91c111_update(s);
+}
+
+/* Add a packet to the TX FIFO. */
+static void smc91c111_queue_tx(smc91c111_state *s, int packet)
+{
+ if (s->tx_fifo_len == NUM_PACKETS)
+ return;
+ s->tx_fifo[s->tx_fifo_len++] = packet;
+ smc91c111_do_tx(s);
+}
+
+static void smc91c111_reset(smc91c111_state *s)
+{
+ s->bank = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->allocated = 0;
+ s->packet_num = 0;
+ s->tx_alloc = 0;
+ s->tcr = 0;
+ s->rcr = 0;
+ s->cr = 0xa0b1;
+ s->ctr = 0x1210;
+ s->ptr = 0;
+ s->ercv = 0x1f;
+ s->int_level = INT_TX_EMPTY;
+ s->int_mask = 0;
+ smc91c111_update(s);
+}
+
+#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
+#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
+
+static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset -= s->base;
+ if (offset == 14) {
+ s->bank = value;
+ return;
+ }
+ if (offset == 15)
+ return;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ SET_LOW(tcr, value);
+ return;
+ case 1:
+ SET_HIGH(tcr, value);
+ return;
+ case 4: /* RCR */
+ SET_LOW(rcr, value);
+ return;
+ case 5:
+ SET_HIGH(rcr, value);
+ if (s->rcr & RCR_SOFT_RST)
+ smc91c111_reset(s);
+ return;
+ case 10: case 11: /* RPCR */
+ /* Ignored */
+ return;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ SET_LOW(cr, value);
+ return;
+ case 1:
+ SET_HIGH(cr,value);
+ return;
+ case 2: case 3: /* BASE */
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ /* Not implemented. */
+ return;
+ case 10: /* Genral Purpose */
+ SET_LOW(gpr, value);
+ return;
+ case 11:
+ SET_HIGH(gpr, value);
+ return;
+ case 12: /* Control */
+ if (value & 1)
+ fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
+ if (value & 2)
+ fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
+ value &= ~3;
+ SET_LOW(ctr, value);
+ return;
+ case 13:
+ SET_HIGH(ctr, value);
+ return;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: /* MMU Command */
+ switch (value >> 5) {
+ case 0: /* no-op */
+ break;
+ case 1: /* Allocate for TX. */
+ s->tx_alloc = 0x80;
+ s->int_level &= ~INT_ALLOC;
+ smc91c111_update(s);
+ smc91c111_tx_alloc(s);
+ break;
+ case 2: /* Reset MMU. */
+ s->allocated = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->tx_alloc = 0;
+ break;
+ case 3: /* Remove from RX FIFO. */
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 4: /* Remove from RX FIFO and release. */
+ if (s->rx_fifo_len > 0) {
+ smc91c111_release_packet(s, s->rx_fifo[0]);
+ }
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 5: /* Release. */
+ smc91c111_release_packet(s, s->packet_num);
+ break;
+ case 6: /* Add to TX FIFO. */
+ smc91c111_queue_tx(s, s->packet_num);
+ break;
+ case 7: /* Reset TX FIFO. */
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ break;
+ }
+ return;
+ case 1:
+ /* Ignore. */
+ return;
+ case 2: /* Packet Number Register */
+ s->packet_num = value;
+ return;
+ case 3: case 4: case 5:
+ /* Should be readonly, but linux writes to them anyway. Ignore. */
+ return;
+ case 6: /* Pointer */
+ SET_LOW(ptr, value);
+ return;
+ case 7:
+ SET_HIGH(ptr, value);
+ return;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
+ } else {
+ p += (offset & 3);
+ }
+ s->data[n][p] = value;
+ }
+ return;
+ case 12: /* Interrupt ACK. */
+ s->int_level &= ~(value & 0xd6);
+ if (value & INT_TX)
+ smc91c111_pop_tx_fifo_done(s);
+ smc91c111_update(s);
+ return;
+ case 13: /* Interrupt mask. */
+ s->int_mask = value;
+ smc91c111_update(s);
+ return;
+ }
+ break;;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return;
+ case 8: case 9: /* Management Interface. */
+ /* Not implemented. */
+ return;
+ case 12: /* Early receive. */
+ s->ercv = value & 0x1f;
+ case 13:
+ /* Ignore. */
+ return;
+ }
+ break;
+ }
+ cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
+ s->bank, (int)offset);
+}
+
+static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset -= s->base;
+ if (offset == 14) {
+ return s->bank;
+ }
+ if (offset == 15)
+ return 0x33;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ return s->tcr & 0xff;
+ case 1:
+ return s->tcr >> 8;
+ case 2: /* EPH Status */
+ return 0;
+ case 3:
+ return 0x40;
+ case 4: /* RCR */
+ return s->rcr & 0xff;
+ case 5:
+ return s->rcr >> 8;
+ case 6: /* Counter */
+ case 7:
+ /* Not implemented. */
+ return 0;
+ case 8: /* Memory size. */
+ return NUM_PACKETS;
+ case 9: /* Free memory available. */
+ {
+ int i;
+ int n;
+ n = 0;
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if (s->allocated & (1 << i))
+ n++;
+ }
+ return n;
+ }
+ case 10: case 11: /* RPCR */
+ /* Not implemented. */
+ return 0;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ return s->cr & 0xff;
+ case 1:
+ return s->cr >> 8;
+ case 2: case 3: /* BASE */
+ /* Not implemented. */
+ return 0;
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ return s->macaddr[offset - 4];
+ case 10: /* General Purpose */
+ return s->gpr & 0xff;
+ case 11:
+ return s->gpr >> 8;
+ case 12: /* Control */
+ return s->ctr & 0xff;
+ case 13:
+ return s->ctr >> 8;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: case 1: /* MMUCR Busy bit. */
+ return 0;
+ case 2: /* Packet Number. */
+ return s->packet_num;
+ case 3: /* Allocation Result. */
+ return s->tx_alloc;
+ case 4: /* TX FIFO */
+ if (s->tx_fifo_done_len == 0)
+ return 0x80;
+ else
+ return s->tx_fifo_done[0];
+ case 5: /* RX FIFO */
+ if (s->rx_fifo_len == 0)
+ return 0x80;
+ else
+ return s->rx_fifo[0];
+ case 6: /* Pointer */
+ return s->ptr & 0xff;
+ case 7:
+ return (s->ptr >> 8) & 0xf7;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
+ } else {
+ p += (offset & 3);
+ }
+ return s->data[n][p];
+ }
+ case 12: /* Interrupt status. */
+ return s->int_level;
+ case 13: /* Interrupt mask. */
+ return s->int_mask;
+ }
+ break;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return 0;
+ case 8: /* Management Interface. */
+ /* Not implemented. */
+ return 0x30;
+ case 9:
+ return 0x33;
+ case 10: /* Revision. */
+ return 0x91;
+ case 11:
+ return 0x33;
+ case 12:
+ return s->ercv;
+ case 13:
+ return 0;
+ }
+ break;
+ }
+ cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
+ s->bank, (int)offset);
+ return 0;
+}
+
+static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_writeb(opaque, offset, value & 0xff);
+ smc91c111_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+ /* 32-bit writes to offset 0xc only actually write to the bank select
+ register (offset 0xe) */
+ if (offset != s->base + 0xc)
+ smc91c111_writew(opaque, offset, value & 0xffff);
+ smc91c111_writew(opaque, offset + 2, value >> 16);
+}
+
+static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readb(opaque, offset);
+ val |= smc91c111_readb(opaque, offset + 1) << 8;
+ return val;
+}
+
+static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readw(opaque, offset);
+ val |= smc91c111_readw(opaque, offset + 2) << 16;
+ return val;
+}
+
+static int smc91c111_can_receive(void *opaque)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return 1;
+ if (s->allocated == (1 << NUM_PACKETS) - 1)
+ return 0;
+ return 1;
+}
+
+static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+ int status;
+ int packetsize;
+ uint32_t crc;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return;
+ /* Short packets are padded with zeros. Receiving a packet
+ < 64 bytes long is considered an error condition. */
+ if (size < 64)
+ packetsize = 64;
+ else
+ packetsize = (size & ~1);
+ packetsize += 6;
+ crc = (s->rcr & RCR_STRIP_CRC) == 0;
+ if (crc)
+ packetsize += 4;
+ /* TODO: Flag overrun and receive errors. */
+ if (packetsize > 2048)
+ return;
+ packetnum = smc91c111_allocate_packet(s);
+ if (packetnum == 0x80)
+ return;
+ s->rx_fifo[s->rx_fifo_len++] = packetnum;
+
+ p = &s->data[packetnum][0];
+ /* ??? Multicast packets? */
+ status = 0;
+ if (size > 1518)
+ status |= RS_TOOLONG;
+ if (size & 1)
+ status |= RS_ODDFRAME;
+ *(p++) = status & 0xff;
+ *(p++) = status >> 8;
+ *(p++) = packetsize & 0xff;
+ *(p++) = packetsize >> 8;
+ memcpy(p, buf, size & ~1);
+ p += (size & ~1);
+ /* Pad short packets. */
+ if (size < 64) {
+ int pad;
+
+ if (size & 1)
+ *(p++) = buf[size - 1];
+ pad = 64 - size;
+ memset(p, 0, pad);
+ p += pad;
+ size = 64;
+ }
+ /* It's not clear if the CRC should go before or after the last byte in
+ odd sized packets. Linux disables the CRC, so that's no help.
+ The pictures in the documentation show the CRC aligned on a 16-bit
+ boundary before the last odd byte, so that's what we do. */
+ if (crc) {
+ crc = crc32(~0, buf, size);
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ }
+ if (size & 1) {
+ *(p++) = buf[size - 1];
+ *(p++) = 0x60;
+ } else {
+ *(p++) = 0;
+ *(p++) = 0x40;
+ }
+ /* TODO: Raise early RX interrupt? */
+ s->int_level |= INT_RCV;
+ smc91c111_update(s);
+}
+
+static CPUReadMemoryFunc *smc91c111_readfn[] = {
+ smc91c111_readb,
+ smc91c111_readw,
+ smc91c111_readl
+};
+
+static CPUWriteMemoryFunc *smc91c111_writefn[] = {
+ smc91c111_writeb,
+ smc91c111_writew,
+ smc91c111_writel
+};
+
+void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+ smc91c111_state *s;
+ int iomemtype;
+
+ s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
+ iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
+ smc91c111_writefn, s);
+ cpu_register_physical_memory(base, 16, iomemtype);
+ s->base = base;
+ s->irq = irq;
+ memcpy(s->macaddr, nd->macaddr, 6);
+
+ smc91c111_reset(s);
+
+ s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive,
+ smc91c111_can_receive, s);
+ /* ??? Save/restore. */
+}
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
new file mode 100644
index 0000000..406c9ab
--- /dev/null
+++ b/hw/usb-hid.c
@@ -0,0 +1,896 @@
+/*
+ * QEMU USB HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "usb.h"
+
+/* HID interface requests */
+#define GET_REPORT 0xa101
+#define GET_IDLE 0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_REPORT 0x2109
+#define SET_IDLE 0x210a
+#define SET_PROTOCOL 0x210b
+
+/* HID descriptor types */
+#define USB_DT_HID 0x21
+#define USB_DT_REPORT 0x22
+#define USB_DT_PHY 0x23
+
+#define USB_MOUSE 1
+#define USB_TABLET 2
+#define USB_KEYBOARD 3
+
+typedef struct USBMouseState {
+ int dx, dy, dz, buttons_state;
+ int x, y;
+ int mouse_grabbed;
+ QEMUPutMouseEntry *eh_entry;
+} USBMouseState;
+
+typedef struct USBKeyboardState {
+ uint16_t modifiers;
+ uint8_t leds;
+ uint8_t key[16];
+ int keys;
+} USBKeyboardState;
+
+typedef struct USBHIDState {
+ USBDevice dev;
+ union {
+ USBMouseState ptr;
+ USBKeyboardState kbd;
+ };
+ int kind;
+ int protocol;
+ int idle;
+ int changed;
+} USBHIDState;
+
+/* mostly the same values as the Bochs USB Mouse device */
+static const uint8_t qemu_mouse_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x00, 0x01, /* u16 bcdUSB; v1.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x27, 0x06, /* u16 idVendor; */
+ 0x01, 0x00, /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+
+ 0x03, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x01, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_mouse_config_descriptor[] = {
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x22, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x04, /* u8 iConfiguration; */
+ 0xa0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 50, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x03, /* u8 if_bInterfaceClass; */
+ 0x01, /* u8 if_bInterfaceSubClass; */
+ 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x07, /* u8 if_iInterface; */
+
+ /* HID descriptor */
+ 0x09, /* u8 bLength; */
+ 0x21, /* u8 bDescriptorType; */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ 0x22, /* u8 type; Report */
+ 52, 0, /* u16 len */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x04, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_tablet_config_descriptor[] = {
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x22, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x05, /* u8 iConfiguration; */
+ 0xa0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 50, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x03, /* u8 if_bInterfaceClass; */
+ 0x01, /* u8 if_bInterfaceSubClass; */
+ 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x07, /* u8 if_iInterface; */
+
+ /* HID descriptor */
+ 0x09, /* u8 bLength; */
+ 0x21, /* u8 bDescriptorType; */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ 0x22, /* u8 type; Report */
+ 74, 0, /* u16 len */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x08, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_keyboard_config_descriptor[] = {
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */
+ 0x22, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x06, /* u8 iConfiguration; */
+ 0xa0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x32, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x03, /* u8 if_bInterfaceClass; HID */
+ 0x01, /* u8 if_bInterfaceSubClass; Boot */
+ 0x01, /* u8 if_bInterfaceProtocol; Keyboard */
+ 0x07, /* u8 if_iInterface; */
+
+ /* HID descriptor */
+ 0x09, /* u8 bLength; */
+ USB_DT_HID, /* u8 bDescriptorType; */
+ 0x11, 0x01, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ USB_DT_REPORT, /* u8 type; Report */
+ 0x3f, 0x00, /* u16 len */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */
+ USB_DIR_IN | 0x01, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x08, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_mouse_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
+};
+
+static const uint8_t qemu_tablet_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x35, 0x00, /* Physical Minimum (same as logical) */
+ 0x45, 0x00, /* Physical Maximum (same as logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
+};
+
+static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0xe0, /* Usage Minimum (224) */
+ 0x29, 0xe7, /* Usage Maximum (231) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x05, 0x08, /* Usage Page (LEDs) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x05, /* Usage Maximum (5) */
+ 0x91, 0x02, /* Output (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x03, /* Report Size (3) */
+ 0x91, 0x01, /* Output (Constant) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0xff, /* Logical Maximum (255) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x29, 0xff, /* Usage Maximum (255) */
+ 0x81, 0x00, /* Input (Data, Array) */
+ 0xc0, /* End Collection */
+};
+
+#define USB_HID_USAGE_ERROR_ROLLOVER 0x01
+#define USB_HID_USAGE_POSTFAIL 0x02
+#define USB_HID_USAGE_ERROR_UNDEFINED 0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
+static const uint8_t usb_hid_usage_keys[0x100] = {
+ 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+ 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+ 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+ 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+ 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+ 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+ 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+ 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+ 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+ 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+ 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+ 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+ 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+ 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void usb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ USBHIDState *hs = opaque;
+ USBMouseState *s = &hs->ptr;
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ s->buttons_state = buttons_state;
+ hs->changed = 1;
+}
+
+static void usb_tablet_event(void *opaque,
+ int x, int y, int dz, int buttons_state)
+{
+ USBHIDState *hs = opaque;
+ USBMouseState *s = &hs->ptr;
+
+ s->x = x;
+ s->y = y;
+ s->dz += dz;
+ s->buttons_state = buttons_state;
+ hs->changed = 1;
+}
+
+static void usb_keyboard_event(void *opaque, int keycode)
+{
+ USBHIDState *hs = opaque;
+ USBKeyboardState *s = &hs->kbd;
+ uint8_t hid_code, key;
+ int i;
+
+ key = keycode & 0x7f;
+ hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
+ s->modifiers &= ~(1 << 8);
+
+ hs->changed = 1;
+
+ switch (hid_code) {
+ case 0x00:
+ return;
+
+ case 0xe0:
+ if (s->modifiers & (1 << 9)) {
+ s->modifiers ^= 3 << 8;
+ return;
+ }
+ case 0xe1 ... 0xe7:
+ if (keycode & (1 << 7)) {
+ s->modifiers &= ~(1 << (hid_code & 0x0f));
+ return;
+ }
+ case 0xe8 ... 0xef:
+ s->modifiers |= 1 << (hid_code & 0x0f);
+ return;
+ }
+
+ if (keycode & (1 << 7)) {
+ for (i = s->keys - 1; i >= 0; i --)
+ if (s->key[i] == hid_code) {
+ s->key[i] = s->key[-- s->keys];
+ s->key[s->keys] = 0x00;
+ return;
+ }
+ } else {
+ for (i = s->keys - 1; i >= 0; i --)
+ if (s->key[i] == hid_code)
+ return;
+ if (s->keys < sizeof(s->key))
+ s->key[s->keys ++] = hid_code;
+ }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+ if (val < vmin)
+ return vmin;
+ else if (val > vmax)
+ return vmax;
+ else
+ return val;
+}
+
+static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+ int dx, dy, dz, b, l;
+ USBMouseState *s = &hs->ptr;
+
+ if (!s->mouse_grabbed) {
+ s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs,
+ 0, "QEMU USB Mouse");
+ s->mouse_grabbed = 1;
+ }
+
+ dx = int_clamp(s->dx, -127, 127);
+ dy = int_clamp(s->dy, -127, 127);
+ dz = int_clamp(s->dz, -127, 127);
+
+ s->dx -= dx;
+ s->dy -= dy;
+ s->dz -= dz;
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ l = 0;
+ if (len > l)
+ buf[l ++] = b;
+ if (len > l)
+ buf[l ++] = dx;
+ if (len > l)
+ buf[l ++] = dy;
+ if (len > l)
+ buf[l ++] = dz;
+ return l;
+}
+
+static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+ int dz, b, l;
+ USBMouseState *s = &hs->ptr;
+
+ if (!s->mouse_grabbed) {
+ s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs,
+ 1, "QEMU USB Tablet");
+ s->mouse_grabbed = 1;
+ }
+
+ dz = int_clamp(s->dz, -127, 127);
+ s->dz -= dz;
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ buf[0] = b;
+ buf[1] = s->x & 0xff;
+ buf[2] = s->x >> 8;
+ buf[3] = s->y & 0xff;
+ buf[4] = s->y >> 8;
+ buf[5] = dz;
+ l = 6;
+
+ return l;
+}
+
+static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len)
+{
+ if (len < 2)
+ return 0;
+
+ buf[0] = s->modifiers & 0xff;
+ buf[1] = 0;
+ if (s->keys > 6)
+ memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+ else
+ memcpy(buf + 2, s->key, MIN(8, len) - 2);
+
+ return MIN(8, len);
+}
+
+static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
+{
+ if (len > 0) {
+ /* 0x01: Num Lock LED
+ * 0x02: Caps Lock LED
+ * 0x04: Scroll Lock LED
+ * 0x08: Compose LED
+ * 0x10: Kana LED */
+ s->leds = buf[0];
+ }
+ return 0;
+}
+
+static void usb_mouse_handle_reset(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ s->ptr.dx = 0;
+ s->ptr.dy = 0;
+ s->ptr.dz = 0;
+ s->ptr.x = 0;
+ s->ptr.y = 0;
+ s->ptr.buttons_state = 0;
+ s->protocol = 1;
+}
+
+static void usb_keyboard_handle_reset(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ qemu_add_kbd_event_handler(usb_keyboard_event, s);
+ s->protocol = 1;
+}
+
+static int usb_hid_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+ int ret = 0;
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_mouse_dev_descriptor,
+ sizeof(qemu_mouse_dev_descriptor));
+ ret = sizeof(qemu_mouse_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ if (s->kind == USB_MOUSE) {
+ memcpy(data, qemu_mouse_config_descriptor,
+ sizeof(qemu_mouse_config_descriptor));
+ ret = sizeof(qemu_mouse_config_descriptor);
+ } else if (s->kind == USB_TABLET) {
+ memcpy(data, qemu_tablet_config_descriptor,
+ sizeof(qemu_tablet_config_descriptor));
+ ret = sizeof(qemu_tablet_config_descriptor);
+ } else if (s->kind == USB_KEYBOARD) {
+ memcpy(data, qemu_keyboard_config_descriptor,
+ sizeof(qemu_keyboard_config_descriptor));
+ ret = sizeof(qemu_keyboard_config_descriptor);
+ }
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, s->dev.devname);
+ break;
+ case 3:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 4:
+ ret = set_usb_string(data, "HID Mouse");
+ break;
+ case 5:
+ ret = set_usb_string(data, "HID Tablet");
+ break;
+ case 6:
+ ret = set_usb_string(data, "HID Keyboard");
+ break;
+ case 7:
+ ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* hid specific requests */
+ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case 0x22:
+ if (s->kind == USB_MOUSE) {
+ memcpy(data, qemu_mouse_hid_report_descriptor,
+ sizeof(qemu_mouse_hid_report_descriptor));
+ ret = sizeof(qemu_mouse_hid_report_descriptor);
+ } else if (s->kind == USB_TABLET) {
+ memcpy(data, qemu_tablet_hid_report_descriptor,
+ sizeof(qemu_tablet_hid_report_descriptor));
+ ret = sizeof(qemu_tablet_hid_report_descriptor);
+ } else if (s->kind == USB_KEYBOARD) {
+ memcpy(data, qemu_keyboard_hid_report_descriptor,
+ sizeof(qemu_keyboard_hid_report_descriptor));
+ ret = sizeof(qemu_keyboard_hid_report_descriptor);
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case GET_REPORT:
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, data, length);
+ else if (s->kind == USB_TABLET)
+ ret = usb_tablet_poll(s, data, length);
+ else if (s->kind == USB_KEYBOARD)
+ ret = usb_keyboard_poll(&s->kbd, data, length);
+ break;
+ case SET_REPORT:
+ if (s->kind == USB_KEYBOARD)
+ ret = usb_keyboard_write(&s->kbd, data, length);
+ else
+ goto fail;
+ break;
+ case GET_PROTOCOL:
+ if (s->kind != USB_KEYBOARD)
+ goto fail;
+ ret = 1;
+ data[0] = s->protocol;
+ break;
+ case SET_PROTOCOL:
+ if (s->kind != USB_KEYBOARD)
+ goto fail;
+ ret = 0;
+ s->protocol = value;
+ break;
+ case GET_IDLE:
+ ret = 1;
+ data[0] = s->idle;
+ break;
+ case SET_IDLE:
+ s->idle = value;
+ ret = 0;
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+ int ret = 0;
+
+ switch(p->pid) {
+ case USB_TOKEN_IN:
+ if (p->devep == 1) {
+ /* TODO: Implement finite idle delays. */
+ if (!(s->changed || s->idle))
+ return USB_RET_NAK;
+ s->changed = 0;
+ if (s->kind == USB_MOUSE)
+ ret = usb_mouse_poll(s, p->data, p->len);
+ else if (s->kind == USB_TABLET)
+ ret = usb_tablet_poll(s, p->data, p->len);
+ else if (s->kind == USB_KEYBOARD)
+ ret = usb_keyboard_poll(&s->kbd, p->data, p->len);
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_hid_handle_destroy(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ if (s->kind != USB_KEYBOARD)
+ qemu_remove_mouse_event_handler(s->ptr.eh_entry);
+ /* TODO: else */
+ qemu_free(s);
+}
+
+USBDevice *usb_tablet_init(void)
+{
+ USBHIDState *s;
+
+ s = qemu_mallocz(sizeof(USBHIDState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_control = usb_hid_handle_control;
+ s->dev.handle_data = usb_hid_handle_data;
+ s->dev.handle_destroy = usb_hid_handle_destroy;
+ s->kind = USB_TABLET;
+ /* Force poll routine to be run and grab input the first time. */
+ s->changed = 1;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
+
+ return (USBDevice *)s;
+}
+
+USBDevice *usb_mouse_init(void)
+{
+ USBHIDState *s;
+
+ s = qemu_mallocz(sizeof(USBHIDState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_mouse_handle_reset;
+ s->dev.handle_control = usb_hid_handle_control;
+ s->dev.handle_data = usb_hid_handle_data;
+ s->dev.handle_destroy = usb_hid_handle_destroy;
+ s->kind = USB_MOUSE;
+ /* Force poll routine to be run and grab input the first time. */
+ s->changed = 1;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+
+ return (USBDevice *)s;
+}
+
+USBDevice *usb_keyboard_init(void)
+{
+ USBHIDState *s;
+
+ s = qemu_mallocz(sizeof(USBHIDState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_keyboard_handle_reset;
+ s->dev.handle_control = usb_hid_handle_control;
+ s->dev.handle_data = usb_hid_handle_data;
+ s->dev.handle_destroy = usb_hid_handle_destroy;
+ s->kind = USB_KEYBOARD;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard");
+
+ return (USBDevice *) s;
+}
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
new file mode 100644
index 0000000..97c3d05
--- /dev/null
+++ b/hw/usb-hub.c
@@ -0,0 +1,554 @@
+/*
+ * QEMU USB HUB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+
+//#define DEBUG
+
+#define MAX_PORTS 8
+
+typedef struct USBHubPort {
+ USBPort port;
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+} USBHubPort;
+
+typedef struct USBHubState {
+ USBDevice dev;
+ int nb_ports;
+ USBHubPort ports[MAX_PORTS];
+} USBHubState;
+
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+#define PORT_STAT_CONNECTION 0x0001
+#define PORT_STAT_ENABLE 0x0002
+#define PORT_STAT_SUSPEND 0x0004
+#define PORT_STAT_OVERCURRENT 0x0008
+#define PORT_STAT_RESET 0x0010
+#define PORT_STAT_POWER 0x0100
+#define PORT_STAT_LOW_SPEED 0x0200
+#define PORT_STAT_HIGH_SPEED 0x0400
+#define PORT_STAT_TEST 0x0800
+#define PORT_STAT_INDICATOR 0x1000
+
+#define PORT_STAT_C_CONNECTION 0x0001
+#define PORT_STAT_C_ENABLE 0x0002
+#define PORT_STAT_C_SUSPEND 0x0004
+#define PORT_STAT_C_OVERCURRENT 0x0008
+#define PORT_STAT_C_RESET 0x0010
+
+#define PORT_CONNECTION 0
+#define PORT_ENABLE 1
+#define PORT_SUSPEND 2
+#define PORT_OVERCURRENT 3
+#define PORT_RESET 4
+#define PORT_POWER 8
+#define PORT_LOWSPEED 9
+#define PORT_HIGHSPEED 10
+#define PORT_C_CONNECTION 16
+#define PORT_C_ENABLE 17
+#define PORT_C_SUSPEND 18
+#define PORT_C_OVERCURRENT 19
+#define PORT_C_RESET 20
+#define PORT_TEST 21
+#define PORT_INDICATOR 22
+
+/* same as Linux kernel root hubs */
+
+static const uint8_t qemu_hub_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x10, 0x01, /* u16 bcdUSB; v1.1 */
+
+ 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* u16 idVendor; */
+ 0x00, 0x00, /* u16 idProduct; */
+ 0x01, 0x01, /* u16 bcdDevice */
+
+ 0x03, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x01, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+/* XXX: patch interrupt size */
+static const uint8_t qemu_hub_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0xc0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x00, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* u8 if_bInterfaceSubClass; */
+ 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x00, /* u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_hub_hub_descriptor[] =
+{
+ 0x00, /* u8 bLength; patched in later */
+ 0x29, /* u8 bDescriptorType; Hub-descriptor */
+ 0x00, /* u8 bNbrPorts; (patched later) */
+ 0x0a, /* u16 wHubCharacteristics; */
+ 0x00, /* (per-port OC, no power switching) */
+ 0x01, /* u8 bPwrOn2pwrGood; 2ms */
+ 0x00 /* u8 bHubContrCurrent; 0 mA */
+
+ /* DeviceRemovable and PortPwrCtrlMask patched in later */
+};
+
+static void usb_hub_attach(USBPort *port1, USBDevice *dev)
+{
+ USBHubState *s = port1->opaque;
+ USBHubPort *port = &s->ports[port1->index];
+
+ if (dev) {
+ if (port->port.dev)
+ usb_attach(port1, NULL);
+
+ port->wPortStatus |= PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (dev->speed == USB_SPEED_LOW)
+ port->wPortStatus |= PORT_STAT_LOW_SPEED;
+ else
+ port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
+ port->port.dev = dev;
+ /* send the attach message */
+ usb_send_msg(dev, USB_MSG_ATTACH);
+ } else {
+ dev = port->port.dev;
+ if (dev) {
+ port->wPortStatus &= ~PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (port->wPortStatus & PORT_STAT_ENABLE) {
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ port->wPortChange |= PORT_STAT_C_ENABLE;
+ }
+ /* send the detach message */
+ usb_send_msg(dev, USB_MSG_DETACH);
+ port->port.dev = NULL;
+ }
+ }
+}
+
+static void usb_hub_handle_reset(USBDevice *dev)
+{
+ /* XXX: do it */
+}
+
+static int usb_hub_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_hub_dev_descriptor,
+ sizeof(qemu_hub_dev_descriptor));
+ ret = sizeof(qemu_hub_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_hub_config_descriptor,
+ sizeof(qemu_hub_config_descriptor));
+
+ /* status change endpoint size based on number
+ * of ports */
+ data[22] = (s->nb_ports + 1 + 7) / 8;
+
+ ret = sizeof(qemu_hub_config_descriptor);
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* serial number */
+ ret = set_usb_string(data, "314159");
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB Hub");
+ break;
+ case 3:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* usb specific requests */
+ case GetHubStatus:
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ ret = 4;
+ break;
+ case GetPortStatus:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ data[0] = port->wPortStatus;
+ data[1] = port->wPortStatus >> 8;
+ data[2] = port->wPortChange;
+ data[3] = port->wPortChange >> 8;
+ ret = 4;
+ }
+ break;
+ case SetHubFeature:
+ case ClearHubFeature:
+ if (value == 0 || value == 1) {
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case SetPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ USBDevice *dev;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ switch(value) {
+ case PORT_SUSPEND:
+ port->wPortStatus |= PORT_STAT_SUSPEND;
+ break;
+ case PORT_RESET:
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ port->wPortChange |= PORT_STAT_C_RESET;
+ /* set enable bit */
+ port->wPortStatus |= PORT_STAT_ENABLE;
+ }
+ break;
+ case PORT_POWER:
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case ClearPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ USBDevice *dev;
+ if (n >= s->nb_ports)
+ goto fail;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ switch(value) {
+ case PORT_ENABLE:
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ break;
+ case PORT_C_ENABLE:
+ port->wPortChange &= ~PORT_STAT_C_ENABLE;
+ break;
+ case PORT_SUSPEND:
+ port->wPortStatus &= ~PORT_STAT_SUSPEND;
+ break;
+ case PORT_C_SUSPEND:
+ port->wPortChange &= ~PORT_STAT_C_SUSPEND;
+ break;
+ case PORT_C_CONNECTION:
+ port->wPortChange &= ~PORT_STAT_C_CONNECTION;
+ break;
+ case PORT_C_OVERCURRENT:
+ port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
+ break;
+ case PORT_C_RESET:
+ port->wPortChange &= ~PORT_STAT_C_RESET;
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case GetHubDescriptor:
+ {
+ unsigned int n, limit, var_hub_size = 0;
+ memcpy(data, qemu_hub_hub_descriptor,
+ sizeof(qemu_hub_hub_descriptor));
+ data[2] = s->nb_ports;
+
+ /* fill DeviceRemovable bits */
+ limit = ((s->nb_ports + 1 + 7) / 8) + 7;
+ for (n = 7; n < limit; n++) {
+ data[n] = 0x00;
+ var_hub_size++;
+ }
+
+ /* fill PortPwrCtrlMask bits */
+ limit = limit + ((s->nb_ports + 7) / 8);
+ for (;n < limit; n++) {
+ data[n] = 0xff;
+ var_hub_size++;
+ }
+
+ ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+ data[0] = ret;
+ break;
+ }
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ switch(p->pid) {
+ case USB_TOKEN_IN:
+ if (p->devep == 1) {
+ USBHubPort *port;
+ unsigned int status;
+ int i, n;
+ n = (s->nb_ports + 1 + 7) / 8;
+ if (p->len == 1) { /* FreeBSD workaround */
+ n = 1;
+ } else if (n > p->len) {
+ return USB_RET_BABBLE;
+ }
+ status = 0;
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ if (port->wPortChange)
+ status |= (1 << (i + 1));
+ }
+ if (status != 0) {
+ for(i = 0; i < n; i++) {
+ p->data[i] = status >> (8 * i);
+ }
+ ret = n;
+ } else {
+ ret = USB_RET_NAK; /* usb11 11.13.1 */
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
+{
+ USBHubPort *port;
+ USBDevice *dev;
+ int i, ret;
+
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
+ ret = dev->handle_packet(dev, p);
+ if (ret != USB_RET_NODEV) {
+ return ret;
+ }
+ }
+ }
+ return USB_RET_NODEV;
+}
+
+static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
+{
+ USBHubState *s = (USBHubState *)dev;
+
+#if defined(DEBUG) && 0
+ printf("usb_hub: pid=0x%x\n", pid);
+#endif
+ if (dev->state == USB_STATE_DEFAULT &&
+ dev->addr != 0 &&
+ p->devaddr != dev->addr &&
+ (p->pid == USB_TOKEN_SETUP ||
+ p->pid == USB_TOKEN_OUT ||
+ p->pid == USB_TOKEN_IN)) {
+ /* broadcast the packet to the devices */
+ return usb_hub_broadcast_packet(s, p);
+ }
+ return usb_generic_handle_packet(dev, p);
+}
+
+static void usb_hub_handle_destroy(USBDevice *dev)
+{
+ USBHubState *s = (USBHubState *)dev;
+
+ qemu_free(s);
+}
+
+USBDevice *usb_hub_init(int nb_ports)
+{
+ USBHubState *s;
+ USBHubPort *port;
+ int i;
+
+ if (nb_ports > MAX_PORTS)
+ return NULL;
+ s = qemu_mallocz(sizeof(USBHubState));
+ if (!s)
+ return NULL;
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_hub_handle_packet;
+
+ /* generic USB device init */
+ s->dev.handle_reset = usb_hub_handle_reset;
+ s->dev.handle_control = usb_hub_handle_control;
+ s->dev.handle_data = usb_hub_handle_data;
+ s->dev.handle_destroy = usb_hub_handle_destroy;
+
+ pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Hub");
+
+ s->nb_ports = nb_ports;
+ for(i = 0; i < s->nb_ports; i++) {
+ port = &s->ports[i];
+ qemu_register_usb_port(&port->port, s, i, usb_hub_attach);
+ port->wPortStatus = PORT_STAT_POWER;
+ port->wPortChange = 0;
+ }
+ return (USBDevice *)s;
+}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
new file mode 100644
index 0000000..f7ad25e
--- /dev/null
+++ b/hw/usb-msd.c
@@ -0,0 +1,578 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "block.h"
+#include "scsi-disk.h"
+
+//#define DEBUG_MSD
+
+#ifdef DEBUG_MSD
+#define DPRINTF(fmt, args...) \
+do { printf("usb-msd: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+/* USB requests. */
+#define MassStorageReset 0xff
+#define GetMaxLun 0xfe
+
+enum USBMSDMode {
+ USB_MSDM_CBW, /* Command Block. */
+ USB_MSDM_DATAOUT, /* Tranfer data to device. */
+ USB_MSDM_DATAIN, /* Transfer data from device. */
+ USB_MSDM_CSW /* Command Status. */
+};
+
+typedef struct {
+ USBDevice dev;
+ enum USBMSDMode mode;
+ uint32_t scsi_len;
+ uint8_t *scsi_buf;
+ uint32_t usb_len;
+ uint8_t *usb_buf;
+ uint32_t data_len;
+ uint32_t residue;
+ uint32_t tag;
+ BlockDriverState *bs;
+ SCSIDevice *scsi_dev;
+ int result;
+ /* For async completion. */
+ USBPacket *packet;
+} MSDState;
+
+struct usb_msd_cbw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t data_len;
+ uint8_t flags;
+ uint8_t lun;
+ uint8_t cmd_len;
+ uint8_t cmd[16];
+};
+
+struct usb_msd_csw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t residue;
+ uint8_t status;
+};
+
+static const uint8_t qemu_msd_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x00, 0x01, /* u16 bcdUSB; v1.0 */
+
+ 0x00, /* u8 bDeviceClass; */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ /* Vendor and product id are arbitrary. */
+ 0x00, 0x00, /* u16 idVendor; */
+ 0x00, 0x00, /* u16 idProduct; */
+ 0x00, 0x00, /* u16 bcdDevice */
+
+ 0x01, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x03, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+static const uint8_t qemu_msd_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x20, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0xc0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x00, /* u8 MaxPower; */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x02, /* u8 if_bNumEndpoints; */
+ 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
+ 0x06, /* u8 if_bInterfaceSubClass; SCSI */
+ 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
+ 0x00, /* u8 if_iInterface; */
+
+ /* Bulk-In endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00, /* u8 ep_bInterval; */
+
+ /* Bulk-Out endpoint */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
+ 0x02, /* u8 ep_bmAttributes; Bulk */
+ 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x00 /* u8 ep_bInterval; */
+};
+
+static void usb_msd_copy_data(MSDState *s)
+{
+ uint32_t len;
+ len = s->usb_len;
+ if (len > s->scsi_len)
+ len = s->scsi_len;
+ if (s->mode == USB_MSDM_DATAIN) {
+ memcpy(s->usb_buf, s->scsi_buf, len);
+ } else {
+ memcpy(s->scsi_buf, s->usb_buf, len);
+ }
+ s->usb_len -= len;
+ s->scsi_len -= len;
+ s->usb_buf += len;
+ s->scsi_buf += len;
+ s->data_len -= len;
+ if (s->scsi_len == 0) {
+ if (s->mode == USB_MSDM_DATAIN) {
+ s->scsi_dev->read_data(s->scsi_dev, s->tag);
+ } else if (s->mode == USB_MSDM_DATAOUT) {
+ s->scsi_dev->write_data(s->scsi_dev, s->tag);
+ }
+ }
+}
+
+static void usb_msd_send_status(MSDState *s)
+{
+ struct usb_msd_csw csw;
+
+ csw.sig = cpu_to_le32(0x53425355);
+ csw.tag = cpu_to_le32(s->tag);
+ csw.residue = s->residue;
+ csw.status = s->result;
+ memcpy(s->usb_buf, &csw, 13);
+}
+
+static void usb_msd_command_complete(void *opaque, int reason, uint32_t tag,
+ uint32_t arg)
+{
+ MSDState *s = (MSDState *)opaque;
+ USBPacket *p = s->packet;
+
+ if (tag != s->tag) {
+ fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
+ }
+ if (reason == SCSI_REASON_DONE) {
+ DPRINTF("Command complete %d\n", arg);
+ s->residue = s->data_len;
+ s->result = arg != 0;
+ if (s->packet) {
+ if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
+ /* A deferred packet with no write data remaining must be
+ the status read packet. */
+ usb_msd_send_status(s);
+ s->mode = USB_MSDM_CBW;
+ } else {
+ if (s->data_len) {
+ s->data_len -= s->usb_len;
+ if (s->mode == USB_MSDM_DATAIN)
+ memset(s->usb_buf, 0, s->usb_len);
+ s->usb_len = 0;
+ }
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ }
+ s->packet = NULL;
+ usb_packet_complete(p);
+ } else if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ return;
+ }
+ s->scsi_len = arg;
+ s->scsi_buf = s->scsi_dev->get_buf(s->scsi_dev, tag);
+ if (p) {
+ usb_msd_copy_data(s);
+ if (s->usb_len == 0) {
+ /* Set s->packet to NULL before calling usb_packet_complete
+ because annother request may be issued before
+ usb_packet_complete returns. */
+ DPRINTF("Packet complete %p\n", p);
+ s->packet = NULL;
+ usb_packet_complete(p);
+ }
+ }
+}
+
+static void usb_msd_handle_reset(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ DPRINTF("Reset\n");
+ s->mode = USB_MSDM_CBW;
+}
+
+static int usb_msd_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ MSDState *s = (MSDState *)dev;
+ int ret = 0;
+
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+ (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case USB_DT_DEVICE:
+ memcpy(data, qemu_msd_dev_descriptor,
+ sizeof(qemu_msd_dev_descriptor));
+ ret = sizeof(qemu_msd_dev_descriptor);
+ break;
+ case USB_DT_CONFIG:
+ memcpy(data, qemu_msd_config_descriptor,
+ sizeof(qemu_msd_config_descriptor));
+ ret = sizeof(qemu_msd_config_descriptor);
+ break;
+ case USB_DT_STRING:
+ switch(value & 0xff) {
+ case 0:
+ /* language ids */
+ data[0] = 4;
+ data[1] = 3;
+ data[2] = 0x09;
+ data[3] = 0x04;
+ ret = 4;
+ break;
+ case 1:
+ /* vendor description */
+ ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+ break;
+ case 2:
+ /* product description */
+ ret = set_usb_string(data, "QEMU USB HARDDRIVE");
+ break;
+ case 3:
+ /* serial number */
+ ret = set_usb_string(data, "1");
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = 1;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+ /* Class specific requests. */
+ case MassStorageReset:
+ /* Reset state ready for the next CBW. */
+ s->mode = USB_MSDM_CBW;
+ ret = 0;
+ break;
+ case GetMaxLun:
+ data[0] = 0;
+ ret = 1;
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_msd_cancel_io(USBPacket *p, void *opaque)
+{
+ MSDState *s = opaque;
+ s->scsi_dev->cancel_io(s->scsi_dev, s->tag);
+ s->packet = NULL;
+ s->scsi_len = 0;
+}
+
+static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
+{
+ MSDState *s = (MSDState *)dev;
+ int ret = 0;
+ struct usb_msd_cbw cbw;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_CBW:
+ if (len != 31) {
+ fprintf(stderr, "usb-msd: Bad CBW size");
+ goto fail;
+ }
+ memcpy(&cbw, data, 31);
+ if (le32_to_cpu(cbw.sig) != 0x43425355) {
+ fprintf(stderr, "usb-msd: Bad signature %08x\n",
+ le32_to_cpu(cbw.sig));
+ goto fail;
+ }
+ DPRINTF("Command on LUN %d\n", cbw.lun);
+ if (cbw.lun != 0) {
+ fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
+ goto fail;
+ }
+ s->tag = le32_to_cpu(cbw.tag);
+ s->data_len = le32_to_cpu(cbw.data_len);
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ } else if (cbw.flags & 0x80) {
+ s->mode = USB_MSDM_DATAIN;
+ } else {
+ s->mode = USB_MSDM_DATAOUT;
+ }
+ DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
+ s->tag, cbw.flags, cbw.cmd_len, s->data_len);
+ s->residue = 0;
+ s->scsi_dev->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ /* ??? Should check that USB and SCSI data transfer
+ directions match. */
+ if (s->residue == 0) {
+ if (s->mode == USB_MSDM_DATAIN) {
+ s->scsi_dev->read_data(s->scsi_dev, s->tag);
+ } else if (s->mode == USB_MSDM_DATAOUT) {
+ s->scsi_dev->write_data(s->scsi_dev, s->tag);
+ }
+ }
+ ret = len;
+ break;
+
+ case USB_MSDM_DATAOUT:
+ DPRINTF("Data out %d/%d\n", len, s->data_len);
+ if (len > s->data_len)
+ goto fail;
+
+ s->usb_buf = data;
+ s->usb_len = len;
+ if (s->scsi_len) {
+ usb_msd_copy_data(s);
+ }
+ if (s->residue && s->usb_len) {
+ s->data_len -= s->usb_len;
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ s->usb_len = 0;
+ }
+ if (s->usb_len) {
+ DPRINTF("Deferring packet %p\n", p);
+ usb_defer_packet(p, usb_msd_cancel_io, s);
+ s->packet = p;
+ ret = USB_RET_ASYNC;
+ } else {
+ ret = len;
+ }
+ break;
+
+ default:
+ DPRINTF("Unexpected write (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_DATAOUT:
+ if (s->data_len != 0 || len < 13)
+ goto fail;
+ /* Waiting for SCSI write to complete. */
+ usb_defer_packet(p, usb_msd_cancel_io, s);
+ s->packet = p;
+ ret = USB_RET_ASYNC;
+ break;
+
+ case USB_MSDM_CSW:
+ DPRINTF("Command status %d tag 0x%x, len %d\n",
+ s->result, s->tag, len);
+ if (len < 13)
+ goto fail;
+
+ s->usb_len = len;
+ s->usb_buf = data;
+ usb_msd_send_status(s);
+ s->mode = USB_MSDM_CBW;
+ ret = 13;
+ break;
+
+ case USB_MSDM_DATAIN:
+ DPRINTF("Data in %d/%d\n", len, s->data_len);
+ if (len > s->data_len)
+ len = s->data_len;
+ s->usb_buf = data;
+ s->usb_len = len;
+ if (s->scsi_len) {
+ usb_msd_copy_data(s);
+ }
+ if (s->residue && s->usb_len) {
+ s->data_len -= s->usb_len;
+ memset(s->usb_buf, 0, s->usb_len);
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ s->usb_len = 0;
+ }
+ if (s->usb_len) {
+ DPRINTF("Deferring packet %p\n", p);
+ usb_defer_packet(p, usb_msd_cancel_io, s);
+ s->packet = p;
+ ret = USB_RET_ASYNC;
+ } else {
+ ret = len;
+ }
+ break;
+
+ default:
+ DPRINTF("Unexpected read (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_msd_handle_destroy(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ s->scsi_dev->destroy(s->scsi_dev);
+ bdrv_delete(s->bs);
+ qemu_free(s);
+}
+
+USBDevice *usb_msd_init(const char *filename)
+{
+ MSDState *s;
+ BlockDriverState *bdrv;
+ BlockDriver *drv = NULL;
+ const char *p1;
+ char fmt[32];
+
+ p1 = strchr(filename, ':');
+ if (p1++) {
+ const char *p2;
+
+ if (strstart(filename, "format=", &p2)) {
+ int len = MIN(p1 - p2, sizeof(fmt));
+ pstrcpy(fmt, len, p2);
+
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ printf("invalid format %s\n", fmt);
+ return NULL;
+ }
+ } else if (*filename != ':') {
+ printf("unrecognized USB mass-storage option %s\n", filename);
+ return NULL;
+ }
+
+ filename = p1;
+ }
+
+ if (!*filename) {
+ printf("block device specification needed\n");
+ return NULL;
+ }
+
+ s = qemu_mallocz(sizeof(MSDState));
+ if (!s)
+ return NULL;
+
+ bdrv = bdrv_new("usb");
+ if (bdrv_open2(bdrv, filename, 0, drv) < 0)
+ goto fail;
+ if (qemu_key_check(bdrv, filename))
+ goto fail;
+ s->bs = bdrv;
+
+ s->dev.speed = USB_SPEED_FULL;
+ s->dev.handle_packet = usb_generic_handle_packet;
+
+ s->dev.handle_reset = usb_msd_handle_reset;
+ s->dev.handle_control = usb_msd_handle_control;
+ s->dev.handle_data = usb_msd_handle_data;
+ s->dev.handle_destroy = usb_msd_handle_destroy;
+
+ snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
+ filename);
+
+ s->scsi_dev = scsi_disk_init(bdrv, 0, usb_msd_command_complete, s);
+ usb_msd_handle_reset((USBDevice *)s);
+ return (USBDevice *)s;
+ fail:
+ qemu_free(s);
+ return NULL;
+}
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
new file mode 100644
index 0000000..55cb77b
--- /dev/null
+++ b/hw/usb-ohci.c
@@ -0,0 +1,1684 @@
+/*
+ * QEMU USB OHCI Emulation
+ * Copyright (c) 2004 Gianni Tedesco
+ * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * 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 Isochronous transfers
+ * o Allocate bandwidth in frames properly
+ * o Disable timers when nothing needs to be done, or remove timer usage
+ * all together.
+ * o Handle unrecoverable errors properly
+ * o BIOS work to boot from USB storage
+*/
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "pxa.h"
+
+//#define DEBUG_OHCI
+/* Dump packet contents. */
+//#define DEBUG_PACKET
+//#define DEBUG_ISOCH
+/* This causes frames to occur 1000x slower */
+//#define OHCI_TIME_WARP 1
+
+#ifdef DEBUG_OHCI
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+/* Number of Downstream Ports on the root hub. */
+
+#define OHCI_MAX_PORTS 15
+
+static int64_t usb_frame_time;
+static int64_t usb_bit_time;
+
+typedef struct OHCIPort {
+ USBPort port;
+ uint32_t ctrl;
+} OHCIPort;
+
+enum ohci_type {
+ OHCI_TYPE_PCI,
+ OHCI_TYPE_PXA
+};
+
+typedef struct {
+ qemu_irq irq;
+ enum ohci_type type;
+ target_phys_addr_t mem_base;
+ int mem;
+ int num_ports;
+ const char *name;
+
+ QEMUTimer *eof_timer;
+ int64_t sof_time;
+
+ /* 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, ctrl_cur;
+ uint32_t bulk_head, bulk_cur;
+ uint32_t per_cur;
+ uint32_t done;
+ int done_count;
+
+ /* Frame counter partition */
+ uint32_t fsmps:15;
+ uint32_t fit:1;
+ uint32_t fi:14;
+ uint32_t frt:1;
+ uint16_t frame_number;
+ uint16_t padding;
+ uint32_t pstart;
+ uint32_t lst;
+
+ /* Root Hub partition */
+ uint32_t rhdesc_a, rhdesc_b;
+ uint32_t rhstatus;
+ OHCIPort rhport[OHCI_MAX_PORTS];
+
+ /* PXA27x Non-OHCI events */
+ uint32_t hstatus;
+ uint32_t hmask;
+ uint32_t hreset;
+ uint32_t htest;
+
+ /* Active packets. */
+ uint32_t old_ctl;
+ USBPacket usb_packet;
+ uint8_t usb_buf[8192];
+ uint32_t async_td;
+ int async_complete;
+
+} OHCIState;
+
+/* Host Controller Communications Area */
+struct ohci_hcca {
+ uint32_t intr[32];
+ uint16_t frame, pad;
+ uint32_t done;
+};
+
+static void ohci_bus_stop(OHCIState *ohci);
+
+/* Bitfields for the first word of an Endpoint Desciptor. */
+#define OHCI_ED_FA_SHIFT 0
+#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_EN_SHIFT 7
+#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT)
+#define OHCI_ED_D_SHIFT 11
+#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT)
+#define OHCI_ED_S (1<<13)
+#define OHCI_ED_K (1<<14)
+#define OHCI_ED_F (1<<15)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK (0x7ff<<OHCI_ED_MPS_SHIFT)
+
+/* Flags in the head field of an Endpoint Desciptor. */
+#define OHCI_ED_H 1
+#define OHCI_ED_C 2
+
+/* Bitfields for the first word of a Transfer Desciptor. */
+#define OHCI_TD_R (1<<18)
+#define OHCI_TD_DP_SHIFT 19
+#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT)
+#define OHCI_TD_DI_SHIFT 21
+#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
+#define OHCI_TD_T0 (1<<24)
+#define OHCI_TD_T1 (1<<24)
+#define OHCI_TD_EC_SHIFT 26
+#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
+#define OHCI_TD_CC_SHIFT 28
+#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT)
+
+/* Bitfields for the first word of an Isochronous Transfer Desciptor. */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT 0
+#define OHCI_TD_SF_MASK (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT 24
+#define OHCI_TD_FC_MASK (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK 0xfffff000
+#define OHCI_OFFSET_MASK 0xfff
+
+#define OHCI_DPTR_MASK 0xfffffff0
+
+#define OHCI_BM(val, field) \
+ (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
+
+#define OHCI_SET_BM(val, field, newval) do { \
+ val &= ~OHCI_##field##_MASK; \
+ val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
+ } while(0)
+
+/* endpoint descriptor */
+struct ohci_ed {
+ uint32_t flags;
+ uint32_t tail;
+ uint32_t head;
+ uint32_t next;
+};
+
+/* General transfer descriptor */
+struct ohci_td {
+ uint32_t flags;
+ uint32_t cbp;
+ uint32_t next;
+ uint32_t be;
+};
+
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+ uint32_t flags;
+ uint32_t bp;
+ uint32_t next;
+ uint32_t be;
+ uint16_t offset[8];
+};
+
+#define USB_HZ 12000000
+
+/* OHCI Local stuff */
+#define OHCI_CTL_CBSR ((1<<0)|(1<<1))
+#define OHCI_CTL_PLE (1<<2)
+#define OHCI_CTL_IE (1<<3)
+#define OHCI_CTL_CLE (1<<4)
+#define OHCI_CTL_BLE (1<<5)
+#define OHCI_CTL_HCFS ((1<<6)|(1<<7))
+#define OHCI_USB_RESET 0x00
+#define OHCI_USB_RESUME 0x40
+#define OHCI_USB_OPERATIONAL 0x80
+#define OHCI_USB_SUSPEND 0xc0
+#define OHCI_CTL_IR (1<<8)
+#define OHCI_CTL_RWC (1<<9)
+#define OHCI_CTL_RWE (1<<10)
+
+#define OHCI_STATUS_HCR (1<<0)
+#define OHCI_STATUS_CLF (1<<1)
+#define OHCI_STATUS_BLF (1<<2)
+#define OHCI_STATUS_OCR (1<<3)
+#define OHCI_STATUS_SOC ((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD (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_EDPTR_MASK 0xfffffff0
+
+#define OHCI_FMI_FI 0x00003fff
+#define OHCI_FMI_FSMPS 0xffff0000
+#define OHCI_FMI_FIT 0x80000000
+
+#define OHCI_FR_RT (1<<31)
+
+#define OHCI_LS_THRESH 0x628
+
+#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */
+#define OHCI_RHA_PSM (1<<8)
+#define OHCI_RHA_NPS (1<<9)
+#define OHCI_RHA_DT (1<<10)
+#define OHCI_RHA_OCPM (1<<11)
+#define OHCI_RHA_NOCP (1<<12)
+#define OHCI_RHA_POTPGT_MASK 0xff000000
+
+#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)
+#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
+ |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
+
+#define OHCI_TD_DIR_SETUP 0x0
+#define OHCI_TD_DIR_OUT 0x1
+#define OHCI_TD_DIR_IN 0x2
+#define OHCI_TD_DIR_RESERVED 0x3
+
+#define OHCI_CC_NOERROR 0x0
+#define OHCI_CC_CRC 0x1
+#define OHCI_CC_BITSTUFFING 0x2
+#define OHCI_CC_DATATOGGLEMISMATCH 0x3
+#define OHCI_CC_STALL 0x4
+#define OHCI_CC_DEVICENOTRESPONDING 0x5
+#define OHCI_CC_PIDCHECKFAILURE 0x6
+#define OHCI_CC_UNDEXPETEDPID 0x7
+#define OHCI_CC_DATAOVERRUN 0x8
+#define OHCI_CC_DATAUNDERRUN 0x9
+#define OHCI_CC_BUFFEROVERRUN 0xc
+#define OHCI_CC_BUFFERUNDERRUN 0xd
+
+#define OHCI_HRESET_FSBIR (1 << 0)
+
+/* Update IRQ levels */
+static inline void ohci_intr_update(OHCIState *ohci)
+{
+ int level = 0;
+
+ if ((ohci->intr & OHCI_INTR_MIE) &&
+ (ohci->intr_status & ohci->intr))
+ level = 1;
+
+ qemu_set_irq(ohci->irq, level);
+}
+
+/* Set an interrupt */
+static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
+{
+ ohci->intr_status |= intr;
+ ohci_intr_update(ohci);
+}
+
+/* Attach or detach a device on a root hub port. */
+static void ohci_attach(USBPort *port1, USBDevice *dev)
+{
+ OHCIState *s = port1->opaque;
+ OHCIPort *port = &s->rhport[port1->index];
+ uint32_t old_state = port->ctrl;
+
+ if (dev) {
+ if (port->port.dev) {
+ usb_attach(port1, NULL);
+ }
+ /* set connect status */
+ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
+
+ /* update speed */
+ if (dev->speed == USB_SPEED_LOW)
+ port->ctrl |= OHCI_PORT_LSDA;
+ else
+ port->ctrl &= ~OHCI_PORT_LSDA;
+ port->port.dev = dev;
+
+ /* notify of remote-wakeup */
+ if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND)
+ ohci_set_interrupt(s, OHCI_INTR_RD);
+
+ /* send the attach message */
+ usb_send_msg(dev, USB_MSG_ATTACH);
+ dprintf("usb-ohci: Attached port %d\n", port1->index);
+ } else {
+ /* set connect status */
+ if (port->ctrl & OHCI_PORT_CCS) {
+ port->ctrl &= ~OHCI_PORT_CCS;
+ port->ctrl |= OHCI_PORT_CSC;
+ }
+ /* disable port */
+ if (port->ctrl & OHCI_PORT_PES) {
+ port->ctrl &= ~OHCI_PORT_PES;
+ port->ctrl |= OHCI_PORT_PESC;
+ }
+ dev = port->port.dev;
+ if (dev) {
+ /* send the detach message */
+ usb_send_msg(dev, USB_MSG_DETACH);
+ }
+ port->port.dev = NULL;
+ dprintf("usb-ohci: Detached port %d\n", port1->index);
+ }
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(s, OHCI_INTR_RHSC);
+}
+
+/* Reset the controller */
+static void ohci_reset(void *opaque)
+{
+ OHCIState *ohci = opaque;
+ OHCIPort *port;
+ int i;
+
+ ohci_bus_stop(ohci);
+ ohci->ctl = 0;
+ ohci->old_ctl = 0;
+ ohci->status = 0;
+ ohci->intr_status = 0;
+ ohci->intr = OHCI_INTR_MIE;
+
+ ohci->hcca = 0;
+ ohci->ctrl_head = ohci->ctrl_cur = 0;
+ ohci->bulk_head = ohci->bulk_cur = 0;
+ ohci->per_cur = 0;
+ ohci->done = 0;
+ ohci->done_count = 7;
+
+ /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+ * I took the value linux sets ...
+ */
+ ohci->fsmps = 0x2778;
+ ohci->fi = 0x2edf;
+ ohci->fit = 0;
+ ohci->frt = 0;
+ ohci->frame_number = 0;
+ ohci->pstart = 0;
+ ohci->lst = OHCI_LS_THRESH;
+
+ ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+ ohci->rhdesc_b = 0x0; /* Impl. specific */
+ ohci->rhstatus = 0;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ {
+ port = &ohci->rhport[i];
+ port->ctrl = 0;
+ if (port->port.dev)
+ ohci_attach(&port->port, port->port.dev);
+ }
+ if (ohci->async_td) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ dprintf("usb-ohci: Reset %s\n", ohci->name);
+}
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+ *buf = le32_to_cpu(*buf);
+ }
+
+ return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint32_t tmp = cpu_to_le32(*buf);
+ cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+ }
+
+ return 1;
+}
+
+/* Get an array of words from main memory */
+static inline int get_words(uint32_t addr, uint16_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+ *buf = le16_to_cpu(*buf);
+ }
+
+ return 1;
+}
+
+/* Put an array of words in to main memory */
+static inline int put_words(uint32_t addr, uint16_t *buf, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint16_t tmp = cpu_to_le16(*buf);
+ cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+ }
+
+ return 1;
+}
+
+static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed)
+{
+ return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_read_td(uint32_t addr, struct ohci_td *td)
+{
+ return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+ return (get_dwords(addr, (uint32_t *)td, 4) &&
+ get_words(addr + 16, td->offset, 8));
+}
+
+static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed)
+{
+ return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_put_td(uint32_t addr, struct ohci_td *td)
+{
+ return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+ return (put_dwords(addr, (uint32_t *)td, 4) &&
+ put_words(addr + 16, td->offset, 8));
+}
+
+/* Read/Write the contents of a TD from/to main memory. */
+static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
+{
+ uint32_t ptr;
+ uint32_t n;
+
+ ptr = td->cbp;
+ n = 0x1000 - (ptr & 0xfff);
+ if (n > len)
+ n = len;
+ cpu_physical_memory_rw(ptr, buf, n, write);
+ if (n == len)
+ return;
+ ptr = td->be & ~0xfffu;
+ buf += n;
+ cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+/* Read/Write the contents of an ISO TD from/to main memory. */
+static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr,
+ uint8_t *buf, int len, int write)
+{
+ uint32_t ptr;
+ uint32_t n;
+
+ ptr = start_addr;
+ n = 0x1000 - (ptr & 0xfff);
+ if (n > len)
+ n = len;
+ cpu_physical_memory_rw(ptr, buf, n, write);
+ if (n == len)
+ return;
+ ptr = end_addr & ~0xfffu;
+ buf += n;
+ cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
+
+static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
+{
+ OHCIState *ohci = opaque;
+#ifdef DEBUG_PACKET
+ dprintf("Async packet complete\n");
+#endif
+ ohci->async_complete = 1;
+ ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+ int completion)
+{
+ int dir;
+ size_t len = 0;
+ const char *str = NULL;
+ int pid;
+ int ret;
+ int i;
+ USBDevice *dev;
+ struct ohci_iso_td iso_td;
+ uint32_t addr;
+ uint16_t starting_frame;
+ int16_t relative_frame_number;
+ int frame_count;
+ uint32_t start_offset, next_offset, end_offset = 0;
+ uint32_t start_addr, end_addr;
+
+ addr = ed->head & OHCI_DPTR_MASK;
+
+ if (!ohci_read_iso_td(addr, &iso_td)) {
+ printf("usb-ohci: ISO_TD read error at %x\n", addr);
+ return 0;
+ }
+
+ starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+ frame_count = OHCI_BM(iso_td.flags, TD_FC);
+ relative_frame_number = USUB(ohci->frame_number, starting_frame);
+
+#ifdef DEBUG_ISOCH
+ printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "frame_number 0x%.8x starting_frame 0x%.8x\n"
+ "frame_count 0x%.8x relative %d\n"
+ "di 0x%.8x cc 0x%.8x\n",
+ ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+ iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+ iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+ iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+ ohci->frame_number, starting_frame,
+ frame_count, relative_frame_number,
+ OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+ if (relative_frame_number < 0) {
+ dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+ return 1;
+ } else if (relative_frame_number > frame_count) {
+ /* ISO TD expired - retire the TD to the Done Queue and continue with
+ the next ISO TD of the same ED */
+ dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number,
+ frame_count);
+ OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+ iso_td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(iso_td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ ohci_put_iso_td(addr, &iso_td);
+ return 0;
+ }
+
+ dir = OHCI_BM(ed->flags, ED_D);
+ switch (dir) {
+ case OHCI_TD_DIR_IN:
+ str = "in";
+ pid = USB_TOKEN_IN;
+ break;
+ case OHCI_TD_DIR_OUT:
+ str = "out";
+ pid = USB_TOKEN_OUT;
+ break;
+ case OHCI_TD_DIR_SETUP:
+ str = "setup";
+ pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ printf("usb-ohci: Bad direction %d\n", dir);
+ return 1;
+ }
+
+ if (!iso_td.bp || !iso_td.be) {
+ printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+ return 1;
+ }
+
+ start_offset = iso_td.offset[relative_frame_number];
+ next_offset = iso_td.offset[relative_frame_number + 1];
+
+ if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) ||
+ ((relative_frame_number < frame_count) &&
+ !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+ printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+ start_offset, next_offset);
+ return 1;
+ }
+
+ if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+ printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+ start_offset, next_offset);
+ return 1;
+ }
+
+ if ((start_offset & 0x1000) == 0) {
+ start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+ (start_offset & OHCI_OFFSET_MASK);
+ } else {
+ start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+ (start_offset & OHCI_OFFSET_MASK);
+ }
+
+ if (relative_frame_number < frame_count) {
+ end_offset = next_offset - 1;
+ if ((end_offset & 0x1000) == 0) {
+ end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+ (end_offset & OHCI_OFFSET_MASK);
+ } else {
+ end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+ (end_offset & OHCI_OFFSET_MASK);
+ }
+ } else {
+ /* Last packet in the ISO TD */
+ end_addr = iso_td.be;
+ }
+
+ if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+ len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+ - (start_addr & OHCI_OFFSET_MASK);
+ } else {
+ len = end_addr - start_addr + 1;
+ }
+
+ if (len && dir != OHCI_TD_DIR_IN) {
+ ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0);
+ }
+
+ if (completion) {
+ ret = ohci->usb_packet.len;
+ } else {
+ ret = USB_RET_NODEV;
+ for (i = 0; i < ohci->num_ports; i++) {
+ dev = ohci->rhport[i].port.dev;
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+ continue;
+ ohci->usb_packet.pid = pid;
+ ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+ ohci->usb_packet.data = ohci->usb_buf;
+ ohci->usb_packet.len = len;
+ ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+ ohci->usb_packet.complete_opaque = ohci;
+ ret = dev->handle_packet(dev, &ohci->usb_packet);
+ if (ret != USB_RET_NODEV)
+ break;
+ }
+
+ if (ret == USB_RET_ASYNC) {
+ return 1;
+ }
+ }
+
+#ifdef DEBUG_ISOCH
+ printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+ start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+ /* Writeback */
+ if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+ /* IN transfer succeeded */
+ ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_NOERROR);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+ } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+ /* OUT transfer succeeded */
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_NOERROR);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+ } else {
+ if (ret > (ssize_t) len) {
+ printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DATAOVERRUN);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ len);
+ } else if (ret >= 0) {
+ printf("usb-ohci: DataUnderrun %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DATAUNDERRUN);
+ } else {
+ switch (ret) {
+ case USB_RET_NODEV:
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DEVICENOTRESPONDING);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ 0);
+ break;
+ case USB_RET_NAK:
+ case USB_RET_STALL:
+ printf("usb-ohci: got NAK/STALL %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_STALL);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ 0);
+ break;
+ default:
+ printf("usb-ohci: Bad device response %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_UNDEXPETEDPID);
+ break;
+ }
+ }
+ }
+
+ if (relative_frame_number == frame_count) {
+ /* Last data packet of ISO TD - retire the TD to the Done Queue */
+ OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+ iso_td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(iso_td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ }
+ ohci_put_iso_td(addr, &iso_td);
+ return 1;
+}
+
+/* Service a transport descriptor.
+ Returns nonzero to terminate processing of this endpoint. */
+
+static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
+{
+ int dir;
+ size_t len = 0;
+ const char *str = NULL;
+ int pid;
+ int ret;
+ int i;
+ USBDevice *dev;
+ struct ohci_td td;
+ uint32_t addr;
+ int flag_r;
+ int completion;
+
+ addr = ed->head & OHCI_DPTR_MASK;
+ /* See if this TD has already been submitted to the device. */
+ completion = (addr == ohci->async_td);
+ if (completion && !ohci->async_complete) {
+#ifdef DEBUG_PACKET
+ dprintf("Skipping async TD\n");
+#endif
+ return 1;
+ }
+ if (!ohci_read_td(addr, &td)) {
+ fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+ return 0;
+ }
+
+ dir = OHCI_BM(ed->flags, ED_D);
+ switch (dir) {
+ case OHCI_TD_DIR_OUT:
+ case OHCI_TD_DIR_IN:
+ /* Same value. */
+ break;
+ default:
+ dir = OHCI_BM(td.flags, TD_DP);
+ break;
+ }
+
+ switch (dir) {
+ case OHCI_TD_DIR_IN:
+ str = "in";
+ pid = USB_TOKEN_IN;
+ break;
+ case OHCI_TD_DIR_OUT:
+ str = "out";
+ pid = USB_TOKEN_OUT;
+ break;
+ case OHCI_TD_DIR_SETUP:
+ str = "setup";
+ pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad direction\n");
+ return 1;
+ }
+ if (td.cbp && td.be) {
+ if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
+ len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
+ } else {
+ len = (td.be - td.cbp) + 1;
+ }
+
+ if (len && dir != OHCI_TD_DIR_IN && !completion) {
+ ohci_copy_td(&td, ohci->usb_buf, len, 0);
+ }
+ }
+
+ flag_r = (td.flags & OHCI_TD_R) != 0;
+#ifdef DEBUG_PACKET
+ dprintf(" TD @ 0x%.8x %u bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+ addr, len, str, flag_r, td.cbp, td.be);
+
+ if (len > 0 && dir != OHCI_TD_DIR_IN) {
+ dprintf(" data:");
+ for (i = 0; i < len; i++)
+ printf(" %.2x", ohci->usb_buf[i]);
+ dprintf("\n");
+ }
+#endif
+ if (completion) {
+ ret = ohci->usb_packet.len;
+ ohci->async_td = 0;
+ ohci->async_complete = 0;
+ } else {
+ ret = USB_RET_NODEV;
+ for (i = 0; i < ohci->num_ports; i++) {
+ dev = ohci->rhport[i].port.dev;
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+ continue;
+
+ if (ohci->async_td) {
+ /* ??? The hardware should allow one active packet per
+ endpoint. We only allow one active packet per controller.
+ This should be sufficient as long as devices respond in a
+ timely manner.
+ */
+#ifdef DEBUG_PACKET
+ dprintf("Too many pending packets\n");
+#endif
+ return 1;
+ }
+ ohci->usb_packet.pid = pid;
+ ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+ ohci->usb_packet.data = ohci->usb_buf;
+ ohci->usb_packet.len = len;
+ ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+ ohci->usb_packet.complete_opaque = ohci;
+ ret = dev->handle_packet(dev, &ohci->usb_packet);
+ if (ret != USB_RET_NODEV)
+ break;
+ }
+#ifdef DEBUG_PACKET
+ dprintf("ret=%d\n", ret);
+#endif
+ if (ret == USB_RET_ASYNC) {
+ ohci->async_td = addr;
+ return 1;
+ }
+ }
+ if (ret >= 0) {
+ if (dir == OHCI_TD_DIR_IN) {
+ ohci_copy_td(&td, ohci->usb_buf, ret, 1);
+#ifdef DEBUG_PACKET
+ dprintf(" data:");
+ for (i = 0; i < ret; i++)
+ printf(" %.2x", ohci->usb_buf[i]);
+ dprintf("\n");
+#endif
+ } else {
+ ret = len;
+ }
+ }
+
+ /* Writeback */
+ if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+ /* Transmission succeeded. */
+ if (ret == len) {
+ td.cbp = 0;
+ } else {
+ td.cbp += ret;
+ if ((td.cbp & 0xfff) + ret > 0xfff) {
+ td.cbp &= 0xfff;
+ td.cbp |= td.be & ~0xfff;
+ }
+ }
+ td.flags |= OHCI_TD_T1;
+ td.flags ^= OHCI_TD_T0;
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
+ OHCI_SET_BM(td.flags, TD_EC, 0);
+
+ ed->head &= ~OHCI_ED_C;
+ if (td.flags & OHCI_TD_T0)
+ ed->head |= OHCI_ED_C;
+ } else {
+ if (ret >= 0) {
+ dprintf("usb-ohci: Underrun\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
+ } else {
+ switch (ret) {
+ case USB_RET_NODEV:
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+ case USB_RET_NAK:
+ dprintf("usb-ohci: got NAK\n");
+ return 1;
+ case USB_RET_STALL:
+ dprintf("usb-ohci: got STALL\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
+ break;
+ case USB_RET_BABBLE:
+ dprintf("usb-ohci: got BABBLE\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
+ OHCI_SET_BM(td.flags, TD_EC, 3);
+ break;
+ }
+ }
+ ed->head |= OHCI_ED_H;
+ }
+
+ /* Retire this TD */
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= td.next & OHCI_DPTR_MASK;
+ td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ ohci_put_td(addr, &td);
+ return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
+}
+
+/* Service an endpoint list. Returns nonzero if active TD were found. */
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
+{
+ struct ohci_ed ed;
+ uint32_t next_ed;
+ uint32_t cur;
+ int active;
+
+ active = 0;
+
+ if (head == 0)
+ return 0;
+
+ for (cur = head; cur; cur = next_ed) {
+ if (!ohci_read_ed(cur, &ed)) {
+ fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+ return 0;
+ }
+
+ next_ed = ed.next & OHCI_DPTR_MASK;
+
+ if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
+ uint32_t addr;
+ /* Cancel pending packets for ED that have been paused. */
+ addr = ed.head & OHCI_DPTR_MASK;
+ if (ohci->async_td && addr == ohci->async_td) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ continue;
+ }
+
+ while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
+#ifdef DEBUG_PACKET
+ dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
+ "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+ OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
+ OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
+ (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
+ OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
+ (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+ ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+#endif
+ active = 1;
+
+ if ((ed.flags & OHCI_ED_F) == 0) {
+ if (ohci_service_td(ohci, &ed))
+ break;
+ } else {
+ /* Handle isochronous endpoints */
+ if (ohci_service_iso_td(ohci, &ed, completion))
+ break;
+ }
+ }
+
+ ohci_put_ed(cur, &ed);
+ }
+
+ return active;
+}
+
+/* Generate a SOF event, and set a timer for EOF */
+static void ohci_sof(OHCIState *ohci)
+{
+ ohci->sof_time = qemu_get_clock(vm_clock);
+ qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+ ohci_set_interrupt(ohci, OHCI_INTR_SF);
+}
+
+/* Process Control and Bulk lists. */
+static void ohci_process_lists(OHCIState *ohci, int completion)
+{
+ if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
+ if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
+ dprintf("usb-ohci: head %x, cur %x\n",
+ ohci->ctrl_head, ohci->ctrl_cur);
+ if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
+ ohci->ctrl_cur = 0;
+ ohci->status &= ~OHCI_STATUS_CLF;
+ }
+ }
+
+ if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
+ if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
+ ohci->bulk_cur = 0;
+ ohci->status &= ~OHCI_STATUS_BLF;
+ }
+ }
+}
+
+/* Do frame processing on frame boundary */
+static void ohci_frame_boundary(void *opaque)
+{
+ OHCIState *ohci = opaque;
+ struct ohci_hcca hcca;
+
+ cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0);
+
+ /* Process all the lists at the end of the frame */
+ if (ohci->ctl & OHCI_CTL_PLE) {
+ int n;
+
+ n = ohci->frame_number & 0x1f;
+ ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
+ }
+
+ /* Cancel all pending packets if either of the lists has been disabled. */
+ if (ohci->async_td &&
+ ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ ohci->old_ctl = ohci->ctl;
+ ohci_process_lists(ohci, 0);
+
+ /* Frame boundary, so do EOF stuf here */
+ ohci->frt = ohci->fit;
+
+ /* XXX: endianness */
+ ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
+ hcca.frame = cpu_to_le32(ohci->frame_number);
+
+ if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
+ if (!ohci->done)
+ abort();
+ if (ohci->intr & ohci->intr_status)
+ ohci->done |= 1;
+ hcca.done = cpu_to_le32(ohci->done);
+ ohci->done = 0;
+ ohci->done_count = 7;
+ ohci_set_interrupt(ohci, OHCI_INTR_WD);
+ }
+
+ if (ohci->done_count != 7 && ohci->done_count != 0)
+ ohci->done_count--;
+
+ /* Do SOF stuff here */
+ ohci_sof(ohci);
+
+ /* Writeback HCCA */
+ cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1);
+}
+
+/* Start sending SOF tokens across the USB bus, lists are processed in
+ * next frame
+ */
+static int ohci_bus_start(OHCIState *ohci)
+{
+ ohci->eof_timer = qemu_new_timer(vm_clock,
+ ohci_frame_boundary,
+ ohci);
+
+ if (ohci->eof_timer == NULL) {
+ fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", ohci->name);
+ /* TODO: Signal unrecoverable error */
+ return 0;
+ }
+
+ dprintf("usb-ohci: %s: USB Operational\n", ohci->name);
+
+ ohci_sof(ohci);
+
+ return 1;
+}
+
+/* Stop sending SOF tokens on the bus */
+static void ohci_bus_stop(OHCIState *ohci)
+{
+ if (ohci->eof_timer)
+ qemu_del_timer(ohci->eof_timer);
+ ohci->eof_timer = NULL;
+}
+
+/* Sets a flag in a port status register but only set it if the port is
+ * connected, if not set ConnectStatusChange flag. If flag is enabled
+ * return 1.
+ */
+static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
+{
+ int ret = 1;
+
+ /* writing a 0 has no effect */
+ if (val == 0)
+ return 0;
+
+ /* If CurrentConnectStatus is cleared we set
+ * ConnectStatusChange
+ */
+ if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
+ if (ohci->rhstatus & OHCI_RHS_DRWE) {
+ /* TODO: CSC is a wakeup event */
+ }
+ return 0;
+ }
+
+ if (ohci->rhport[i].ctrl & val)
+ ret = 0;
+
+ /* set the bit */
+ ohci->rhport[i].ctrl |= val;
+
+ return ret;
+}
+
+/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
+static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
+{
+ val &= OHCI_FMI_FI;
+
+ if (val != ohci->fi) {
+ dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
+ ohci->name, ohci->fi, ohci->fi);
+ }
+
+ ohci->fi = val;
+}
+
+static void ohci_port_power(OHCIState *ohci, int i, int p)
+{
+ if (p) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
+ } else {
+ ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
+ OHCI_PORT_CCS|
+ OHCI_PORT_PSS|
+ OHCI_PORT_PRS);
+ }
+}
+
+/* Set HcControlRegister */
+static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+ uint32_t new_state;
+
+ old_state = ohci->ctl & OHCI_CTL_HCFS;
+ ohci->ctl = val;
+ new_state = ohci->ctl & OHCI_CTL_HCFS;
+
+ /* no state change */
+ if (old_state == new_state)
+ return;
+
+ switch (new_state) {
+ case OHCI_USB_OPERATIONAL:
+ ohci_bus_start(ohci);
+ break;
+ case OHCI_USB_SUSPEND:
+ ohci_bus_stop(ohci);
+ dprintf("usb-ohci: %s: USB Suspended\n", ohci->name);
+ break;
+ case OHCI_USB_RESUME:
+ dprintf("usb-ohci: %s: USB Resume\n", ohci->name);
+ break;
+ case OHCI_USB_RESET:
+ ohci_reset(ohci);
+ dprintf("usb-ohci: %s: USB Reset\n", ohci->name);
+ break;
+ }
+}
+
+static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
+{
+ uint16_t fr;
+ int64_t tks;
+
+ if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
+ return (ohci->frt << 31);
+
+ /* Being in USB operational state guarnatees sof_time was
+ * set already.
+ */
+ tks = qemu_get_clock(vm_clock) - ohci->sof_time;
+
+ /* avoid muldiv if possible */
+ if (tks >= usb_frame_time)
+ return (ohci->frt << 31);
+
+ tks = muldiv64(1, tks, usb_bit_time);
+ fr = (uint16_t)(ohci->fi - tks);
+
+ return (ohci->frt << 31) | fr;
+}
+
+
+/* Set root hub status */
+static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+
+ old_state = ohci->rhstatus;
+
+ /* write 1 to clear OCIC */
+ if (val & OHCI_RHS_OCIC)
+ ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+ if (val & OHCI_RHS_LPS) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 0);
+ dprintf("usb-ohci: powered down all ports\n");
+ }
+
+ if (val & OHCI_RHS_LPSC) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 1);
+ dprintf("usb-ohci: powered up all ports\n");
+ }
+
+ if (val & OHCI_RHS_DRWE)
+ ohci->rhstatus |= OHCI_RHS_DRWE;
+
+ if (val & OHCI_RHS_CRWE)
+ ohci->rhstatus &= ~OHCI_RHS_DRWE;
+
+ if (old_state != ohci->rhstatus)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+}
+
+/* Set root hub port status */
+static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
+{
+ uint32_t old_state;
+ OHCIPort *port;
+
+ port = &ohci->rhport[portnum];
+ old_state = port->ctrl;
+
+ /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
+ if (val & OHCI_PORT_WTC)
+ port->ctrl &= ~(val & OHCI_PORT_WTC);
+
+ if (val & OHCI_PORT_CCS)
+ port->ctrl &= ~OHCI_PORT_PES;
+
+ ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS))
+ dprintf("usb-ohci: port %d: SUSPEND\n", portnum);
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
+ dprintf("usb-ohci: port %d: RESET\n", portnum);
+ usb_send_msg(port->port.dev, USB_MSG_RESET);
+ port->ctrl &= ~OHCI_PORT_PRS;
+ /* ??? Should this also set OHCI_PORT_PESC. */
+ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
+ }
+
+ /* Invert order here to ensure in ambiguous case, device is
+ * powered up...
+ */
+ if (val & OHCI_PORT_LSDA)
+ ohci_port_power(ohci, portnum, 0);
+ if (val & OHCI_PORT_PPS)
+ ohci_port_power(ohci, portnum, 1);
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+
+ return;
+}
+
+static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
+{
+ OHCIState *ohci = ptr;
+
+ addr -= ohci->mem_base;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+ return 0xffffffff;
+ }
+
+ if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
+ }
+
+ switch (addr >> 2) {
+ case 0: /* HcRevision */
+ return 0x10;
+
+ case 1: /* HcControl */
+ return ohci->ctl;
+
+ case 2: /* HcCommandStatus */
+ return ohci->status;
+
+ case 3: /* HcInterruptStatus */
+ return ohci->intr_status;
+
+ case 4: /* HcInterruptEnable */
+ case 5: /* HcInterruptDisable */
+ return ohci->intr;
+
+ case 6: /* HcHCCA */
+ return ohci->hcca;
+
+ case 7: /* HcPeriodCurrentED */
+ return ohci->per_cur;
+
+ case 8: /* HcControlHeadED */
+ return ohci->ctrl_head;
+
+ case 9: /* HcControlCurrentED */
+ return ohci->ctrl_cur;
+
+ case 10: /* HcBulkHeadED */
+ return ohci->bulk_head;
+
+ case 11: /* HcBulkCurrentED */
+ return ohci->bulk_cur;
+
+ case 12: /* HcDoneHead */
+ return ohci->done;
+
+ case 13: /* HcFmInterval */
+ return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
+
+ case 14: /* HcFmRemaining */
+ return ohci_get_frame_remaining(ohci);
+
+ case 15: /* HcFmNumber */
+ return ohci->frame_number;
+
+ case 16: /* HcPeriodicStart */
+ return ohci->pstart;
+
+ case 17: /* HcLSThreshold */
+ return ohci->lst;
+
+ case 18: /* HcRhDescriptorA */
+ return ohci->rhdesc_a;
+
+ case 19: /* HcRhDescriptorB */
+ return ohci->rhdesc_b;
+
+ case 20: /* HcRhStatus */
+ return ohci->rhstatus;
+
+ /* PXA27x specific registers */
+ case 24: /* HcStatus */
+ return ohci->hstatus & ohci->hmask;
+
+ case 25: /* HcHReset */
+ return ohci->hreset;
+
+ case 26: /* HcHInterruptEnable */
+ return ohci->hmask;
+
+ case 27: /* HcHInterruptTest */
+ return ohci->htest;
+
+ default:
+ fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+ return 0xffffffff;
+ }
+}
+
+static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ OHCIState *ohci = ptr;
+
+ addr -= ohci->mem_base;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+ return;
+ }
+
+ if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
+ return;
+ }
+
+ switch (addr >> 2) {
+ case 1: /* HcControl */
+ ohci_set_ctl(ohci, val);
+ break;
+
+ case 2: /* HcCommandStatus */
+ /* 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);
+ break;
+
+ case 3: /* HcInterruptStatus */
+ ohci->intr_status &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 4: /* HcInterruptEnable */
+ ohci->intr |= val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 5: /* HcInterruptDisable */
+ ohci->intr &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 6: /* HcHCCA */
+ ohci->hcca = val & OHCI_HCCA_MASK;
+ break;
+
+ case 8: /* HcControlHeadED */
+ ohci->ctrl_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 9: /* HcControlCurrentED */
+ ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 10: /* HcBulkHeadED */
+ ohci->bulk_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 11: /* HcBulkCurrentED */
+ ohci->bulk_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 13: /* HcFmInterval */
+ ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
+ ohci->fit = (val & OHCI_FMI_FIT) >> 31;
+ ohci_set_frame_interval(ohci, val);
+ break;
+
+ case 15: /* HcFmNumber */
+ break;
+
+ case 16: /* HcPeriodicStart */
+ ohci->pstart = val & 0xffff;
+ break;
+
+ case 17: /* HcLSThreshold */
+ ohci->lst = val & 0xffff;
+ break;
+
+ case 18: /* HcRhDescriptorA */
+ ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
+ ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
+ break;
+
+ case 19: /* HcRhDescriptorB */
+ break;
+
+ case 20: /* HcRhStatus */
+ ohci_set_hub_status(ohci, val);
+ break;
+
+ /* PXA27x specific registers */
+ case 24: /* HcStatus */
+ ohci->hstatus &= ~(val & ohci->hmask);
+
+ case 25: /* HcHReset */
+ ohci->hreset = val & ~OHCI_HRESET_FSBIR;
+ if (val & OHCI_HRESET_FSBIR)
+ ohci_reset(ohci);
+ break;
+
+ case 26: /* HcHInterruptEnable */
+ ohci->hmask = val;
+ break;
+
+ case 27: /* HcHInterruptTest */
+ ohci->htest = val;
+ break;
+
+ default:
+ fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+ break;
+ }
+}
+
+/* Only dword reads are defined on OHCI register space */
+static CPUReadMemoryFunc *ohci_readfn[3]={
+ ohci_mem_read,
+ ohci_mem_read,
+ ohci_mem_read
+};
+
+/* Only dword writes are defined on OHCI register space */
+static CPUWriteMemoryFunc *ohci_writefn[3]={
+ ohci_mem_write,
+ ohci_mem_write,
+ ohci_mem_write
+};
+
+static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn,
+ qemu_irq irq, enum ohci_type type, const char *name)
+{
+ int i;
+
+ if (usb_frame_time == 0) {
+#ifdef OHCI_TIME_WARP
+ usb_frame_time = ticks_per_sec;
+ usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000);
+#else
+ usb_frame_time = muldiv64(1, ticks_per_sec, 1000);
+ if (ticks_per_sec >= USB_HZ) {
+ usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ);
+ } else {
+ usb_bit_time = 1;
+ }
+#endif
+ dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n",
+ usb_frame_time, usb_bit_time);
+ }
+
+ ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci);
+ ohci->name = name;
+
+ ohci->irq = irq;
+ ohci->type = type;
+
+ ohci->num_ports = num_ports;
+ for (i = 0; i < num_ports; i++) {
+ qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach);
+ }
+
+ ohci->async_td = 0;
+ qemu_register_reset(ohci_reset, ohci);
+ ohci_reset(ohci);
+}
+
+typedef struct {
+ PCIDevice pci_dev;
+ OHCIState state;
+} OHCIPCIState;
+
+static void ohci_mapfunc(PCIDevice *pci_dev, int i,
+ uint32_t addr, uint32_t size, int type)
+{
+ OHCIPCIState *ohci = (OHCIPCIState *)pci_dev;
+ ohci->state.mem_base = addr;
+ cpu_register_physical_memory(addr, size, ohci->state.mem);
+}
+
+void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn)
+{
+ OHCIPCIState *ohci;
+ int vid = 0x106b;
+ int did = 0x003f;
+
+ ohci = (OHCIPCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci),
+ devfn, NULL, NULL);
+ if (ohci == NULL) {
+ fprintf(stderr, "usb-ohci: Failed to register PCI device\n");
+ 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 */
+
+ usb_ohci_init(&ohci->state, num_ports, devfn, ohci->pci_dev.irq[0],
+ OHCI_TYPE_PCI, ohci->pci_dev.name);
+
+ pci_register_io_region((struct PCIDevice *)ohci, 0, 256,
+ PCI_ADDRESS_SPACE_MEM, ohci_mapfunc);
+}
+
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+ qemu_irq irq)
+{
+ OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState));
+
+ usb_ohci_init(ohci, num_ports, devfn, irq,
+ OHCI_TYPE_PXA, "OHCI USB");
+ ohci->mem_base = base;
+
+ cpu_register_physical_memory(ohci->mem_base, 0x1000, ohci->mem);
+}
diff --git a/hw/usb.c b/hw/usb.c
new file mode 100644
index 0000000..c17266d
--- /dev/null
+++ b/hw/usb.c
@@ -0,0 +1,231 @@
+/*
+ * QEMU USB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 2008 Generic packet handler rewrite by Max Krasnyansky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+
+void usb_attach(USBPort *port, USBDevice *dev)
+{
+ port->attach(port, dev);
+}
+
+/**********************/
+
+/* generic USB device helpers (you are not forced to use them when
+ writing your USB device driver, but they help handling the
+ protocol)
+*/
+
+#define SETUP_STATE_IDLE 0
+#define SETUP_STATE_DATA 1
+#define SETUP_STATE_ACK 2
+
+static int do_token_setup(USBDevice *s, USBPacket *p)
+{
+ int request, value, index;
+ int ret = 0;
+
+ if (p->len != 8)
+ return USB_RET_STALL;
+
+ memcpy(s->setup_buf, p->data, 8);
+ s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
+ s->setup_index = 0;
+
+ request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+ value = (s->setup_buf[3] << 8) | s->setup_buf[2];
+ index = (s->setup_buf[5] << 8) | s->setup_buf[4];
+
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ ret = s->handle_control(s, request, value, index,
+ s->setup_len, s->data_buf);
+ if (ret < 0)
+ return ret;
+
+ if (ret < s->setup_len)
+ s->setup_len = ret;
+ s->setup_state = SETUP_STATE_DATA;
+ } else {
+ if (s->setup_len == 0)
+ s->setup_state = SETUP_STATE_ACK;
+ else
+ s->setup_state = SETUP_STATE_DATA;
+ }
+
+ return ret;
+}
+
+static int do_token_in(USBDevice *s, USBPacket *p)
+{
+ int request, value, index;
+ int ret = 0;
+
+ if (p->devep != 0)
+ return s->handle_data(s, p);
+
+ request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+ value = (s->setup_buf[3] << 8) | s->setup_buf[2];
+ index = (s->setup_buf[5] << 8) | s->setup_buf[4];
+
+ switch(s->setup_state) {
+ case SETUP_STATE_ACK:
+ if (!(s->setup_buf[0] & USB_DIR_IN)) {
+ s->setup_state = SETUP_STATE_IDLE;
+ ret = s->handle_control(s, request, value, index,
+ s->setup_len, s->data_buf);
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ /* return 0 byte */
+ return 0;
+
+ case SETUP_STATE_DATA:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ int len = s->setup_len - s->setup_index;
+ if (len > p->len)
+ len = p->len;
+ memcpy(p->data, s->data_buf + s->setup_index, len);
+ s->setup_index += len;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ return len;
+ }
+
+ s->setup_state = SETUP_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+static int do_token_out(USBDevice *s, USBPacket *p)
+{
+ if (p->devep != 0)
+ return s->handle_data(s, p);
+
+ switch(s->setup_state) {
+ case SETUP_STATE_ACK:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ s->setup_state = SETUP_STATE_IDLE;
+ /* transfer OK */
+ } else {
+ /* ignore additional output */
+ }
+ return 0;
+
+ case SETUP_STATE_DATA:
+ if (!(s->setup_buf[0] & USB_DIR_IN)) {
+ int len = s->setup_len - s->setup_index;
+ if (len > p->len)
+ len = p->len;
+ memcpy(s->data_buf + s->setup_index, p->data, len);
+ s->setup_index += len;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ return len;
+ }
+
+ s->setup_state = SETUP_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+/*
+ * Generic packet handler.
+ * Called by the HC (host controller).
+ *
+ * Returns length of the transaction or one of the USB_RET_XXX codes.
+ */
+int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
+{
+ switch(p->pid) {
+ case USB_MSG_ATTACH:
+ s->state = USB_STATE_ATTACHED;
+ return 0;
+
+ case USB_MSG_DETACH:
+ s->state = USB_STATE_NOTATTACHED;
+ return 0;
+
+ case USB_MSG_RESET:
+ s->remote_wakeup = 0;
+ s->addr = 0;
+ s->state = USB_STATE_DEFAULT;
+ s->handle_reset(s);
+ return 0;
+ }
+
+ /* Rest of the PIDs must match our address */
+ if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
+ return USB_RET_NODEV;
+
+ switch (p->pid) {
+ case USB_TOKEN_SETUP:
+ return do_token_setup(s, p);
+
+ case USB_TOKEN_IN:
+ return do_token_in(s, p);
+
+ case USB_TOKEN_OUT:
+ return do_token_out(s, p);
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+/* XXX: fix overflow */
+int set_usb_string(uint8_t *buf, const char *str)
+{
+ int len, i;
+ uint8_t *q;
+
+ q = buf;
+ len = strlen(str);
+ *q++ = 2 * len + 2;
+ *q++ = 3;
+ for(i = 0; i < len; i++) {
+ *q++ = str[i];
+ *q++ = 0;
+ }
+ return q - buf;
+}
+
+/* Send an internal message to a USB device. */
+void usb_send_msg(USBDevice *dev, int msg)
+{
+ USBPacket p;
+ memset(&p, 0, sizeof(p));
+ p.pid = msg;
+ dev->handle_packet(dev, &p);
+
+ /* This _must_ be synchronous */
+}
diff --git a/hw/usb.h b/hw/usb.h
new file mode 100644
index 0000000..1a353bb
--- /dev/null
+++ b/hw/usb.h
@@ -0,0 +1,291 @@
+/*
+ * QEMU USB API
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define USB_TOKEN_SETUP 0x2d
+#define USB_TOKEN_IN 0x69 /* device -> host */
+#define USB_TOKEN_OUT 0xe1 /* host -> device */
+
+/* specific usb messages, also sent in the 'pid' parameter */
+#define USB_MSG_ATTACH 0x100
+#define USB_MSG_DETACH 0x101
+#define USB_MSG_RESET 0x102
+
+#define USB_RET_NODEV (-1)
+#define USB_RET_NAK (-2)
+#define USB_RET_STALL (-3)
+#define USB_RET_BABBLE (-4)
+#define USB_RET_ASYNC (-5)
+
+#define USB_SPEED_LOW 0
+#define USB_SPEED_FULL 1
+#define USB_SPEED_HIGH 2
+
+#define USB_STATE_NOTATTACHED 0
+#define USB_STATE_ATTACHED 1
+//#define USB_STATE_POWERED 2
+#define USB_STATE_DEFAULT 3
+//#define USB_STATE_ADDRESS 4
+//#define USB_STATE_CONFIGURED 5
+#define USB_STATE_SUSPENDED 6
+
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_STILL_IMAGE 6
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_CDC_DATA 0x0a
+#define USB_CLASS_CSCID 0x0b
+#define USB_CLASS_CONTENT_SEC 0x0d
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+#define USB_DIR_OUT 0
+#define USB_DIR_IN 0x80
+
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define InterfaceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+#define USB_DEVICE_SELF_POWERED 0
+#define USB_DEVICE_REMOTE_WAKEUP 1
+
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+
+#define USB_ENDPOINT_XFER_CONTROL 0
+#define USB_ENDPOINT_XFER_ISOC 1
+#define USB_ENDPOINT_XFER_BULK 2
+#define USB_ENDPOINT_XFER_INT 3
+
+typedef struct USBPort USBPort;
+typedef struct USBDevice USBDevice;
+typedef struct USBPacket USBPacket;
+
+/* definition of a USB device */
+struct USBDevice {
+ void *opaque;
+
+ /*
+ * Process USB packet.
+ * Called by the HC (Host Controller).
+ *
+ * Returns length of the transaction
+ * or one of the USB_RET_XXX codes.
+ */
+ int (*handle_packet)(USBDevice *dev, USBPacket *p);
+
+ /*
+ * Called when device is destroyed.
+ */
+ void (*handle_destroy)(USBDevice *dev);
+
+ int speed;
+
+ /* The following fields are used by the generic USB device
+ layer. They are here just to avoid creating a new structure
+ for them. */
+
+ /*
+ * Reset the device
+ */
+ void (*handle_reset)(USBDevice *dev);
+
+ /*
+ * Process control request.
+ * Called from handle_packet().
+ *
+ * Returns length or one of the USB_RET_ codes.
+ */
+ int (*handle_control)(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data);
+
+ /*
+ * Process data transfers (both BULK and ISOC).
+ * Called from handle_packet().
+ *
+ * Returns length or one of the USB_RET_ codes.
+ */
+ int (*handle_data)(USBDevice *dev, USBPacket *p);
+
+ uint8_t addr;
+ char devname[32];
+
+ int state;
+ uint8_t setup_buf[8];
+ uint8_t data_buf[1024];
+ int remote_wakeup;
+ int setup_state;
+ int setup_len;
+ int setup_index;
+};
+
+typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev);
+
+/* USB port on which a device can be connected */
+struct USBPort {
+ USBDevice *dev;
+ usb_attachfn attach;
+ void *opaque;
+ int index; /* internal port index, may be used with the opaque */
+ struct USBPort *next; /* Used internally by qemu. */
+};
+
+typedef void USBCallback(USBPacket * packet, void *opaque);
+
+/* Structure used to hold information about an active USB packet. */
+struct USBPacket {
+ /* Data fields for use by the driver. */
+ int pid;
+ uint8_t devaddr;
+ uint8_t devep;
+ uint8_t *data;
+ int len;
+ /* Internal use by the USB layer. */
+ USBCallback *complete_cb;
+ void *complete_opaque;
+ USBCallback *cancel_cb;
+ void *cancel_opaque;
+};
+
+/* Defer completion of a USB packet. The hadle_packet routine should then
+ return USB_RET_ASYNC. Packets that complete immediately (before
+ handle_packet returns) should not call this method. */
+static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
+ void * opaque)
+{
+ p->cancel_cb = cancel;
+ p->cancel_opaque = opaque;
+}
+
+/* Notify the controller that an async packet is complete. This should only
+ be called for packets previously deferred with usb_defer_packet, and
+ should never be called from within handle_packet. */
+static inline void usb_packet_complete(USBPacket *p)
+{
+ p->complete_cb(p, p->complete_opaque);
+}
+
+/* Cancel an active packet. The packed must have been deferred with
+ usb_defer_packet, and not yet completed. */
+static inline void usb_cancel_packet(USBPacket * p)
+{
+ p->cancel_cb(p, p->cancel_opaque);
+}
+
+int usb_device_add_dev(USBDevice *dev);
+int usb_device_del_addr(int bus_num, int addr);
+void usb_attach(USBPort *port, USBDevice *dev);
+int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
+int set_usb_string(uint8_t *buf, const char *str);
+void usb_send_msg(USBDevice *dev, int msg);
+
+/* usb hub */
+USBDevice *usb_hub_init(int nb_ports);
+
+/* usb-linux.c */
+USBDevice *usb_host_device_open(const char *devname);
+int usb_host_device_close(const char *devname);
+void usb_host_info(void);
+
+/* usb-hid.c */
+USBDevice *usb_mouse_init(void);
+USBDevice *usb_tablet_init(void);
+USBDevice *usb_keyboard_init(void);
+
+/* usb-msd.c */
+USBDevice *usb_msd_init(const char *filename);
+
+/* usb-net.c */
+USBDevice *usb_net_init(NICInfo *nd);
+
+/* usb-wacom.c */
+USBDevice *usb_wacom_init(void);
+
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
+/* usb ports of the VM */
+
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+ usb_attachfn attach);
+
+#define VM_USB_HUB_SIZE 8
+
+/* usb-musb.c */
+enum musb_irq_source_e {
+ musb_irq_suspend = 0,
+ musb_irq_resume,
+ musb_irq_rst_babble,
+ musb_irq_sof,
+ musb_irq_connect,
+ musb_irq_disconnect,
+ musb_irq_vbus_request,
+ musb_irq_vbus_error,
+ musb_irq_rx,
+ musb_irq_tx,
+ musb_set_vbus,
+ musb_set_session,
+ __musb_irq_max,
+};
+
+struct musb_s;
+struct musb_s *musb_init(qemu_irq *irqs);
+uint32_t musb_core_intr_get(struct musb_s *s);
+void musb_core_intr_clear(struct musb_s *s, uint32_t mask);
+void musb_set_size(struct musb_s *s, int epnum, int size, int is_tx);