aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:43:59 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-10 15:43:59 -0800
commitc27f813900a3c114562efbb8df1065e94766fc48 (patch)
treed95919283707dcab61009e27007374a745c9541e /hw
parent0852ad57fa372f9b2854e4df685eaba8d8ef6790 (diff)
downloadexternal_qemu-c27f813900a3c114562efbb8df1065e94766fc48.zip
external_qemu-c27f813900a3c114562efbb8df1065e94766fc48.tar.gz
external_qemu-c27f813900a3c114562efbb8df1065e94766fc48.tar.bz2
auto import from //branches/cupcake/...@130745
Diffstat (limited to 'hw')
-rw-r--r--hw/android_arm.c45
-rw-r--r--hw/arm-misc.h49
-rw-r--r--hw/arm_boot.c218
-rw-r--r--hw/arm_gic.c747
-rw-r--r--hw/arm_pic.c12
-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.c25
-rw-r--r--hw/devices.h74
-rw-r--r--hw/dma.c31
-rw-r--r--hw/goldfish_audio.c4
-rw-r--r--hw/goldfish_battery.c2
-rw-r--r--hw/goldfish_device.c2
-rw-r--r--hw/goldfish_events_device.c4
-rw-r--r--hw/goldfish_fb.c4
-rw-r--r--hw/goldfish_interrupt.c2
-rw-r--r--hw/goldfish_memlog.c2
-rw-r--r--hw/goldfish_mmc.c5
-rw-r--r--hw/goldfish_nand.c11
-rw-r--r--hw/goldfish_switch.c2
-rw-r--r--hw/goldfish_timer.c3
-rw-r--r--hw/goldfish_trace.c3
-rw-r--r--hw/goldfish_trace.h3
-rw-r--r--hw/goldfish_tty.c9
-rw-r--r--hw/hw.h110
-rw-r--r--hw/irq.c2
-rw-r--r--hw/irq.h2
-rw-r--r--hw/isa.h27
-rw-r--r--hw/pc.h148
-rw-r--r--hw/pci.c262
-rw-r--r--hw/pci.h142
-rw-r--r--hw/pci_host.h2
-rw-r--r--hw/pcmcia.h50
-rw-r--r--hw/pxa.h227
-rw-r--r--hw/scsi-disk.c709
-rw-r--r--hw/scsi-disk.h36
-rw-r--r--hw/smc91c111.c25
-rw-r--r--hw/usb-hid.c609
-rw-r--r--hw/usb-hub.c79
-rw-r--r--hw/usb-msd.c304
-rw-r--r--hw/usb-ohci.c662
-rw-r--r--hw/usb.c287
-rw-r--r--hw/usb.h160
46 files changed, 5076 insertions, 805 deletions
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 3455054..efc8ba1 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -9,10 +9,18 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#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;
@@ -49,18 +57,23 @@ uint32_t switch_test_write(void *opaque, uint32_t state)
}
#endif
-static void android_arm_init(int ram_size, int vga_ram_size,
- int boot_device, DisplayState *ds, const char **fd_filename, int snapshot,
- const char *kernel_filename, const char *kernel_cmdline,
- const char *initrd_filename)
+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_arm_set_model(env, ARM_CPUID_ARM926);
+ env = cpu_init(cpu_model);
register_savevm( "cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, env );
@@ -108,8 +121,11 @@ static void android_arm_init(int ram_size, int vga_ram_size,
goldfish_audio_init(0xff004000, 0, audio_input_source);
}
#endif
- if (bs_table[0])
- goldfish_mmc_init(0xff005000, 0, bs_table[0]);
+ {
+ 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);
@@ -140,8 +156,15 @@ static void android_arm_init(int ram_size, int vga_ram_size,
}
#endif
- arm_load_kernel(ram_size, kernel_filename, kernel_cmdline,
- initrd_filename, 1441);
+ 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 = {
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
index 0e28d4a..5990961 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -1,13 +1,15 @@
-/*
+/*
* ARM kernel loader.
*
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the GPL.
*/
-#include "vl.h"
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000
@@ -24,12 +26,39 @@ static uint32_t bootloader[] = {
0 /* Kernel entry point. Set by integratorcp_init. */
};
-static void set_kernel_args(uint32_t ram_size, int initrd_size,
- const char *kernel_cmdline)
+/* 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 *)(phys_ram_base + KERNEL_ARGS_ADDR);
+ p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
/* ATAG_CORE */
stl_raw(p++, 5);
stl_raw(p++, 0x54410001);
@@ -37,69 +66,186 @@ static void set_kernel_args(uint32_t ram_size, int initrd_size,
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++, ram_size);
- stl_raw(p++, 0);
+ 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++, INITRD_LOAD_ADDR);
+ stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
stl_raw(p++, initrd_size);
}
- if (kernel_cmdline && *kernel_cmdline) {
+ if (info->kernel_cmdline && *info->kernel_cmdline) {
/* ATAG_CMDLINE */
int cmdline_size;
- cmdline_size = strlen(kernel_cmdline);
- memcpy (p + 2, kernel_cmdline, cmdline_size + 1);
+ 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);
}
-void arm_load_kernel(int ram_size, const char *kernel_filename,
- const char *kernel_cmdline, const char *initrd_filename,
- int board_id)
+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 (!kernel_filename) {
+ if (!info->kernel_filename) {
fprintf(stderr, "Kernel image must be specified\n");
exit(1);
}
- kernel_size = load_image(kernel_filename,
- phys_ram_base + KERNEL_LOAD_ADDR);
+
+ 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) {
- fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ 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 (initrd_filename) {
- initrd_size = load_image(initrd_filename,
- phys_ram_base + INITRD_LOAD_ADDR);
- if (initrd_size < 0) {
- fprintf(stderr, "qemu: could not load initrd '%s'\n",
- initrd_filename);
- exit(1);
- }
+ if (!is_linux) {
+ /* Jump to the entry point. */
+ env->regs[15] = entry & 0xfffffffe;
+ env->thumb = entry & 1;
} else {
- initrd_size = 0;
+ 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);
}
- bootloader[1] |= board_id & 0xff;
- bootloader[2] |= (board_id >> 8) & 0xff;
- bootloader[5] = KERNEL_ARGS_ADDR;
- bootloader[6] = KERNEL_LOAD_ADDR;
- for (n = 0; n < sizeof(bootloader) / 4; n++)
- stl_raw(phys_ram_base + (n * 4), bootloader[n]);
- set_kernel_args(ram_size, initrd_size, kernel_cmdline);
}
-
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
index c228c04..1fe55b7 100644
--- a/hw/arm_pic.c
+++ b/hw/arm_pic.c
@@ -1,4 +1,4 @@
-/*
+/*
* Generic ARM Programmable Interrupt Controller support.
*
* Copyright (c) 2006 CodeSourcery.
@@ -7,8 +7,8 @@
* This code is licenced under the LGPL
*/
-#include "vl.h"
-#include "arm_pic.h"
+#include "hw.h"
+#include "arm-misc.h"
/* Stub functions for hardware that doesn't exist. */
void pic_info(void)
@@ -19,7 +19,8 @@ void irq_info(void)
{
}
-/* Input 0 is IRQ and input 1 is FIQ */
+
+/* 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;
@@ -37,8 +38,7 @@ static void arm_pic_cpu_handler(void *opaque, int irq, int level)
cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ);
break;
default:
- cpu_abort(env, "arm_pic_cpu_handler: Bad interrput line %d\n",
- irq);
+ cpu_abort(env, "arm_pic_cpu_handler: Bad interrput line %d\n", irq);
}
}
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
index a43b417..2aa4d3b 100644
--- a/hw/cdrom.c
+++ b/hw/cdrom.c
@@ -1,8 +1,8 @@
/*
* 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
@@ -25,7 +25,8 @@
/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
here. */
-#include <vl.h>
+#include "qemu-common.h"
+#include "scsi-disk.h"
static void lba_to_msf(uint8_t *buf, int lba)
{
@@ -41,7 +42,7 @@ 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;
@@ -85,7 +86,7 @@ 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 */
@@ -101,7 +102,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
*q++ = 1; /* first track */
*q++ = 0x00; /* disk type */
*q++ = 0x00;
-
+
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
@@ -113,7 +114,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
*q++ = 1; /* last track */
*q++ = 0x00;
*q++ = 0x00;
-
+
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
@@ -138,14 +139,14 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
- *q++ = 0;
+ *q++ = 0;
lba_to_msf(q, 0);
q += 3;
} else {
- *q++ = 0;
- *q++ = 0;
- *q++ = 0;
- *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
}
len = q - buf;
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
index ea13eae..00c6332 100644
--- a/hw/dma.c
+++ b/hw/dma.c
@@ -21,7 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "hw.h"
+#include "isa.h"
/* #define DEBUG_DMA */
@@ -383,7 +384,7 @@ void DMA_register_channel (int nchan,
int DMA_read_memory (int nchan, void *buf, int pos, int len)
{
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
- target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
if (r->mode & 0x20) {
int i;
@@ -405,7 +406,7 @@ int DMA_read_memory (int nchan, void *buf, int pos, int 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_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
if (r->mode & 0x20) {
int i;
@@ -438,11 +439,18 @@ static void dma_reset(void *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)
{
- const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
+ static const int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
int i;
d->dshift = dshift;
@@ -470,6 +478,9 @@ static void dma_init2(struct dma_cont *d, int base, int dshift,
}
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)
@@ -481,12 +492,12 @@ static void dma_save (QEMUFile *f, void *opaque)
qemu_put_8s (f, &d->command);
qemu_put_8s (f, &d->mask);
qemu_put_8s (f, &d->flip_flop);
- qemu_put_be32s (f, &d->dshift);
+ qemu_put_be32 (f, d->dshift);
for (i = 0; i < 4; ++i) {
struct dma_regs *r = &d->regs[i];
- qemu_put_be32s (f, &r->now[0]);
- qemu_put_be32s (f, &r->now[1]);
+ 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);
@@ -509,12 +520,12 @@ static int dma_load (QEMUFile *f, void *opaque, int version_id)
qemu_get_8s (f, &d->command);
qemu_get_8s (f, &d->mask);
qemu_get_8s (f, &d->flip_flop);
- qemu_get_be32s (f, &d->dshift);
+ d->dshift=qemu_get_be32 (f);
for (i = 0; i < 4; ++i) {
struct dma_regs *r = &d->regs[i];
- qemu_get_be32s (f, &r->now[0]);
- qemu_get_be32s (f, &r->now[1]);
+ 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);
diff --git a/hw/goldfish_audio.c b/hw/goldfish_audio.c
index 3472d73..d0a44b5 100644
--- a/hw/goldfish_audio.c
+++ b/hw/goldfish_audio.c
@@ -9,10 +9,10 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_device.h"
#include "audio/audio.h"
-#include "android_debug.h"
+#include "qemu_debug.h"
#include "android/globals.h"
#define DEBUG 1
diff --git a/hw/goldfish_battery.c b/hw/goldfish_battery.c
index f8452db..d9ef785 100644
--- a/hw/goldfish_battery.c
+++ b/hw/goldfish_battery.c
@@ -9,7 +9,7 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_device.h"
#include "power_supply.h"
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
index a7d80a6..2c9dd6e 100644
--- a/hw/goldfish_device.c
+++ b/hw/goldfish_device.c
@@ -9,7 +9,7 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "arm_pic.h"
#include "goldfish_device.h"
diff --git a/hw/goldfish_events_device.c b/hw/goldfish_events_device.c
index 66ac2fc..4cb2904 100644
--- a/hw/goldfish_events_device.c
+++ b/hw/goldfish_events_device.c
@@ -9,8 +9,8 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
-#include "android_events.h"
+#include "qemu_file.h"
+#include "android/hw-events.h"
#include "irq.h"
#if 0
diff --git a/hw/goldfish_fb.c b/hw/goldfish_fb.c
index db3e25b..71cede2 100644
--- a/hw/goldfish_fb.c
+++ b/hw/goldfish_fb.c
@@ -9,8 +9,8 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
-#include "android.h"
+#include "qemu_file.h"
+#include "android/android.h"
#include "goldfish_device.h"
#include "framebuffer.h"
diff --git a/hw/goldfish_interrupt.c b/hw/goldfish_interrupt.c
index c89130d..2cba649 100644
--- a/hw/goldfish_interrupt.c
+++ b/hw/goldfish_interrupt.c
@@ -9,7 +9,7 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "arm_pic.h"
#include "goldfish_device.h"
#include "irq.h"
diff --git a/hw/goldfish_memlog.c b/hw/goldfish_memlog.c
index e2a89a6..98fcffc 100644
--- a/hw/goldfish_memlog.c
+++ b/hw/goldfish_memlog.c
@@ -13,7 +13,7 @@
#include <fcntl.h>
#include <string.h>
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_device.h"
#include "audio/audio.h"
diff --git a/hw/goldfish_mmc.c b/hw/goldfish_mmc.c
index 0daaebd..272f403 100644
--- a/hw/goldfish_mmc.c
+++ b/hw/goldfish_mmc.c
@@ -9,10 +9,11 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_device.h"
#include "mmc.h"
#include "sd.h"
+#include "block.h"
enum {
/* status register */
@@ -190,7 +191,7 @@ static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd,
uint8_t exponent;
uint32_t m;
- bdrv_get_geometry(s->bs, &sector_count);
+ 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
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
index e3042bf..61b075e 100644
--- a/hw/goldfish_nand.c
+++ b/hw/goldfish_nand.c
@@ -9,12 +9,12 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_nand_reg.h"
#include "goldfish_nand.h"
-#include "android_utils.h"
-#include "android_debug.h"
-#include "android.h"
+#include "android/utils/tempfile.h"
+#include "qemu_debug.h"
+#include "android/android.h"
#define DEBUG 1
#if DEBUG
@@ -153,9 +153,6 @@ static int nand_dev_state_load(QEMUFile* f, void* opaque, int version_id)
}
-extern void vmemcpy(target_ulong ptr, char *buf, int size); /* copy memory from the simulated virtual space to a buffer in QEMU */
-extern void pmemcpy(target_ulong ptr, char *buf, int size); /* copy memory from the QEMU buffer to simulated virtual space */
-
static int do_read(int fd, void* buf, size_t size)
{
int ret;
diff --git a/hw/goldfish_switch.c b/hw/goldfish_switch.c
index 04fcad7..8a12d66 100644
--- a/hw/goldfish_switch.c
+++ b/hw/goldfish_switch.c
@@ -9,7 +9,7 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
#include "goldfish_device.h"
enum {
diff --git a/hw/goldfish_timer.c b/hw/goldfish_timer.c
index bb345a5..73f1455 100644
--- a/hw/goldfish_timer.c
+++ b/hw/goldfish_timer.c
@@ -9,7 +9,8 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu-timer.h"
+#include "cpu.h"
#include "arm_pic.h"
#include "goldfish_device.h"
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
index 47822b3..ad0eba5 100644
--- a/hw/goldfish_trace.c
+++ b/hw/goldfish_trace.c
@@ -13,8 +13,7 @@
* Virtual hardware for bridging the FUSE kernel module
* in the emulated OS and outside file system
*/
-#include "vl.h"
-
+#include "qemu_file.h"
#include "goldfish_trace.h"
//#define DEBUG 1
diff --git a/hw/goldfish_trace.h b/hw/goldfish_trace.h
index ea627e8..44190ee 100644
--- a/hw/goldfish_trace.h
+++ b/hw/goldfish_trace.h
@@ -51,8 +51,9 @@ typedef struct {
* interfaces for copy from virtual space
* from target-arm/op_helper.c
*/
-extern unsigned long v2p(target_ulong ptr, int is_user);
+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);
/*
diff --git a/hw/goldfish_tty.c b/hw/goldfish_tty.c
index d61712a..aa62d75 100644
--- a/hw/goldfish_tty.c
+++ b/hw/goldfish_tty.c
@@ -9,7 +9,8 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "vl.h"
+#include "qemu_file.h"
+#include "qemu-char.h"
#include "goldfish_device.h"
enum {
@@ -165,14 +166,14 @@ static void goldfish_tty_write(void *opaque, target_phys_addr_t offset, uint32_t
}
}
-static int tty_can_recieve(void *opaque)
+static int tty_can_receive(void *opaque)
{
struct tty_state *s = opaque;
return (sizeof(s->data) - s->data_count);
}
-static void tty_recieve(void *opaque, const uint8_t *buf, int size)
+static void tty_receive(void *opaque, const uint8_t *buf, int size)
{
struct tty_state *s = opaque;
@@ -210,7 +211,7 @@ int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq)
s->cs = cs;
if(cs) {
- qemu_chr_add_read_handler(cs, tty_can_recieve, tty_recieve, s);
+ 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);
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
index 3621b8e..eca707d 100644
--- a/hw/irq.c
+++ b/hw/irq.c
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "qemu-common.h"
#include "irq.h"
struct IRQState {
diff --git a/hw/irq.h b/hw/irq.h
index 814511a..0880ad2 100644
--- a/hw/irq.h
+++ b/hw/irq.h
@@ -3,7 +3,9 @@
/* 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);
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/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
index d8cbc2b..5f7004a 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -21,7 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "hw.h"
+#include "pci.h"
+#include "console.h"
+#include "net.h"
//#define DEBUG_PCI
@@ -29,25 +32,83 @@ 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;
- void *irq_opaque;
+ 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;
-PCIBus *pci_register_bus(pci_set_irq_fn set_irq, void *pic, int devfn_min)
+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;
- bus = qemu_mallocz(sizeof(PCIBus));
+ 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;
}
@@ -56,21 +117,31 @@ int pci_bus_num(PCIBus *s)
return s->bus_num;
}
-void generic_pci_save(QEMUFile* f, void *opaque)
+void pci_device_save(PCIDevice *s, QEMUFile *f)
{
- PCIDevice* s=(PCIDevice*)opaque;
+ 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 generic_pci_load(QEMUFile* f, void *opaque, int version_id)
+int pci_device_load(PCIDevice *s, QEMUFile *f)
{
- PCIDevice* s=(PCIDevice*)opaque;
+ uint32_t version_id;
+ int i;
- if (version_id != 1)
+ 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;
}
@@ -99,6 +170,7 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
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;
@@ -108,6 +180,7 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
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;
}
@@ -133,7 +206,7 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num,
*(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
}
-target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
{
return addr + pci_mem_base;
}
@@ -221,16 +294,23 @@ uint32_t pci_default_read_config(PCIDevice *d,
uint32_t address, int len)
{
uint32_t val;
+
switch(len) {
- case 1:
- val = d->config[address];
- break;
- case 2:
- val = le16_to_cpu(*(uint16_t *)(d->config + address));
- break;
default:
case 4:
- val = le32_to_cpu(*(uint32_t *)(d->config + address));
+ 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;
@@ -320,7 +400,8 @@ void pci_default_write_config(PCIDevice *d,
if (can_write) {
d->config[addr] = val;
}
- addr++;
+ if (++addr > 0xff)
+ break;
val >>= 8;
}
@@ -342,7 +423,9 @@ void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
addr, val, len);
#endif
bus_num = (addr >> 16) & 0xff;
- if (bus_num != 0)
+ while (s && s->bus_num != bus_num)
+ s = s->next;
+ if (!s)
return;
pci_dev = s->devices[(addr >> 8) & 0xff];
if (!pci_dev)
@@ -363,7 +446,9 @@ uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
uint32_t val;
bus_num = (addr >> 16) & 0xff;
- if (bus_num != 0)
+ while (s && s->bus_num != bus_num)
+ s= s->next;
+ if (!s)
goto fail;
pci_dev = s->devices[(addr >> 8) & 0xff];
if (!pci_dev) {
@@ -400,10 +485,26 @@ uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
/* generic PCI irq support */
/* 0 <= irq_num <= 3. level must be 0 or 1 */
-void pci_set_irq(PCIDevice *pci_dev, int irq_num, int level)
+static void pci_set_irq(void *opaque, int irq_num, int level)
{
- PCIBus *bus = pci_dev->bus;
- bus->set_irq(pci_dev, bus->irq_opaque, irq_num, 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);
}
/***********************************************************/
@@ -416,12 +517,40 @@ typedef struct {
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}
};
@@ -451,6 +580,9 @@ static void pci_info_device(PCIDevice *d)
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) {
@@ -464,14 +596,19 @@ static void pci_info_device(PCIDevice *d)
}
}
}
+ if (class == 0x0604 && d->config[0x19] != 0) {
+ pci_for_each_device(d->config[0x19], pci_info_device);
+ }
}
-void pci_for_each_device(void (*fn)(PCIDevice *d))
+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];
@@ -483,25 +620,82 @@ void pci_for_each_device(void (*fn)(PCIDevice *d))
void pci_info(void)
{
- pci_for_each_device(pci_info_device);
+ pci_for_each_device(0, pci_info_device);
}
/* Initialize a PCI NIC. */
-void pci_nic_init(PCIBus *bus, NICInfo *nd)
+void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
{
-#if 0 /* ANDROID */
+#if 0
if (strcmp(nd->model, "ne2k_pci") == 0) {
- pci_ne2000_init(bus, nd);
+ 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);
+ 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);
+ 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 {
-#else
- {
-#endif
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
index 708dae2..49a0c59 100644
--- a/hw/pci_host.h
+++ b/hw/pci_host.h
@@ -2,7 +2,7 @@
* 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
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/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
index decab1f..16b3215 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -7,6 +7,10 @@
* 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
@@ -21,153 +25,255 @@ do { printf("scsi-disk: " fmt , ##args); } while (0)
#define BADF(fmt, args...) \
do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
-#include "vl.h"
+#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
-struct SCSIDevice
-{
- int command;
+#define SCSI_DMA_BUF_SIZE 65536
+
+typedef struct SCSIRequest {
+ SCSIDeviceState *dev;
uint32_t tag;
- BlockDriverState *bdrv;
- /* 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;
- /* When transfering data buf_pos and buf_len contain a partially
- transferred block of data (or response to a command), and
- sector/sector_count identify any remaining sectors.
- Both sector and sector_count are in terms of qemu 512 byte blocks. */
/* ??? 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;
- int buf_pos;
+ /* 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;
- char buf[512];
+ int tcq;
+ /* Completion functions may be called from either scsi_{read,write}_data
+ or from the AIO completion routines. */
scsi_completionfn completion;
void *opaque;
};
-static void scsi_command_complete(SCSIDevice *s, int sense)
+/* Global pool of SCSIRequest structures. */
+static SCSIRequest *free_requests = NULL;
+
+static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
{
- s->sense = sense;
- s->completion(s->opaque, s->tag, sense);
+ 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;
}
-/* Read data from a scsi device. Returns nonzero on failure. */
-int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
+static void scsi_remove_request(SCSIRequest *r)
{
- uint32_t n;
-
- DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
- if (s->buf_len == 0 && s->sector_count == 0)
- return 1;
+ SCSIRequest *last;
+ SCSIDeviceState *s = r->dev;
- if (s->buf_len) {
- n = s->buf_len;
- if (n > len)
- n = len;
- memcpy(data, s->buf + s->buf_pos, n);
- s->buf_pos += n;
- s->buf_len -= n;
- data += n;
- len -= n;
- if (s->buf_len == 0)
- s->buf_pos = 0;
+ 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;
+}
- n = len / 512;
- if (n > s->sector_count)
- n = s->sector_count;
+static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+{
+ SCSIRequest *r;
- if (n != 0) {
- bdrv_read(s->bdrv, s->sector, data, n);
- data += n * 512;
- len -= n * 512;
- s->sector += n;
- s->sector_count -= n;
- }
+ 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);
+}
- if (len && s->sector_count) {
- bdrv_read(s->bdrv, s->sector, s->buf, 1);
- s->sector++;
- s->sector_count--;
- s->buf_pos = 0;
- s->buf_len = 512;
- /* Recurse to complete the partial read. */
- return scsi_read_data(s, data, len);
+/* 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);
}
+}
- if (len != 0)
- return 1;
+static void scsi_read_complete(void * opaque, int ret)
+{
+ SCSIRequest *r = (SCSIRequest *)opaque;
+ SCSIDeviceState *s = r->dev;
- if (s->buf_len == 0 && s->sector_count == 0)
- scsi_command_complete(s, SENSE_NO_SENSE);
+ 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);
- return 0;
+ s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
}
-/* Read data to a scsi device. Returns nonzero on failure. */
-int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t 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;
- DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
- if (s->buf_pos != 0) {
- BADF("Bad state on write\n");
- return 1;
+ 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;
}
- if (s->sector_count == 0)
- return 1;
+ 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;
+}
- if (s->buf_len != 0 || len < 512) {
- n = 512 - s->buf_len;
- if (n > len)
- n = len;
-
- memcpy(s->buf + s->buf_len, data, n);
- data += n;
- s->buf_len += n;
- len -= n;
- if (s->buf_len == 512) {
- /* A full sector has been accumulated. Write it to disk. */
- bdrv_write(s->bdrv, s->sector, s->buf, 1);
- s->buf_len = 0;
- s->sector++;
- s->sector_count--;
- }
- }
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIRequest *r = (SCSIRequest *)opaque;
+ SCSIDeviceState *s = r->dev;
+ uint32_t len;
- n = len / 512;
- if (n > s->sector_count)
- n = s->sector_count;
+ if (ret) {
+ fprintf(stderr, "scsi-disc: IO write error\n");
+ exit(1);
+ }
- if (n != 0) {
- bdrv_write(s->bdrv, s->sector, data, n);
- data += n * 512;
- len -= n * 512;
- s->sector += n;
- s->sector_count -= n;
+ 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);
}
+}
- if (len >= 512)
- return 1;
+/* 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;
- if (len && s->sector_count) {
- /* Recurse to complete the partial write. */
- return scsi_write_data(s, data, len);
+ 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);
}
- if (len != 0)
- return 1;
+ return 0;
+}
- if (s->sector_count == 0)
- scsi_command_complete(s, SENSE_NO_SENSE);
+/* Return a pointer to the data buffer. */
+static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDeviceState *s = d->state;
+ SCSIRequest *r;
- return 0;
+ 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
@@ -175,22 +281,32 @@ int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
-int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
+static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
+ uint8_t *buf, int lun)
{
- int64_t nb_sectors;
+ SCSIDeviceState *s = d->state;
+ uint64_t nb_sectors;
uint32_t lba;
uint32_t len;
int cmdlen;
int is_write;
-
- s->command = buf[0];
- s->tag = tag;
- s->sector_count = 0;
- s->buf_pos = 0;
- s->buf_len = 0;
+ 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: 0x%02x", buf[0]);
- switch (s->command >> 5) {
+ 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];
@@ -213,7 +329,7 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
cmdlen = 12;
break;
default:
- BADF("Unsupported command length, command %x\n", s->command);
+ BADF("Unsupported command length, command %x\n", command);
goto fail;
}
#ifdef DEBUG_SCSI
@@ -230,7 +346,7 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
goto fail;
}
- switch (s->command) {
+ switch (command) {
case 0x0:
DPRINTF("Test Unit Ready\n");
break;
@@ -238,34 +354,157 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
DPRINTF("Request Sense (len %d)\n", len);
if (len < 4)
goto fail;
- memset(buf, 0, 4);
- s->buf[0] = 0xf0;
- s->buf[1] = 0;
- s->buf[2] = s->sense;
- s->buf_len = 4;
+ 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 (len < 36) {
- BADF("Inquiry buffer too small (%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(s->buf, 0, 36);
+ memset(outbuf, 0, 36);
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
- s->buf[0] = 5;
- s->buf[1] = 0x80;
- memcpy(&s->buf[16], "QEMU CD-ROM ", 16);
+ outbuf[0] = 5;
+ outbuf[1] = 0x80;
+ memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
- s->buf[0] = 0;
- memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
+ outbuf[0] = 0;
+ memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
}
- memcpy(&s->buf[8], "QEMU ", 8);
- memcpy(&s->buf[32], QEMU_VERSION, 4);
+ memcpy(&outbuf[8], "QEMU ", 8);
+ memcpy(&outbuf[32], QEMU_VERSION, 4);
/* Identify device as SCSI-3 rev 1.
Some later commands are also implemented. */
- s->buf[2] = 3;
- s->buf[3] = 2; /* Format 2 */
- s->buf[4] = 32;
- s->buf_len = 36;
+ 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");
@@ -280,25 +519,95 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
case 0x1a:
case 0x5a:
{
- char *p;
+ uint8_t *p;
int page;
page = buf[2] & 0x3f;
DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
- p = s->buf;
+ p = outbuf;
memset(p, 0, 4);
- s->buf[1] = 0; /* Default media type. */
- s->buf[3] = 0; /* Block descriptor length. */
+ outbuf[1] = 0; /* Default media type. */
+ outbuf[3] = 0; /* Block descriptor length. */
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
- s->buf[2] = 0x80; /* Readonly. */
+ outbuf[2] = 0x80; /* Readonly. */
}
p += 4;
- if ((page == 8 || page == 0x3f)) {
+ 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 += 19;
+ p += 20;
}
if ((page == 0x3f || page == 0x2a)
&& (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
@@ -328,12 +637,12 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
p[19] = (16 * 176) & 0xff;
p[20] = (16 * 176) >> 8; // 16x write speed current
p[21] = (16 * 176) & 0xff;
- p += 21;
+ p += 22;
}
- s->buf_len = p - s->buf;
- s->buf[0] = s->buf_len - 4;
- if (s->buf_len > len)
- s->buf_len = len;
+ r->buf_len = p - outbuf;
+ outbuf[0] = r->buf_len - 4;
+ if (r->buf_len > len)
+ r->buf_len = len;
}
break;
case 0x1b:
@@ -346,33 +655,40 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
case 0x25:
DPRINTF("Read Capacity\n");
/* The normal LEN field for this command is zero. */
- memset(s->buf, 0, 8);
+ memset(outbuf, 0, 8);
bdrv_get_geometry(s->bdrv, &nb_sectors);
- s->buf[0] = (nb_sectors >> 24) & 0xff;
- s->buf[1] = (nb_sectors >> 16) & 0xff;
- s->buf[2] = (nb_sectors >> 8) & 0xff;
- s->buf[3] = nb_sectors & 0xff;
- s->buf[4] = 0;
- s->buf[5] = 0;
- s->buf[6] = s->cluster_size * 2;
- s->buf[7] = 0;
- s->buf_len = 8;
+ /* 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);
- s->sector = lba * s->cluster_size;
- s->sector_count = len * s->cluster_size;
+ 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);
- s->sector = lba * s->cluster_size;
- s->sector_count = len * s->cluster_size;
+ r->sector = lba * s->cluster_size;
+ r->sector_count = len * s->cluster_size;
is_write = 1;
break;
case 0x35:
- DPRINTF("Syncronise cache (sector %d, count %d)\n", lba, len);
+ DPRINTF("Synchronise cache (sector %d, count %d)\n", lba, len);
bdrv_flush(s->bdrv);
break;
case 0x43:
@@ -386,18 +702,18 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
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, s->buf, msf, start_track);
+ toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
break;
case 1:
/* multi session : only a single session defined */
toclen = 12;
- memset(s->buf, 0, 12);
- s->buf[1] = 0x0a;
- s->buf[2] = 0x01;
- s->buf[3] = 0x01;
+ memset(outbuf, 0, 12);
+ outbuf[1] = 0x0a;
+ outbuf[2] = 0x01;
+ outbuf[3] = 0x01;
break;
case 2:
- toclen = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
+ toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
break;
default:
goto error_cmd;
@@ -405,7 +721,7 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
if (toclen > 0) {
if (len > toclen)
len = toclen;
- s->buf_len = len;
+ r->buf_len = len;
break;
}
error_cmd:
@@ -414,11 +730,11 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
}
case 0x46:
DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
- memset(s->buf, 0, 8);
- /* ??? This shoud probably return much more information. For now
+ memset(outbuf, 0, 8);
+ /* ??? This should probably return much more information. For now
just return the basic header indicating the CD-ROM profile. */
- s->buf[7] = 8; // CD-ROM
- s->buf_len = 8;
+ outbuf[7] = 8; // CD-ROM
+ r->buf_len = 8;
break;
case 0x56:
DPRINTF("Reserve(10)\n");
@@ -434,37 +750,44 @@ int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
DPRINTF("Report LUNs (len %d)\n", len);
if (len < 16)
goto fail;
- memset(s->buf, 0, 16);
- s->buf[3] = 8;
- s->buf_len = 16;
+ 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(s, SENSE_ILLEGAL_REQUEST);
+ scsi_command_complete(r, SENSE_ILLEGAL_REQUEST);
return 0;
}
- if (s->sector_count == 0 && s->buf_len == 0) {
- scsi_command_complete(s, SENSE_NO_SENSE);
+ 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;
}
- len = s->sector_count * 512 + s->buf_len;
- return is_write ? -len : len;
}
-void scsi_disk_destroy(SCSIDevice *s)
+static void scsi_destroy(SCSIDevice *d)
{
- bdrv_close(s->bdrv);
- qemu_free(s);
+ qemu_free(d->state);
+ qemu_free(d);
}
-SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
- scsi_completionfn completion,
- void *opaque)
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
+ scsi_completionfn completion, void *opaque)
{
- SCSIDevice *s;
+ SCSIDevice *d;
+ SCSIDeviceState *s;
- s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
+ 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) {
@@ -473,6 +796,14 @@ SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
s->cluster_size = 1;
}
- return s;
-}
+ 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/smc91c111.c b/hw/smc91c111.c
index 28b5c5d..410051d 100644
--- a/hw/smc91c111.c
+++ b/hw/smc91c111.c
@@ -1,4 +1,4 @@
-/*
+/*
* SMSC 91C111 Ethernet interface emulation
*
* Copyright (c) 2005 CodeSourcery, LLC.
@@ -7,8 +7,9 @@
* This code is licenced under the GPL
*/
-#include "vl.h"
-#include "irq.h"
+#include "hw.h"
+#include "net.h"
+#include "devices.h"
/* For crc32 */
#include <zlib.h>
@@ -191,7 +192,9 @@ static void smc91c111_do_tx(smc91c111_state *s)
about. */
add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
if (add_crc) {
- uint32_t crc = crc32(~0, p, len);
+ uint32_t crc;
+
+ crc = crc32(~0, p, len);
memcpy(p + len, &crc, 4);
len += 4;
}
@@ -412,7 +415,7 @@ static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
break;
}
cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
- s->bank, offset);
+ s->bank, (int)offset);
}
static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
@@ -444,7 +447,9 @@ static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
case 7:
/* Not implemented. */
return 0;
- case 8: /* Free memory available. */
+ case 8: /* Memory size. */
+ return NUM_PACKETS;
+ case 9: /* Free memory available. */
{
int i;
int n;
@@ -455,8 +460,6 @@ static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
}
return n;
}
- case 9: /* Memory size. */
- return NUM_PACKETS;
case 10: case 11: /* RPCR */
/* Not implemented. */
return 0;
@@ -554,7 +557,7 @@ static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
break;
}
cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
- s->bank, offset);
+ s->bank, (int)offset);
return 0;
}
@@ -614,7 +617,7 @@ static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
return;
- /* Short packets are padded with zeros. Recieveing a packet
+ /* Short packets are padded with zeros. Receiving a packet
< 64 bytes long is considered an error condition. */
if (size < 64)
packetsize = 64;
@@ -648,7 +651,7 @@ static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
/* Pad short packets. */
if (size < 64) {
int pad;
-
+
if (size & 1)
*(p++) = buf[size - 1];
pad = 64 - size;
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 93f46db..406c9ab 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -1,8 +1,9 @@
/*
* 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
@@ -21,31 +22,58 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#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
-#define USB_MOUSE 1
-#define USB_TABLET 2
+/* 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 {
- USBDevice dev;
int dx, dy, dz, buttons_state;
int x, y;
- int kind;
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 */
- 0x10, 0x00, /* u16 bcdUSB; v1.0 */
+ 0x00, 0x01, /* u16 bcdUSB; v1.0 */
0x00, /* u8 bDeviceClass; */
0x00, /* u8 bDeviceSubClass; */
@@ -70,13 +98,13 @@ static const uint8_t qemu_mouse_config_descriptor[] = {
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x04, /* u8 iConfiguration; */
- 0xa0, /* u8 bmAttributes;
+ 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
@@ -97,8 +125,8 @@ static const uint8_t qemu_mouse_config_descriptor[] = {
0x03, /* u8 if_bInterfaceClass; */
0x01, /* u8 if_bInterfaceSubClass; */
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
- 0x05, /* u8 if_iInterface; */
-
+ 0x07, /* u8 if_iInterface; */
+
/* HID descriptor */
0x09, /* u8 bLength; */
0x21, /* u8 bDescriptorType; */
@@ -106,14 +134,14 @@ static const uint8_t qemu_mouse_config_descriptor[] = {
0x00, /* u8 country_code */
0x01, /* u8 num_descriptors */
0x22, /* u8 type; Report */
- 50, 0, /* u16 len */
+ 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 */
- 0x03, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x04, 0x00, /* u16 ep_wMaxPacketSize; */
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
@@ -124,14 +152,14 @@ static const uint8_t qemu_tablet_config_descriptor[] = {
0x22, 0x00, /* u16 wTotalLength; */
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
- 0x04, /* u8 iConfiguration; */
- 0xa0, /* u8 bmAttributes;
+ 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
@@ -152,7 +180,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = {
0x03, /* u8 if_bInterfaceClass; */
0x01, /* u8 if_bInterfaceSubClass; */
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
- 0x05, /* u8 if_iInterface; */
+ 0x07, /* u8 if_iInterface; */
/* HID descriptor */
0x09, /* u8 bLength; */
@@ -169,79 +197,283 @@ static const uint8_t qemu_tablet_config_descriptor[] = {
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* u8 ep_bmAttributes; Interrupt */
0x08, 0x00, /* u16 ep_wMaxPacketSize; */
- 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+ 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, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
- 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
- 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
- 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
- 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81,
- 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
- 0xC0, 0xC0,
+ 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 Mouse */
- 0xA1, 0x01, /* Collection Application */
- 0x09, 0x01, /* Usage Pointer */
- 0xA1, 0x00, /* Collection Physical */
- 0x05, 0x09, /* Usage Page Button */
- 0x19, 0x01, /* Usage Minimum Button 1 */
- 0x29, 0x03, /* Usage Maximum Button 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, Var, Abs) */
- 0x95, 0x01, /* Report Count 1 */
- 0x75, 0x05, /* Report Size 5 */
- 0x81, 0x01, /* Input (Cnst, Var, Abs) */
- 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, 0xFE, 0x7F, /* Physical Maximum 0x7fff */
- 0x75, 0x10, /* Report Size 16 */
- 0x95, 0x02, /* Report Count 2 */
- 0x81, 0x02, /* Input (Data, Var, Abs) */
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x38, /* Usage Wheel */
- 0x15, 0x81, /* Logical Minimum -127 */
- 0x25, 0x7F, /* Logical Maximum 127 */
- 0x35, 0x00, /* Physical Minimum 0 (same as logical) */
- 0x45, 0x00, /* Physical Maximum 0 (same as logical) */
- 0x75, 0x08, /* Report Size 8 */
- 0x95, 0x01, /* Report Count 1 */
- 0x81, 0x02, /* Input (Data, Var, Rel) */
- 0xC0, /* End Collection */
- 0xC0, /* End Collection */
+ 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)
{
- USBMouseState *s = opaque;
+ 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)
{
- USBMouseState *s = opaque;
+ 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)
@@ -254,23 +486,28 @@ static inline int int_clamp(int val, int vmin, int vmax)
return val;
}
-static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len)
+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) {
- qemu_add_mouse_event_handler(usb_mouse_event, s, 0);
+ 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, -128, 127);
- dy = int_clamp(s->dy, -128, 127);
- dz = int_clamp(s->dz, -128, 127);
+
+ 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;
@@ -278,28 +515,31 @@ static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len)
b |= 0x02;
if (s->buttons_state & MOUSE_EVENT_MBUTTON)
b |= 0x04;
-
- buf[0] = b;
- buf[1] = dx;
- buf[2] = dy;
- l = 3;
- if (len >= 4) {
- buf[3] = dz;
- l = 4;
- }
+
+ 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(USBMouseState *s, uint8_t *buf, int len)
+static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len)
{
int dz, b, l;
+ USBMouseState *s = &hs->ptr;
if (!s->mouse_grabbed) {
- qemu_add_mouse_event_handler(usb_tablet_event, s, 1);
+ 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, -128, 127);
+
+ dz = int_clamp(s->dz, -127, 127);
s->dz -= dz;
/* Appears we have to invert the wheel direction */
@@ -323,28 +563,59 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len)
return l;
}
-static void usb_mouse_handle_reset(USBDevice *dev, int destroy)
+static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len)
{
- USBMouseState *s = (USBMouseState *)dev;
+ if (len < 2)
+ return 0;
- if (destroy) {
- qemu_add_mouse_event_handler(NULL, NULL, 0);
- qemu_free(s);
- return;
+ 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;
+}
- s->dx = 0;
- s->dy = 0;
- s->dz = 0;
- s->x = 0;
- s->y = 0;
- s->buttons_state = 0;
+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_mouse_handle_control(USBDevice *dev, int request, int value,
+static int usb_hid_handle_control(USBDevice *dev, int request, int value,
int index, int length, uint8_t *data)
{
- USBMouseState *s = (USBMouseState *)dev;
+ USBHIDState *s = (USBHIDState *)dev;
int ret = 0;
switch(request) {
@@ -377,20 +648,24 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
- memcpy(data, qemu_mouse_dev_descriptor,
+ 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,
+ 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,
+ 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) {
@@ -408,10 +683,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
break;
case 2:
/* product description */
- if (s->kind == USB_MOUSE)
- ret = set_usb_string(data, "QEMU USB Mouse");
- else if (s->kind == USB_TABLET)
- ret = set_usb_string(data, "QEMU USB Tablet");
+ ret = set_usb_string(data, s->dev.devname);
break;
case 3:
/* vendor description */
@@ -421,6 +693,12 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
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:
@@ -450,26 +728,55 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
switch(value >> 8) {
case 0x22:
if (s->kind == USB_MOUSE) {
- memcpy(data, qemu_mouse_hid_report_descriptor,
+ 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,
+ memcpy(data, qemu_tablet_hid_report_descriptor,
sizeof(qemu_tablet_hid_report_descriptor));
ret = sizeof(qemu_tablet_hid_report_descriptor);
- }
- break;
+ } 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);
+ ret = usb_mouse_poll(s, data, length);
else if (s->kind == USB_TABLET)
- ret = usb_tablet_poll(s, data, length);
+ 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:
@@ -480,19 +787,24 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
return ret;
}
-static int usb_mouse_handle_data(USBDevice *dev, int pid,
- uint8_t devep, uint8_t *data, int len)
+static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
{
- USBMouseState *s = (USBMouseState *)dev;
+ USBHIDState *s = (USBHIDState *)dev;
int ret = 0;
- switch(pid) {
+ switch(p->pid) {
case USB_TOKEN_IN:
- if (devep == 1) {
- if (s->kind == USB_MOUSE)
- ret = usb_mouse_poll(s, data, len);
- else if (s->kind == USB_TABLET)
- ret = usb_tablet_poll(s, data, len);
+ 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;
}
@@ -506,20 +818,33 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid,
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)
{
- USBMouseState *s;
+ USBHIDState *s;
- s = qemu_mallocz(sizeof(USBMouseState));
+ 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_mouse_handle_control;
- s->dev.handle_data = usb_mouse_handle_data;
+ 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");
@@ -528,20 +853,44 @@ USBDevice *usb_tablet_init(void)
USBDevice *usb_mouse_init(void)
{
- USBMouseState *s;
+ USBHIDState *s;
- s = qemu_mallocz(sizeof(USBMouseState));
+ 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_mouse_handle_control;
- s->dev.handle_data = usb_mouse_handle_data;
+ 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
index 2eba905..97c3d05 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -2,7 +2,7 @@
* 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
@@ -21,7 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "qemu-common.h"
+#include "usb.h"
//#define DEBUG
@@ -112,13 +113,13 @@ static const uint8_t qemu_hub_config_descriptor[] = {
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
- 0xc0, /* u8 bmAttributes;
+ 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
@@ -140,7 +141,7 @@ static const uint8_t qemu_hub_config_descriptor[] = {
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 */
@@ -167,11 +168,11 @@ 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)
@@ -180,8 +181,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
port->port.dev = dev;
/* send the attach message */
- dev->handle_packet(dev,
- USB_MSG_ATTACH, 0, 0, NULL, 0);
+ usb_send_msg(dev, USB_MSG_ATTACH);
} else {
dev = port->port.dev;
if (dev) {
@@ -192,18 +192,15 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
port->wPortChange |= PORT_STAT_C_ENABLE;
}
/* send the detach message */
- dev->handle_packet(dev,
- USB_MSG_DETACH, 0, 0, NULL, 0);
+ usb_send_msg(dev, USB_MSG_DETACH);
port->port.dev = NULL;
}
}
}
-static void usb_hub_handle_reset(USBDevice *dev, int destroy)
+static void usb_hub_handle_reset(USBDevice *dev)
{
/* XXX: do it */
- if (destroy)
- qemu_free(dev);
}
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
@@ -248,12 +245,12 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
- memcpy(data, qemu_hub_dev_descriptor,
+ 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,
+ memcpy(data, qemu_hub_config_descriptor,
sizeof(qemu_hub_config_descriptor));
/* status change endpoint size based on number
@@ -351,8 +348,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
break;
case PORT_RESET:
if (dev) {
- dev->handle_packet(dev,
- USB_MSG_RESET, 0, 0, NULL, 0);
+ usb_send_msg(dev, USB_MSG_RESET);
port->wPortChange |= PORT_STAT_C_RESET;
/* set enable bit */
port->wPortStatus |= PORT_STAT_ENABLE;
@@ -406,7 +402,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
case GetHubDescriptor:
{
unsigned int n, limit, var_hub_size = 0;
- memcpy(data, qemu_hub_hub_descriptor,
+ memcpy(data, qemu_hub_hub_descriptor,
sizeof(qemu_hub_hub_descriptor));
data[2] = s->nb_ports;
@@ -436,22 +432,21 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
return ret;
}
-static int usb_hub_handle_data(USBDevice *dev, int pid,
- uint8_t devep, uint8_t *data, int len)
+static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
{
USBHubState *s = (USBHubState *)dev;
int ret;
- switch(pid) {
+ switch(p->pid) {
case USB_TOKEN_IN:
- if (devep == 1) {
+ if (p->devep == 1) {
USBHubPort *port;
unsigned int status;
int i, n;
n = (s->nb_ports + 1 + 7) / 8;
- if (len == 1) { /* FreeBSD workaround */
+ if (p->len == 1) { /* FreeBSD workaround */
n = 1;
- } else if (n > len) {
+ } else if (n > p->len) {
return USB_RET_BABBLE;
}
status = 0;
@@ -462,7 +457,7 @@ static int usb_hub_handle_data(USBDevice *dev, int pid,
}
if (status != 0) {
for(i = 0; i < n; i++) {
- data[i] = status >> (8 * i);
+ p->data[i] = status >> (8 * i);
}
ret = n;
} else {
@@ -481,9 +476,7 @@ static int usb_hub_handle_data(USBDevice *dev, int pid,
return ret;
}
-static int usb_hub_broadcast_packet(USBHubState *s, int pid,
- uint8_t devaddr, uint8_t devep,
- uint8_t *data, int len)
+static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
{
USBHubPort *port;
USBDevice *dev;
@@ -493,9 +486,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
port = &s->ports[i];
dev = port->port.dev;
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
- ret = dev->handle_packet(dev, pid,
- devaddr, devep,
- data, len);
+ ret = dev->handle_packet(dev, p);
if (ret != USB_RET_NODEV) {
return ret;
}
@@ -504,9 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
return USB_RET_NODEV;
}
-static int usb_hub_handle_packet(USBDevice *dev, int pid,
- uint8_t devaddr, uint8_t devep,
- uint8_t *data, int len)
+static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
{
USBHubState *s = (USBHubState *)dev;
@@ -515,14 +504,21 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid,
#endif
if (dev->state == USB_STATE_DEFAULT &&
dev->addr != 0 &&
- devaddr != dev->addr &&
- (pid == USB_TOKEN_SETUP ||
- pid == USB_TOKEN_OUT ||
- pid == USB_TOKEN_IN)) {
+ 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, pid, devaddr, devep, data, len);
+ return usb_hub_broadcast_packet(s, p);
}
- return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
+ 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)
@@ -543,6 +539,7 @@ USBDevice *usb_hub_init(int nb_ports)
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");
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 3dccfb9..f7ad25e 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -1,4 +1,4 @@
-/*
+/*
* USB Mass Storage Device emulation
*
* Copyright (c) 2006 CodeSourcery.
@@ -7,7 +7,10 @@
* This code is licenced under the LGPL.
*/
-#include "vl.h"
+#include "qemu-common.h"
+#include "usb.h"
+#include "block.h"
+#include "scsi-disk.h"
//#define DEBUG_MSD
@@ -32,16 +35,41 @@ enum USBMSDMode {
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 */
- 0x10, 0x00, /* u16 bcdUSB; v1.0 */
+ 0x00, 0x01, /* u16 bcdUSB; v1.0 */
0x00, /* u8 bDeviceClass; */
0x00, /* u8 bDeviceSubClass; */
@@ -68,13 +96,13 @@ static const uint8_t qemu_msd_config_descriptor[] = {
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
- 0xc0, /* u8 bmAttributes;
+ 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 */
@@ -85,7 +113,7 @@ static const uint8_t qemu_msd_config_descriptor[] = {
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 */
@@ -103,25 +131,99 @@ static const uint8_t qemu_msd_config_descriptor[] = {
0x00 /* u8 ep_bInterval; */
};
-static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail)
+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;
- DPRINTF("Command complete\n");
- s->result = fail;
- s->mode = USB_MSDM_CSW;
+ 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, int destroy)
+static void usb_msd_handle_reset(USBDevice *dev)
{
MSDState *s = (MSDState *)dev;
DPRINTF("Reset\n");
s->mode = USB_MSDM_CBW;
- if (destroy) {
- scsi_disk_destroy(s->scsi_dev);
- qemu_free(s);
- }
}
static int usb_msd_handle_control(USBDevice *dev, int request, int value,
@@ -160,12 +262,12 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
- memcpy(data, qemu_msd_dev_descriptor,
+ 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,
+ memcpy(data, qemu_msd_config_descriptor,
sizeof(qemu_msd_config_descriptor));
ret = sizeof(qemu_msd_config_descriptor);
break;
@@ -237,32 +339,24 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
return ret;
}
-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 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, int pid, uint8_t devep,
- uint8_t *data, int len)
+static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
{
MSDState *s = (MSDState *)dev;
int ret = 0;
struct usb_msd_cbw cbw;
- struct usb_msd_csw csw;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
- switch (pid) {
+ switch (p->pid) {
case USB_TOKEN_OUT:
if (devep != 2)
goto fail;
@@ -295,7 +389,17 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
}
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
- scsi_send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ 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;
@@ -304,13 +408,25 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
if (len > s->data_len)
goto fail;
- if (scsi_write_data(s->scsi_dev, data, len))
- goto fail;
-
- s->data_len -= len;
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- ret = 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;
+ 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:
@@ -324,33 +440,52 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
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;
- csw.sig = cpu_to_le32(0x53425355);
- csw.tag = cpu_to_le32(s->tag);
- csw.residue = 0;
- csw.status = s->result;
- memcpy(data, &csw, 13);
- ret = 13;
+ 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;
-
- if (scsi_read_data(s->scsi_dev, data, len))
- goto fail;
-
- s->data_len -= len;
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- ret = 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:
@@ -369,18 +504,59 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
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");
- bdrv_open(bdrv, filename, 0);
+ 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;
@@ -388,11 +564,15 @@ USBDevice *usb_msd_init(const char *filename)
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, usb_msd_command_complete, s);
- usb_msd_handle_reset((USBDevice *)s, 0);
+ 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
index e87d9da..55cb77b 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -2,6 +2,7 @@
* 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
@@ -26,11 +27,16 @@
* o BIOS work to boot from USB storage
*/
-#include "vl.h"
+#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
@@ -52,11 +58,18 @@ typedef struct OHCIPort {
uint32_t ctrl;
} OHCIPort;
+enum ohci_type {
+ OHCI_TYPE_PCI,
+ OHCI_TYPE_PXA
+};
+
typedef struct {
- struct PCIDevice pci_dev;
+ 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;
@@ -89,6 +102,20 @@ typedef struct {
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 */
@@ -98,6 +125,8 @@ struct ohci_hcca {
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)
@@ -108,8 +137,8 @@ struct ohci_hcca {
#define OHCI_ED_S (1<<13)
#define OHCI_ED_K (1<<14)
#define OHCI_ED_F (1<<15)
-#define OHCI_ED_MPS_SHIFT 7
-#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT)
+#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
@@ -128,6 +157,22 @@ struct ohci_hcca {
#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) \
@@ -154,6 +199,15 @@ struct ohci_td {
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 */
@@ -248,6 +302,8 @@ struct ohci_td {
#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)
{
@@ -257,7 +313,7 @@ static inline void ohci_intr_update(OHCIState *ohci)
(ohci->intr_status & ohci->intr))
level = 1;
- pci_set_irq(&ohci->pci_dev, 0, level);
+ qemu_set_irq(ohci->irq, level);
}
/* Set an interrupt */
@@ -287,9 +343,13 @@ static void ohci_attach(USBPort *port1, USBDevice *dev)
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 */
- dev->handle_packet(dev,
- USB_MSG_ATTACH, 0, 0, NULL, 0);
+ usb_send_msg(dev, USB_MSG_ATTACH);
dprintf("usb-ohci: Attached port %d\n", port1->index);
} else {
/* set connect status */
@@ -305,8 +365,7 @@ static void ohci_attach(USBPort *port1, USBDevice *dev)
dev = port->port.dev;
if (dev) {
/* send the detach message */
- dev->handle_packet(dev,
- USB_MSG_DETACH, 0, 0, NULL, 0);
+ usb_send_msg(dev, USB_MSG_DETACH);
}
port->port.dev = NULL;
dprintf("usb-ohci: Detached port %d\n", port1->index);
@@ -317,12 +376,15 @@ static void ohci_attach(USBPort *port1, USBDevice *dev)
}
/* Reset the controller */
-static void ohci_reset(OHCIState *ohci)
+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;
@@ -356,7 +418,11 @@ static void ohci_reset(OHCIState *ohci)
if (port->port.dev)
ohci_attach(&port->port, port->port.dev);
}
- dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name);
+ 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 */
@@ -385,6 +451,32 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
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);
@@ -395,6 +487,12 @@ 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);
@@ -405,6 +503,12 @@ 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)
{
@@ -423,6 +527,272 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
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. */
@@ -430,8 +800,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
{
int dir;
size_t len = 0;
- uint8_t buf[8192];
- char *str = NULL;
+ const char *str = NULL;
int pid;
int ret;
int i;
@@ -439,8 +808,17 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
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;
@@ -481,8 +859,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
len = (td.be - td.cbp) + 1;
}
- if (len && dir != OHCI_TD_DIR_IN) {
- ohci_copy_td(&td, buf, len, 0);
+ if (len && dir != OHCI_TD_DIR_IN && !completion) {
+ ohci_copy_td(&td, ohci->usb_buf, len, 0);
}
}
@@ -491,34 +869,61 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
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) {
+ if (len > 0 && dir != OHCI_TD_DIR_IN) {
dprintf(" data:");
for (i = 0; i < len; i++)
- printf(" %.2x", buf[i]);
+ printf(" %.2x", ohci->usb_buf[i]);
dprintf("\n");
}
#endif
- 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;
-
- ret = dev->handle_packet(dev, pid, OHCI_BM(ed->flags, ED_FA),
- OHCI_BM(ed->flags, ED_EN), buf, len);
- if (ret != USB_RET_NODEV)
- break;
- }
+ 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);
+ 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, buf, ret, 1);
+ ohci_copy_td(&td, ohci->usb_buf, ret, 1);
#ifdef DEBUG_PACKET
dprintf(" data:");
for (i = 0; i < ret; i++)
- printf(" %.2x", buf[i]);
+ printf(" %.2x", ohci->usb_buf[i]);
dprintf("\n");
#endif
} else {
@@ -588,7 +993,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
/* Service an endpoint list. Returns nonzero if active TD were found. */
-static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
{
struct ohci_ed ed;
uint32_t next_ed;
@@ -608,12 +1013,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
next_ed = ed.next & OHCI_DPTR_MASK;
- if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K))
+ 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;
-
- /* Skip isochronous endpoints. */
- if (ed.flags & OHCI_ED_F)
- continue;
+ }
while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
#ifdef DEBUG_PACKET
@@ -628,8 +1037,14 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
#endif
active = 1;
- if (ohci_service_td(ohci, &ed))
- break;
+ 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);
@@ -646,6 +1061,27 @@ static void ohci_sof(OHCIState *ohci)
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)
{
@@ -659,23 +1095,17 @@ static void ohci_frame_boundary(void *opaque)
int n;
n = ohci->frame_number & 0x1f;
- ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
- }
- 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)) {
- ohci->ctrl_cur = 0;
- ohci->status &= ~OHCI_STATUS_CLF;
- }
+ ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
}
- if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
- if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
- ohci->bulk_cur = 0;
- ohci->status &= ~OHCI_STATUS_BLF;
- }
+ /* 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;
@@ -715,13 +1145,12 @@ static int ohci_bus_start(OHCIState *ohci)
ohci);
if (ohci->eof_timer == NULL) {
- fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n",
- ohci->pci_dev.name);
+ 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->pci_dev.name);
+ dprintf("usb-ohci: %s: USB Operational\n", ohci->name);
ohci_sof(ohci);
@@ -733,6 +1162,7 @@ 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
@@ -774,7 +1204,7 @@ static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
if (val != ohci->fi) {
dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
- ohci->pci_dev.name, ohci->fi, ohci->fi);
+ ohci->name, ohci->fi, ohci->fi);
}
ohci->fi = val;
@@ -812,13 +1242,14 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
break;
case OHCI_USB_SUSPEND:
ohci_bus_stop(ohci);
- dprintf("usb-ohci: %s: USB Suspended\n", ohci->pci_dev.name);
+ dprintf("usb-ohci: %s: USB Suspended\n", ohci->name);
break;
case OHCI_USB_RESUME:
- dprintf("usb-ohci: %s: USB Resume\n", ohci->pci_dev.name);
+ dprintf("usb-ohci: %s: USB Resume\n", ohci->name);
break;
case OHCI_USB_RESET:
- dprintf("usb-ohci: %s: USB Reset\n", ohci->pci_dev.name);
+ ohci_reset(ohci);
+ dprintf("usb-ohci: %s: USB Reset\n", ohci->name);
break;
}
}
@@ -907,8 +1338,7 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
dprintf("usb-ohci: port %d: RESET\n", portnum);
- port->port.dev->handle_packet(port->port.dev, USB_MSG_RESET,
- 0, 0, NULL, 0);
+ 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;
@@ -1007,6 +1437,19 @@ static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
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;
@@ -1088,6 +1531,9 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
ohci_set_frame_interval(ohci, val);
break;
+ case 15: /* HcFmNumber */
+ break;
+
case 16: /* HcPeriodicStart */
ohci->pstart = val & 0xffff;
break;
@@ -1108,6 +1554,24 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
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;
@@ -1128,24 +1592,13 @@ static CPUWriteMemoryFunc *ohci_writefn[3]={
ohci_mem_write
};
-static void ohci_mapfunc(PCIDevice *pci_dev, int i,
- uint32_t addr, uint32_t size, int type)
-{
- OHCIState *ohci = (OHCIState *)pci_dev;
- ohci->mem_base = addr;
- cpu_register_physical_memory(addr, size, ohci->mem);
-}
-
-void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn)
+static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn,
+ qemu_irq irq, enum ohci_type type, const char *name)
{
- OHCIState *ohci;
- int vid = 0x106b;
- int did = 0x003f;
int i;
-
if (usb_frame_time == 0) {
-#if OHCI_TIME_WARP
+#ifdef OHCI_TIME_WARP
usb_frame_time = ticks_per_sec;
usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000);
#else
@@ -1160,8 +1613,43 @@ void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn)
usb_frame_time, usb_bit_time);
}
- ohci = (OHCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci),
- devfn, NULL, NULL);
+ 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;
@@ -1176,15 +1664,21 @@ void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn)
ohci->pci_dev.config[0x0b] = 0xc;
ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
- ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci);
+ 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);
+}
- ohci->num_ports = num_ports;
- for (i = 0; i < num_ports; i++) {
- qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach);
- }
+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));
- ohci_reset(ohci);
+ 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
index a00d945..c17266d 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -2,7 +2,9 @@
* 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
@@ -21,7 +23,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "qemu-common.h"
+#include "usb.h"
void usb_attach(USBPort *port, USBDevice *dev)
{
@@ -29,153 +32,174 @@ void usb_attach(USBPort *port, USBDevice *dev)
}
/**********************/
+
/* generic USB device helpers (you are not forced to use them when
writing your USB device driver, but they help handling the
- protocol)
+ protocol)
*/
#define SETUP_STATE_IDLE 0
#define SETUP_STATE_DATA 1
#define SETUP_STATE_ACK 2
-int usb_generic_handle_packet(USBDevice *s, int pid,
- uint8_t devaddr, uint8_t devep,
- uint8_t *data, int len)
+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)
{
- int l, ret = 0;
+ 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;
- switch(pid) {
+ 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;
- break;
+ return 0;
+
case USB_MSG_DETACH:
s->state = USB_STATE_NOTATTACHED;
- break;
+ return 0;
+
case USB_MSG_RESET:
s->remote_wakeup = 0;
s->addr = 0;
s->state = USB_STATE_DEFAULT;
- s->handle_reset(s, 0);
- break;
- case USB_MSG_DESTROY:
- s->handle_reset(s, 1);
- break;
+ 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:
- if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
- return USB_RET_NODEV;
- if (len != 8)
- goto fail;
- memcpy(s->setup_buf, data, 8);
- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
- s->setup_index = 0;
- if (s->setup_buf[0] & USB_DIR_IN) {
- ret = s->handle_control(s,
- (s->setup_buf[0] << 8) | s->setup_buf[1],
- (s->setup_buf[3] << 8) | s->setup_buf[2],
- (s->setup_buf[5] << 8) | s->setup_buf[4],
- 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;
- }
- break;
+ return do_token_setup(s, p);
+
case USB_TOKEN_IN:
- if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
- return USB_RET_NODEV;
- switch(devep) {
- case 0:
- 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,
- (s->setup_buf[0] << 8) | s->setup_buf[1],
- (s->setup_buf[3] << 8) | s->setup_buf[2],
- (s->setup_buf[5] << 8) | s->setup_buf[4],
- s->setup_len,
- s->data_buf);
- if (ret > 0)
- ret = 0;
- } else {
- /* return 0 byte */
- }
- break;
- case SETUP_STATE_DATA:
- if (s->setup_buf[0] & USB_DIR_IN) {
- l = s->setup_len - s->setup_index;
- if (l > len)
- l = len;
- memcpy(data, s->data_buf + s->setup_index, l);
- s->setup_index += l;
- if (s->setup_index >= s->setup_len)
- s->setup_state = SETUP_STATE_ACK;
- ret = l;
- } else {
- s->setup_state = SETUP_STATE_IDLE;
- goto fail;
- }
- break;
- default:
- goto fail;
- }
- break;
- default:
- ret = s->handle_data(s, pid, devep, data, len);
- break;
- }
- break;
+ return do_token_in(s, p);
+
case USB_TOKEN_OUT:
- if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
- return USB_RET_NODEV;
- switch(devep) {
- case 0:
- 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 additionnal output */
- }
- break;
- case SETUP_STATE_DATA:
- if (!(s->setup_buf[0] & USB_DIR_IN)) {
- l = s->setup_len - s->setup_index;
- if (l > len)
- l = len;
- memcpy(s->data_buf + s->setup_index, data, l);
- s->setup_index += l;
- if (s->setup_index >= s->setup_len)
- s->setup_state = SETUP_STATE_ACK;
- ret = l;
- } else {
- s->setup_state = SETUP_STATE_IDLE;
- goto fail;
- }
- break;
- default:
- goto fail;
- }
- break;
- default:
- ret = s->handle_data(s, pid, devep, data, len);
- break;
- }
- break;
+ return do_token_out(s, p);
+
default:
- fail:
- ret = USB_RET_STALL;
- break;
+ return USB_RET_STALL;
}
- return ret;
}
/* XXX: fix overflow */
@@ -194,3 +218,14 @@ int set_usb_string(uint8_t *buf, const char *str)
}
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
index b0887d6..1a353bb 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -1,8 +1,8 @@
/*
* 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
@@ -29,12 +29,12 @@
#define USB_MSG_ATTACH 0x100
#define USB_MSG_DETACH 0x101
#define USB_MSG_RESET 0x102
-#define USB_MSG_DESTROY 0x103
-#define USB_RET_NODEV (-1)
+#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
@@ -108,28 +108,64 @@
#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;
- int (*handle_packet)(USBDevice *dev, int pid,
- uint8_t devaddr, uint8_t devep,
- uint8_t *data, int len);
+
+ /*
+ * 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. */
- void (*handle_reset)(USBDevice *dev, int destroy);
+ 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);
- int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
- uint8_t *data, int len);
+
+ /*
+ * 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];
@@ -150,28 +186,106 @@ struct USBPort {
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, int pid,
- uint8_t devaddr, uint8_t devep,
- uint8_t *data, int len);
+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-uhci.c */
-void usb_uhci_init(PCIBus *bus, int devfn);
-
-/* usb-ohci.c */
-void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn);
-
/* 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);