From c27f813900a3c114562efbb8df1065e94766fc48 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 10 Feb 2009 15:43:59 -0800 Subject: auto import from //branches/cupcake/...@130745 --- hw/android_arm.c | 45 ++- hw/arm-misc.h | 49 +++ hw/arm_boot.c | 218 ++++++++++--- hw/arm_gic.c | 747 ++++++++++++++++++++++++++++++++++++++++++++ hw/arm_pic.c | 12 +- hw/armv7m.c | 206 ++++++++++++ hw/armv7m_nvic.c | 407 ++++++++++++++++++++++++ hw/audiodev.h | 17 + hw/baum.h | 29 ++ hw/boards.h | 122 ++++++++ hw/cdrom.c | 25 +- hw/devices.h | 74 +++++ hw/dma.c | 31 +- hw/goldfish_audio.c | 4 +- hw/goldfish_battery.c | 2 +- hw/goldfish_device.c | 2 +- hw/goldfish_events_device.c | 4 +- hw/goldfish_fb.c | 4 +- hw/goldfish_interrupt.c | 2 +- hw/goldfish_memlog.c | 2 +- hw/goldfish_mmc.c | 5 +- hw/goldfish_nand.c | 11 +- hw/goldfish_switch.c | 2 +- hw/goldfish_timer.c | 3 +- hw/goldfish_trace.c | 3 +- hw/goldfish_trace.h | 3 +- hw/goldfish_tty.c | 9 +- hw/hw.h | 110 +++++++ hw/irq.c | 2 +- hw/irq.h | 2 + hw/isa.h | 27 ++ hw/pc.h | 148 +++++++++ hw/pci.c | 262 ++++++++++++++-- hw/pci.h | 142 +++++++++ hw/pci_host.h | 2 +- hw/pcmcia.h | 50 +++ hw/pxa.h | 227 ++++++++++++++ hw/scsi-disk.c | 709 ++++++++++++++++++++++++++++++----------- hw/scsi-disk.h | 36 +++ hw/smc91c111.c | 25 +- hw/usb-hid.c | 609 ++++++++++++++++++++++++++++-------- hw/usb-hub.c | 79 +++-- hw/usb-msd.c | 304 ++++++++++++++---- hw/usb-ohci.c | 662 ++++++++++++++++++++++++++++++++++----- hw/usb.c | 287 +++++++++-------- hw/usb.h | 160 ++++++++-- 46 files changed, 5076 insertions(+), 805 deletions(-) create mode 100644 hw/arm-misc.h create mode 100644 hw/arm_gic.c create mode 100644 hw/armv7m.c create mode 100644 hw/armv7m_nvic.c create mode 100644 hw/audiodev.h create mode 100644 hw/baum.h create mode 100644 hw/boards.h create mode 100644 hw/devices.h create mode 100644 hw/hw.h create mode 100644 hw/isa.h create mode 100644 hw/pc.h create mode 100644 hw/pci.h create mode 100644 hw/pcmcia.h create mode 100644 hw/pxa.h create mode 100644 hw/scsi-disk.h (limited to 'hw') 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 */ + 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 +#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 #include -#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, §or_count); + bdrv_get_geometry(s->bs, (uint64_t*)§or_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 + * + * 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 @@ -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<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); -- cgit v1.1